As I cannot get any output from the sync function (like using console.log(....)
) it really feels like a black box when trying to develop the formula.
Any good advice on how to debug the formula would be much appreciated! It is quite time consuming to make a change, stop/start the sync. gateway - run a sync, and “guess” if things went Ok - and mostly try to figure out why not?
I have a challenge in getting public and private documents replicated correctly. The scene is this:
- All can sync public documents (mostly meta data) to Couchbase Lite (CBL) - but cannot write back to the server
- A user can only sync his private docs to CBL - and delete/update them back to the server
- A user can mark some of his documents as “public” - but should still be able to update/delete as if his “private” document
I have made many attempts over the last couple of days. This is my current sync. formula:
function (doc, oldDoc) {
function _log(t) {
// Does not work - yet...
// console.log('SG: ' + t);
}
function _getUserKey(d) {
var key = null;
if (d) {
if (d.type == 'User') {
key = d.key;
} else if (d.type == 'FishingTrip' || d.type == 'Catch' || d.type == 'Photo' || d.type == 'Private' || d.type == 'Image' || d.type == 'Feedback') {
// TODO: Not sure about Feedback here....??
key = d.userkey;
}
}
return key;
}
if (doc && doc._deleted && oldDoc) {
// Doc. deleted -> if public then require
var userkey = _getUserKey(oldDoc);
if (userkey != null) {
requireUser(userkey);
} else {
requireAdmin();
}
_log('doc deleted, id: ' + (doc._id || 'no id!') + ', ' + (oldDoc ? ('old key=' + oldDoc.key + ', userkey=' + oldDoc.userkey) : 'no oldDoc'));
return;
}
// Document type is mandatory
if (doc.type == undefined) {
throw ({ forbidden: "Document type is required." });
}
// Some document types not allowed on mobile
if (doc.type == 'EnvLake' || doc.type == 'EnvMeasurement' || doc.type == 'ActivityLog') {
throw ({ forbidden: "Document type not allowed to sync to mobile..." });
}
// Document key is mandatory
if (doc.key == undefined) {
throw ({ forbidden: "Document key is required." });
}
// Allow anyone to create a Feedback on the server
if (oldDoc == null && doc.type == 'Feedback') {
_log('Created feedback: ' + (doc._id || 'no id!') + ', key: ' + doc.key + ', user: ' + doc.userkey);
return;
}
// All public docs are available in the app
if (doc.ispublic) {
_log('public, id: ' + (doc._id || 'no id!'));
channel('!');
}
// All non-club fishing trips and catches are available (for stats)
if ((doc.type == 'FishingTrip' || doc.type == 'Catch') && doc.clubonlykey == undefined) {
_log('non-club trips, id: ' + (doc._id || 'no id!'));
channel('!');
}
// All non-specific user info is available (for stats)
if (doc.type == 'User') {
_log('User doc, id: ' + (doc._id || 'no id!'));
channel('!');
}
// Only non-public docs "owned" by user can be replicated
var userkey = _getUserKey(doc);
if (userkey != null) {
if (oldDoc != null) {
// Update
if (oldDoc.type != doc.type) {
throw ({ forbidden: "Can't change doc type" });
}
if (oldDoc.key != doc.key) {
throw ({ forbidden: "Can't change doc key" });
}
if (oldDoc.userkey && oldDoc.userkey != doc.userkey) {
throw ({ forbidden: "Can't change user key" });
}
}
_log('User owned, id: ' + (doc._id || 'no id!') + ', type: ' + doc.type + ', user: ' + doc.userkey);
if(doc.ispublic){
requireAdmin();
}
requireUser(userkey);
channel('channel.' + userkey);
access(userkey, 'channel.' + userkey);
}
/*
if(oldDoc == null){
// Creation
if(doc.type == 'Feedback'){
_log('Created feedback: ' + (doc._id || 'no id!') + ', key: ' + doc.key + ', user: ' + doc.userkey);
}else{
if(doc.key == undefined){
throw({forbidden: "Can't create doc without key"});
}
if(doc.type == 'User'){
// Ok
}else {
if(doc.userkey == undefined){
//throw({forbidden: "Can't create doc without user key"});
}
}
requireUser(_getUserKey(doc));
_log('Create: ' + (doc._id || 'no id!') + ', type: ' + doc.type + ', user: ' + doc.userkey);
}
}else{
// Update
if(oldDoc.type != doc.type){
throw({forbidden: "Can't change doc type"});
}
if(oldDoc.key != doc.key){
throw({forbidden: "Can't change doc key"});
}
if(oldDoc.userkey && oldDoc.userkey != doc.userkey){
throw({forbidden: "Can't change user key"});
}
requireUser(_getUserKey(oldDoc));
_log('Update: ' + (doc._id || 'no id!') + ', type: ' + doc.type + ', user: ' + doc.userkey);
}
*/
// TODO: Check if covered:
// Creation of new Feedback docs (perhaps without userkey)?
// Creation of new docs?
// Updates to existing docs?
// Readonly for all non-user specific docs...
}
So at the moment deletions from the server are not sent to the CBL. The new document is written Ok.
Earlier today I had this part of the formula to handle deletions:
if (doc && doc._deleted){
_log('doc deleted, id: ' + (doc._id || 'no id!') + ', ' + (oldDoc ? ('old key=' + oldDoc.key + ', userkey=' + oldDoc.userkey) : 'no oldDoc'));
return;
}
But I want to try and add functionality to disallow the user from changing public documents on the server by using requireUser(userkey)
and requireAdmin()
.
As I understand it all changes written to CBL are written as admin - and therefore I thought that using requireAdmin()
would allow deletions locally - but not on the server. As I mentioned some documents could be public as well as private - but if I do requireUser(userkey) and this is not the specific user (but there is a userkey) then I should use requireAdmin()
to write any deletion locally (initiated by the other user) - but of course not be able to send a deletion of another user’s document back to the server.
How can I do this? Obviously, my current formula is not working - and not even for my own documents.
I really would like to see some specific examples of sync. formulas to find out how to do things like this (that aren’t really that complex!)
I’m on Couchbase Community Server 6.0.0 and C# Couchbase Lite 2.5