From eaebeb87c9505a69d0ed1b605f13052d9f517ded Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 20 Mar 2024 18:52:54 +0000 Subject: [PATCH] Include bagname in tiddler Etags --- .../multiwikiclient/multiwikiclientadaptor.js | 23 +++++++++++++------ .../multiwikiserver/modules/mws-server.js | 14 +++++++++++ .../routes/handlers/delete-bag-tiddler.js | 2 +- .../routes/handlers/get-bag-tiddler-blob.js | 2 +- .../routes/handlers/get-bag-tiddler.js | 4 ++-- .../routes/handlers/get-recipe-tiddler.js | 4 ++-- .../routes/handlers/put-recipe-tiddler.js | 2 +- .../modules/store/sql-tiddler-store.js | 8 +++++-- 8 files changed, 43 insertions(+), 16 deletions(-) diff --git a/plugins/tiddlywiki/multiwikiclient/multiwikiclientadaptor.js b/plugins/tiddlywiki/multiwikiclient/multiwikiclientadaptor.js index b6b94b684..596000cab 100644 --- a/plugins/tiddlywiki/multiwikiclient/multiwikiclientadaptor.js +++ b/plugins/tiddlywiki/multiwikiclient/multiwikiclientadaptor.js @@ -316,7 +316,7 @@ MultiWikiClientAdaptor.prototype.convertTiddlerFromTiddlyWebFormat = function(ti Split an MWS Etag into its constituent parts. For example: ``` -"tiddler_id:946151" +"tiddler:mybag/946151" ``` Note that the value includes the opening and closing double quotes. @@ -324,16 +324,25 @@ Note that the value includes the opening and closing double quotes. The parts are: ``` -tiddler_id: +"tiddler:/" ``` */ MultiWikiClientAdaptor.prototype.parseEtag = function(etag) { - const PREFIX = "\"tiddler_id:"; - if(!etag.startsWith(PREFIX)) { - return null; + const PREFIX = "\"tiddler:"; + if(etag.startsWith(PREFIX)) { + const slashPos = etag.indexOf("/"); + if(slashPos !== -1) { + const bag_name = etag.slice(PREFIX.length,slashPos), + revision = parseInt(etag.slice(slashPos + 1),10); + if(!isNaN(revision)) { + return { + bag_name: bag_name, + revision: revision + }; + } + } } - const revision = parseInt(etag.slice(PREFIX.length),10); - return isNaN(revision) ? null : revision; + return null; }; if($tw.browser && document.location.protocol.substr(0,4) === "http" ) { diff --git a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js index 39e1492a9..8650502a5 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js @@ -264,6 +264,19 @@ function streamMultipartData(request,options) { }); } +/* +Make an etag. Options include: +bag_name: +tiddler_id: +*/ +function makeTiddlerEtag(options) { + if(options.bag_name || options.tiddler_id) { + return "\"tiddler:" + options.bag_name + "/" + options.tiddler_id + "\""; + } else { + throw "Missing bag_name or tiddler_id"; + } +} + Server.prototype.defaultVariables = { port: "8080", host: "127.0.0.1", @@ -360,6 +373,7 @@ Server.prototype.requestHandler = function(request,response,options) { state.sendResponse = sendResponse.bind(self,request,response); state.redirect = redirect.bind(self,request,response); state.streamMultipartData = streamMultipartData.bind(self,request); + state.makeTiddlerEtag = makeTiddlerEtag.bind(self); // Get the principals authorized to access this resource state.authorizationType = options.authorizationType || this.methodMappings[request.method] || "readers"; // Check whether anonymous access is granted diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-bag-tiddler.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-bag-tiddler.js index 610294f1f..947d9210a 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-bag-tiddler.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-bag-tiddler.js @@ -23,7 +23,7 @@ exports.handler = function(request,response,state) { if(bag_name) { var result = $tw.mws.store.deleteTiddler(title,bag_name); response.writeHead(204, "OK", { - Etag: "\"tiddler_id:" + result.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(result), "Content-Type": "text/plain" }); response.end(); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler-blob.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler-blob.js index d7b893f14..50daf1ec4 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler-blob.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler-blob.js @@ -24,7 +24,7 @@ exports.handler = function(request,response,state) { const result = $tw.mws.store.getBagTiddlerStream(title,bag_name); if(result) { response.writeHead(200, "OK",{ - Etag: "\"tiddler_id:" + result.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(result), "Content-Type": result.type, }); result.stream.pipe(response); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler.js index d4931220c..9d78abf41 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-bag-tiddler.js @@ -42,7 +42,7 @@ exports.handler = function(request,response,state) { }); tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki"; state.sendResponse(200,{ - Etag: "\"tiddler_id:" + tiddlerInfo.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(tiddlerInfo), "Content-Type": "application/json" },JSON.stringify(tiddlerFields),"utf8"); return; @@ -51,7 +51,7 @@ exports.handler = function(request,response,state) { const result = $tw.mws.store.getBagTiddlerStream(title,bag_name); if(result) { response.writeHead(200, "OK",{ - Etag: "\"tiddler_id:" + result.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(result), "Content-Type": result.type }); result.stream.pipe(response); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js index 1f23684ca..229fa6edf 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js @@ -42,7 +42,7 @@ exports.handler = function(request,response,state) { }); tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki"; state.sendResponse(200,{ - Etag: "\"tiddler_id:" + tiddlerInfo.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(tiddlerInfo), "Content-Type": "application/json" },JSON.stringify(tiddlerFields),"utf8"); return; @@ -50,7 +50,7 @@ exports.handler = function(request,response,state) { // This is not a JSON API request, we should return the raw tiddler content var type = tiddlerInfo.tiddler.type || "text/plain"; response.writeHead(200, "OK",{ - Etag: "\"tiddler_id:" + tiddlerInfo.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(tiddlerInfo), "Content-Type": type }); response.write(tiddlerInfo.tiddler.text || "",($tw.config.contentTypeInfo[type] ||{encoding: "utf8"}).encoding); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/put-recipe-tiddler.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/put-recipe-tiddler.js index 825f3cb9e..cc61044f2 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/put-recipe-tiddler.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/put-recipe-tiddler.js @@ -38,7 +38,7 @@ exports.handler = function(request,response,state) { var result = $tw.mws.store.saveRecipeTiddler(fields,recipe_name); if(result) { response.writeHead(204, "OK",{ - Etag: "\"tiddler_id:" + result.tiddler_id + "\"", + Etag: state.makeTiddlerEtag(result), "Content-Type": "text/plain" }); } else { diff --git a/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-store.js b/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-store.js index c28e515a4..c3b8a0c63 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-store.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-store.js @@ -244,7 +244,7 @@ Get an attachment ready to stream. Returns null if there is an error or: tiddler_id: revision of tiddler stream: stream of file type: type of file -Returns {tiddler_id:} +Returns {tiddler_id:,bag_name:} */ SqlTiddlerStore.prototype.getBagTiddlerStream = function(title,bag_name) { const tiddlerInfo = this.sqlTiddlerDatabase.getBagTiddler(title,bag_name); @@ -253,7 +253,10 @@ SqlTiddlerStore.prototype.getBagTiddlerStream = function(title,bag_name) { return $tw.utils.extend( {}, this.attachmentStore.getAttachmentStream(tiddlerInfo.attachment_blob), - {tiddler_id: tiddlerInfo.tiddler_id}); + { + tiddler_id: tiddlerInfo.tiddler_id + } + ); } else { const { Readable } = require('stream'); const stream = new Readable(); @@ -266,6 +269,7 @@ SqlTiddlerStore.prototype.getBagTiddlerStream = function(title,bag_name) { }; return { tiddler_id: tiddlerInfo.tiddler_id, + bag_name: bag_name, stream: stream, type: tiddlerInfo.tiddler.type || "text/plain" }