Add conflict resolution to the browser example

This commit is contained in:
Ritchie Martori 2014-02-06 18:31:56 -08:00
parent 0deb161400
commit 86f581466e
4 changed files with 114 additions and 25 deletions

18
dist/loopback.js vendored
View File

@ -3512,6 +3512,18 @@ Change.getCheckpointModel = function() {
return checkpointModel;
}
/**
* Get the model instance.
* @callback {Function} callback
* @param {Error} err
* @param {Model} model The Model instance
*/
Change.prototype.getModelInst = function(callback) {
var Model = this.getModelCtor();
assert(Model, 'unkown model + ', this.modelName);
Model.findById(this.modelId, callback);
}
/**
* When two changes conflict a conflict is created.
@ -3538,8 +3550,8 @@ Conflict.prototype.fetch = function(cb) {
async.parallel(tasks, cb);
function getSourceModel(change, cb) {
conflict.sourceModel.getModel(function(err, model) {
function getSourceModel(cb) {
conflict.sourceChange.getModelInst(function(err, model) {
if(err) return cb(err);
conflict.source = model;
cb();
@ -3547,7 +3559,7 @@ Conflict.prototype.fetch = function(cb) {
}
function getTargetModel(cb) {
conflict.targetModel.getModel(function(err, model) {
conflict.targetChange.getModelInst(function(err, model) {
if(err) return cb(err);
conflict.target = model;
cb();

View File

@ -13,6 +13,17 @@ app.dataSource('local', {
connector: loopback.Memory
});
var network = {
available: true,
toggle: function() {
this.available = !this.available;
replicate();
replicateFromRemote();
},
status: function() {
return this.available ? 'on' : 'off';
}
};
var Color = loopback.getModel('Color');
var LocalColor = app.model('LocalColor', {
dataSource: 'local',
@ -24,36 +35,79 @@ LocalColor.beforeCreate = function(next, color) {
next();
}
function replicate() {
LocalColor.currentCheckpoint(function(err, cp) {
setTimeout(function() {
LocalColor.replicate(cp, Color, {}, function() {
console.log('replicated local to remote');
var localConflicts = [];
function ReplicationCtlr($scope) {
var interval = 1000;
$scope.replicate = replicate;
$scope.replicateFromRemote = replicate;
$scope.conflicts = [];
$scope.resolveUsingRemote = function(conflict) {
conflict.source.name = conflict.target.name;
conflict.source.save(function() {
conflict.source.resolve();
});
}
LocalColor.on('deleted', replicate);
LocalColor.on('changed', replicate);
LocalColor.on('deletedAll', replicate);
setInterval(replicateFromRemote, interval);
setInterval(replicate, interval);
function replicate() {
// reset the conflicts array
while($scope.conflicts.shift());
if(network.available) {
LocalColor.currentCheckpoint(function(err, cp) {
setTimeout(function() {
LocalColor.replicate(cp, Color, {}, function(err, conflicts) {
// console.log('replicated local to remote');
conflicts.forEach(function(conflict) {
conflict.fetch(function() {
var local = conflict.source.name;
var remote = conflict.target.name;
if(local !== remote) {
$scope.conflicts.push(conflict)
}
$scope.$apply();
});
});
});
}, 0);
});
}
}
function replicateFromRemote() {
if(network.available) {
Color.currentCheckpoint(function(err, cp) {
Color.replicate(0, LocalColor, {}, function() {
// console.log('replicated remote to local');
});
});
}, 0);
});
}
}
}
LocalColor.on('deleted', replicate);
LocalColor.on('changed', replicate);
LocalColor.on('deletedAll', replicate);
function NetworkCtrl($scope) {
$scope.network = network;
}
setInterval(function() {
Color.currentCheckpoint(function(err, cp) {
Color.replicate(cp, LocalColor, {}, function() {
console.log('replicated remote to local');
});
});
}, 1000);
function ConflictCtrl($scope) {
$scope.conflicts = localConflicts;
}
function ListCtrl($scope) {
LocalColor.on('changed', update);
LocalColor.on('deleted', update);
function update() {
LocalColor.find({sort: 'name'}, function(err, colors) {
LocalColor.find({order: 'name ASC'}, function(err, colors) {
$scope.colors = colors;
console.log(colors);
$scope.$apply();
});
}

View File

@ -9,10 +9,21 @@
<style>
body {font-size: 24px;}
</style>
<h1 ng-controller="NetworkCtrl">
<input style="font-size: 48px;" type="checkbox" ng-model="network.available" />Enable Network
</h1>
<ul ng-controller="ReplicationCtlr">
<h2 ng-show="conflicts.length">Conflicts:</h2>
<li ng-repeat="conflict in conflicts">
<button ng-click="conflict.resolve()">Use local {{conflict.source.name}}</button>
<button ng-click="resolveUsingRemote(conflict)">Use remote {{conflict.target.name}}</button>
</li>
</ul>
<ul ng-controller="ListCtrl">
<li ng-repeat="color in colors" style="color: {{color.name}}">
<form ng-submit="color.save()">
<input placeholder="{{color.name}}" ng-model="color.name" />
{{color.conflict}}
<a href="#" ng-click="del(color)">delete</a>
</form>
</li>

View File

@ -399,6 +399,18 @@ Change.getCheckpointModel = function() {
return checkpointModel;
}
/**
* Get the model instance.
* @callback {Function} callback
* @param {Error} err
* @param {Model} model The Model instance
*/
Change.prototype.getModelInst = function(callback) {
var Model = this.getModelCtor();
assert(Model, 'unkown model + ', this.modelName);
Model.findById(this.modelId, callback);
}
/**
* When two changes conflict a conflict is created.
@ -425,8 +437,8 @@ Conflict.prototype.fetch = function(cb) {
async.parallel(tasks, cb);
function getSourceModel(change, cb) {
conflict.sourceModel.getModel(function(err, model) {
function getSourceModel(cb) {
conflict.sourceChange.getModelInst(function(err, model) {
if(err) return cb(err);
conflict.source = model;
cb();
@ -434,7 +446,7 @@ Conflict.prototype.fetch = function(cb) {
}
function getTargetModel(cb) {
conflict.targetModel.getModel(function(err, model) {
conflict.targetChange.getModelInst(function(err, model) {
if(err) return cb(err);
conflict.target = model;
cb();