Stop storing bag and revision details as tiddler fields

Instead we store them as dictionary tiddlers
This commit is contained in:
Jeremy Ruston 2024-03-20 19:22:12 +00:00
parent eaebeb87c9
commit 3aa5607a3a
6 changed files with 44 additions and 122 deletions

View File

@ -13,7 +13,9 @@ A sync adaptor module for synchronising with MultiWikiServer-compatible servers
"use strict";
var CONFIG_HOST_TIDDLER = "$:/config/multiwikiclient/host",
DEFAULT_HOST_TIDDLER = "$protocol$//$host$/";
DEFAULT_HOST_TIDDLER = "$protocol$//$host$/",
BAG_STATE_TIDDLER = "$:/state/federatial/xememex/tiddlers/bag",
REVISION_STATE_TIDDLER = "$:/state/federatial/xememex/tiddlers/revision";
function MultiWikiClientAdaptor(options) {
this.wiki = options.wiki;
@ -52,14 +54,32 @@ MultiWikiClientAdaptor.prototype.getHost = function() {
};
MultiWikiClientAdaptor.prototype.getTiddlerInfo = function(tiddler) {
return {
bag: tiddler.fields.bag
};
var title = tiddler.fields.title,
revision = this.wiki.extractTiddlerDataItem(REVISION_STATE_TIDDLER,title),
bag = this.wiki.extractTiddlerDataItem(BAG_STATE_TIDDLER,title);
if(revision && bag) {
return {
title: title,
revision: revision,
bag: bag
};
} else {
return undefined;
}
};
MultiWikiClientAdaptor.prototype.getTiddlerRevision = function(title) {
var tiddler = this.wiki.getTiddler(title);
return tiddler.fields.revision;
return this.wiki.extractTiddlerDataItem(REVISION_STATE_TIDDLER,title);
};
MultiWikiClientAdaptor.prototype.setTiddlerInfo = function(title,revision,bag) {
this.wiki.setText(BAG_STATE_TIDDLER,null,title,revision,{suppressTimestamp: true});
this.wiki.setText(REVISION_STATE_TIDDLER,null,title,bag,{suppressTimestamp: true});
};
MultiWikiClientAdaptor.prototype.removeTiddlerInfo = function(title) {
this.wiki.setText(BAG_STATE_TIDDLER,null,title,undefined,{suppressTimestamp: true});
this.wiki.setText(REVISION_STATE_TIDDLER,null,title,undefined,{suppressTimestamp: true});
};
/*
@ -157,11 +177,7 @@ MultiWikiClientAdaptor.prototype.getSkinnyTiddlers = function(callback) {
if(err) {
return callback(err);
}
// Process the tiddlers to make sure the revision is a string
var tiddlers = JSON.parse(data);
for(var t=0; t<tiddlers.length; t++) {
tiddlers[t] = self.convertTiddlerFromTiddlyWebFormat(tiddlers[t]);
}
var tiddlers = $tw.utils.parseJSONSafe(data);
// Invoke the callback with the skinny tiddlers
callback(null,tiddlers);
// If Browswer Storage tiddlers were cached on reloading the wiki, add them after sync from server completes in the above callback.
@ -186,7 +202,7 @@ MultiWikiClientAdaptor.prototype.saveTiddler = function(tiddler,callback,options
headers: {
"Content-type": "application/json"
},
data: this.convertTiddlerToTiddlyWebFormat(tiddler),
data: JSON.stringify(tiddler.getFieldStrings()),
callback: function(err,data,request) {
if(err) {
return callback(err);
@ -220,7 +236,7 @@ MultiWikiClientAdaptor.prototype.loadTiddler = function(title,callback) {
return callback(err);
}
// Invoke the callback
callback(null,self.convertTiddlerFromTiddlyWebFormat(JSON.parse(data)));
callback(null,$tw.utils.parseJSONSafe(data));
}
});
};
@ -254,64 +270,6 @@ MultiWikiClientAdaptor.prototype.deleteTiddler = function(title,callback,options
});
};
/*
Convert a tiddler to a field set suitable for PUTting to TiddlyWeb
*/
MultiWikiClientAdaptor.prototype.convertTiddlerToTiddlyWebFormat = function(tiddler) {
var result = {},
knownFields = [
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
];
if(tiddler) {
$tw.utils.each(tiddler.fields,function(fieldValue,fieldName) {
var fieldString = fieldName === "tags" ?
tiddler.fields.tags :
tiddler.getFieldString(fieldName); // Tags must be passed as an array, not a string
if(knownFields.indexOf(fieldName) !== -1) {
// If it's a known field, just copy it across
result[fieldName] = fieldString;
} else {
// If it's unknown, put it in the "fields" field
result.fields = result.fields || {};
result.fields[fieldName] = fieldString;
}
});
}
// Default the content type
result.type = result.type || "text/vnd.tiddlywiki";
return JSON.stringify(result,null,$tw.config.preferences.jsonSpaces);
};
/*
Convert a field set in TiddlyWeb format into ordinary TiddlyWiki5 format
*/
MultiWikiClientAdaptor.prototype.convertTiddlerFromTiddlyWebFormat = function(tiddlerFields) {
var self = this,
result = {};
// Transfer the fields, pulling down the `fields` hashmap
$tw.utils.each(tiddlerFields,function(element,title,object) {
if(title === "fields") {
$tw.utils.each(element,function(element,subTitle,object) {
result[subTitle] = element;
});
} else {
result[title] = tiddlerFields[title];
}
});
// Make sure the revision is expressed as a string
if(typeof result.revision === "number") {
result.revision = result.revision.toString();
}
// Some unholy freaking of content types
if(result.type === "text/javascript") {
result.type = "application/javascript";
} else if(!result.type || result.type === "None") {
result.type = "text/x-tiddlywiki";
}
return result;
};
/*
Split an MWS Etag into its constituent parts. For example:

View File

@ -28,23 +28,10 @@ exports.handler = function(request,response,state) {
if(tiddlerInfo && tiddlerInfo.tiddler) {
// If application/json is requested then this is an API request, and gets the response in JSON
if(request.headers.accept && request.headers.accept.indexOf("application/json") !== -1) {
var tiddlerFields = {},
knownFields = [
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
];
$tw.utils.each(tiddlerInfo.tiddler,function(value,name) {
if(knownFields.indexOf(name) !== -1) {
tiddlerFields[name] = value;
} else {
tiddlerFields.fields = tiddlerFields.fields || {};
tiddlerFields.fields[name] = value;
}
});
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
state.sendResponse(200,{
Etag: state.makeTiddlerEtag(tiddlerInfo),
"Content-Type": "application/json"
},JSON.stringify(tiddlerFields),"utf8");
},JSON.stringify(tiddlerInfo.tiddler),"utf8");
return;
} else {
// This is not a JSON API request, we should return the raw tiddler content

View File

@ -28,23 +28,10 @@ exports.handler = function(request,response,state) {
if(tiddlerInfo && tiddlerInfo.tiddler) {
// If application/json is requested then this is an API request, and gets the response in JSON
if(request.headers.accept && request.headers.accept.indexOf("application/json") !== -1) {
var tiddlerFields = {},
knownFields = [
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
];
$tw.utils.each(tiddlerInfo.tiddler,function(value,name) {
if(knownFields.indexOf(name) !== -1) {
tiddlerFields[name] = value;
} else {
tiddlerFields.fields = tiddlerFields.fields || {};
tiddlerFields.fields[name] = value;
}
});
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
state.sendResponse(200,{
Etag: state.makeTiddlerEtag(tiddlerInfo),
"Content-Type": "application/json"
},JSON.stringify(tiddlerFields),"utf8");
},JSON.stringify(tiddlerInfo.tiddler),"utf8");
return;
} else {
// This is not a JSON API request, we should return the raw tiddler content

View File

@ -64,12 +64,12 @@ exports.handler = function(request,response,state) {
}
});
writeTiddler({
title: "$:/config/multiwikiclient/tiddlers/bag",
title: "$:/state/multiwikiclient/tiddlers/bag",
text: JSON.stringify(bagInfo),
type: "application/json"
});
writeTiddler({
title: "$:/config/multiwikiclient/tiddlers/revision",
title: "$:/state/multiwikiclient/tiddlers/revision",
text: JSON.stringify(revisionInfo),
type: "application/json"
});

View File

@ -21,20 +21,7 @@ exports.handler = function(request,response,state) {
var recipe_name = $tw.utils.decodeURIComponentSafe(state.params[0]),
title = $tw.utils.decodeURIComponentSafe(state.params[1]),
fields = $tw.utils.parseJSONSafe(state.data);
// Pull up any subfields in the `fields` object
if(typeof fields.fields === "object") {
$tw.utils.each(fields.fields,function(field,name) {
fields[name] = field;
});
delete fields.fields;
}
// Stringify any array fields
$tw.utils.each(fields,function(value,name) {
if($tw.utils.isArray(value)) {
fields[name] = $tw.utils.stringifyList(value);
}
});
if(recipe_name) {
if(recipe_name && title === fields.title) {
var result = $tw.mws.store.saveRecipeTiddler(fields,recipe_name);
if(result) {
response.writeHead(204, "OK",{

View File

@ -86,15 +86,18 @@ Given tiddler fields, tiddler_id and a bag_name, return the tiddler fields after
- Apply the bag_name as the bag field
*/
SqlTiddlerStore.prototype.processOutgoingTiddler = function(tiddlerFields,tiddler_id,bag_name,attachment_blob) {
const fields = Object.assign({},tiddlerFields,{
revision: "" + tiddler_id,
bag: bag_name
});
if(attachment_blob !== null) {
delete fields.text;
fields._canonical_uri = `/bags/${encodeURIComponent(bag_name)}/tiddlers/${encodeURIComponent(tiddlerFields.title)}/blob`;
return $tw.utils.extend(
{},
tiddlerFields,
{
text: undefined,
_canonical_uri: `/bags/${encodeURIComponent(bag_name)}/tiddlers/${encodeURIComponent(tiddlerFields.title)}/blob`
}
);
} else {
return tiddlerFields;
}
return fields;
};
/*