Skip to content

Commit

Permalink
Change options for an existing node
Browse files Browse the repository at this point in the history
This commit makes two changes.

1. The connect() method was changed to be able to set options for an
existing node. If the node is disconnected then the options will just be
saved on the existing node. If the node is already connected, it will be
disconnected, the options will be saved and the node will be
reconnected. This only happens if the current options for the node
differ from the options connect() was called with.

2. A new method getOptions(url) was added which can be used to get the
current options for the given node.

This commit closes #447

I decided not to implement a setOptions() method as I don't think that
it has much usage if we can have the same effect by using connect().
  • Loading branch information
nponiros authored and dfahlander committed Jan 30, 2017
1 parent 5b93c78 commit 146950c
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 29 deletions.
8 changes: 8 additions & 0 deletions addons/Dexie.Syncable/src/Dexie.Syncable.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ export default function Syncable (db) {
}
};

db.syncable.getOptions = function(url, cb) {
return db.transaction('r?', db._syncNodes, () => {
return db._syncNodes.where('url').equals(url).first(function(node) {
return node.syncOptions;
}).then(cb);
});
};

db.syncable.list = function() {
return db.transaction('r?', db._syncNodes, ()=>{
return db._syncNodes.where('type').equals('remote').toArray(function(a) {
Expand Down
73 changes: 45 additions & 28 deletions addons/Dexie.Syncable/src/connect-fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,55 @@ export default function initConnectFn(db, activePeers) {
return peer.url === url;
});
if (existingPeer.length > 0) {
// Never create multiple syncNodes with same protocolName and url. Instead, let the next call to connect() return the same promise that
// have already been started and eventually also resolved. If promise has already resolved (node connected), calling existing promise.then() will give a callback directly.
return existingPeer[0].connectPromise;
const activePeer = existingPeer[0];
const diffObject = {};
Dexie.getObjectDiff(activePeer.syncOptions, options, diffObject);
// Options have been changed
// We need to disconnect and reconnect
if (Object.keys(diffObject).length !== 0) {
return db.syncable.disconnect(url)
.then(() => {
return execConnect();
})
} else {
// Never create multiple syncNodes with same protocolName and url. Instead, let the next call to connect() return the same promise that
// have already been started and eventually also resolved. If promise has already resolved (node connected), calling existing promise.then() will give a callback directly.
return existingPeer[0].connectPromise;
}
}

// Use an object otherwise we wouldn't be able to get the reject promise from
// connectProtocol
var rejectConnectPromise = {p: null};
const connectProtocol = initConnectProtocol(db, protocolInstance, dbAliveID, options, rejectConnectPromise);
const getOrCreateSyncNode = initGetOrCreateSyncNode(db, protocolName, url);
var connectPromise = getOrCreateSyncNode(options).then(function (node) {
return connectProtocol(node, activePeer);
});
function execConnect() {
// Use an object otherwise we wouldn't be able to get the reject promise from
// connectProtocol
var rejectConnectPromise = {p: null};
const connectProtocol = initConnectProtocol(db, protocolInstance, dbAliveID, options, rejectConnectPromise);
const getOrCreateSyncNode = initGetOrCreateSyncNode(db, protocolName, url);
var connectPromise = getOrCreateSyncNode(options).then(function (node) {
return connectProtocol(node, activePeer);
});

var disconnected = false;
var activePeer = {
url: url,
status: Statuses.OFFLINE,
connectPromise: connectPromise,
on: Dexie.Events(null, "disconnect"),
disconnect: function (newStatus, error) {
var pos = activePeers.indexOf(activePeer);
if (pos >= 0) activePeers.splice(pos, 1);
if (error && rejectConnectPromise.p) rejectConnectPromise.p(error);
if (!disconnected) {
activePeer.on.disconnect.fire(newStatus, error);
var disconnected = false;
var activePeer = {
url: url,
status: Statuses.OFFLINE,
connectPromise: connectPromise,
syncOptions: options,
on: Dexie.Events(null, "disconnect"),
disconnect: function (newStatus, error) {
var pos = activePeers.indexOf(activePeer);
if (pos >= 0) activePeers.splice(pos, 1);
if (error && rejectConnectPromise.p) rejectConnectPromise.p(error);
if (!disconnected) {
activePeer.on.disconnect.fire(newStatus, error);
}
disconnected = true;
}
disconnected = true;
}
};
activePeers.push(activePeer);
};
activePeers.push(activePeer);

return connectPromise;
}

return connectPromise;
return execConnect();
};
}
1 change: 1 addition & 0 deletions addons/Dexie.Syncable/src/get-or-create-sync-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function initGetOrCreateSyncNode(db, protocolName, url) {
// Node already there. Make syncContext become an instance of PersistedContext:
node.syncContext = new PersistedContext(node.id, node.syncContext);
node.syncProtocol = protocolName; // In case it was changed (would be very strange but...) could happen...
node.syncOptions = options; // Options could have been changed
db._syncNodes.put(node);
} else {
// Create new node and sync everything
Expand Down
1 change: 1 addition & 0 deletions addons/Dexie.Syncable/test/test-syncable.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
<script src="tests-syncable.js"></script>
<script src="tests-syncable-partials.js"></script>
<script src="tests-syncprovider.js"></script>
<script src="tests-changing-options.js"></script>
</body>
</html>
87 changes: 87 additions & 0 deletions addons/Dexie.Syncable/test/tests-changing-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
///<reference path="test-syncable.html" />
(function () {

/* The following is being tested:
1. getOptions method
2. changing options on an existing connected node by using connect() with a different options object than before
3. changing options on an existing disconnected node by using connect() with a different options object than before
*/
var db = new Dexie("optionsTestDB");
var deletePromise = Dexie.delete("optionsTestDB");

module("tests-changing-options", {
setup: function () {
db.close();
stop();
deletePromise.then(function () {
start()
});
},
teardown: function () {
}
});

asyncTest('Change options on an existing node', function () {
var protocolName = 'testProtocol';
var serverUrl = 'http://dummy.local';
var syncProtocol = {
sync: undefined,
partialsThreshold: 1000
};

Dexie.Syncable.registerSyncProtocol(protocolName, syncProtocol);

db.version(1).stores({objects: "$$"});
db.open();

syncProtocol.sync = function(context, url, options, baseRevision, syncedRevision, changes, partial, applyRemoteChanges, onChangesAccepted, onSuccess) {
propEqual(options, {option1: 'option1'}, 'sync got the correct options');
onSuccess({again: 1000});
};

db.syncable.connect(protocolName, serverUrl, {option1: "option1"})
.then(() => {
return db.syncable.getOptions(serverUrl);
})
.then((options) => {
propEqual(options, {option1: 'option1'}, 'getOptions got the correct options');

syncProtocol.sync = function(context, url, options, baseRevision, syncedRevision, changes, partial, applyRemoteChanges, onChangesAccepted, onSuccess) {
propEqual(options, {newOptions: 'other options'}, 'sync got the new options');
onSuccess({again: 1000});
};

// Test changing options on an already connected node
// We are already connected but are changing options
// We expect that the next getOptions/sync call has the new options
return db.syncable.connect(protocolName, serverUrl, {newOptions: 'other options'});
})
.then(() => {
return db.syncable.getOptions(serverUrl);
})
.then((options) => {
propEqual(options, {newOptions: 'other options'}, 'getOptions got the new options');
// Test changing options on a disconnected existing node
return db.syncable.disconnect(serverUrl);
})
.then(() => {
syncProtocol.sync = function(context, url, options, baseRevision, syncedRevision, changes, partial, applyRemoteChanges, onChangesAccepted, onSuccess) {
propEqual(options, {evenNewerOptions: 'super new options'}, 'sync got the even newer options');
onSuccess({again: 1000});
};

return db.syncable.connect(protocolName, serverUrl, {evenNewerOptions: 'super new options'});
})
.then(() => {
return db.syncable.getOptions(serverUrl);
})
.then((options) => {
propEqual(options, {evenNewerOptions: 'super new options'}, 'getOptions got the even newer options');
})
.catch(function(err) {
ok(false, "Error: " + err);
})
.finally(start);
});
})();
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ asyncTest('should return an existing node if one exists for the given URL', () =
ok(node instanceof db.observable.SyncNode, 'returned node is instance of SyncNode');
strictEqual(node.id, addedNodeID, 'We got the correct node back');
propEqual(node.syncContext, {nodeID: addedNodeID}, 'syncContext contains the correct nodeID');
propEqual(node.syncOptions, nodeOpts, 'node contains the given options');
return db._syncNodes.get(addedNodeID);
})
.then((node) => {
Expand All @@ -84,7 +85,6 @@ asyncTest('should return an existing node if one exists for the given URL', () =
.finally(start);
});


asyncTest('should set myRevision to the last _changes if initialUpload is false', () => {
const nodeOpts = {initialUpload: false};
// Don't reuse the save URL, it would cause an error because the index is not unique
Expand Down

0 comments on commit 146950c

Please sign in to comment.