Add base rev
This commit is contained in:
parent
06ffaca7b7
commit
49935b1b8a
|
@ -11,3 +11,4 @@
|
|||
*.swp
|
||||
*.swo
|
||||
node_modules
|
||||
dist
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
* {padding: 10px;}
|
||||
body {font-size: 12px;}
|
||||
table{
|
||||
margin-bottom:20px;
|
||||
width:100%;
|
||||
}
|
||||
tfoot{
|
||||
text-align:center;
|
||||
}
|
||||
td,th{
|
||||
padding:5px;
|
||||
border:1px solid #333;
|
||||
}
|
||||
th:empty,
|
||||
td:empty{
|
||||
border:none;
|
||||
}
|
||||
th{
|
||||
font-weight:bold;
|
||||
}
|
||||
tr:nth-of-type(odd){
|
||||
background:rgba(255,255,136,0.5);
|
||||
}
|
||||
td:nth-of-type(odd),
|
||||
th:nth-of-type(odd){
|
||||
background:rgba(255,255,136,0.5);
|
||||
}
|
||||
td:nth-of-type(odd),
|
||||
tr:nth-of-type(odd),
|
||||
th:nth-of-type(odd){
|
||||
background:rgba(255,255,136,0.5);
|
||||
}
|
||||
|
||||
input {margin-right: 20px !important;}
|
||||
button {margin-right: 20px !important;}
|
|
@ -17,8 +17,6 @@ var network = {
|
|||
available: true,
|
||||
toggle: function() {
|
||||
this.available = !this.available;
|
||||
replicate();
|
||||
replicateFromRemote();
|
||||
},
|
||||
status: function() {
|
||||
return this.available ? 'on' : 'off';
|
||||
|
@ -44,13 +42,13 @@ function ReplicationCtlr($scope) {
|
|||
|
||||
$scope.status = 'idle';
|
||||
$scope.replicate = replicate;
|
||||
$scope.replicateFromRemote = replicate;
|
||||
$scope.replicateFromRemote = replicateFromRemote;
|
||||
$scope.conflicts = [];
|
||||
|
||||
$scope.resolveUsingRemote = function(conflict) {
|
||||
conflict.source.name = conflict.target.name;
|
||||
conflict.source.save(function() {
|
||||
conflict.source.resolve();
|
||||
conflict.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,41 +56,6 @@ function ReplicationCtlr($scope) {
|
|||
LocalColor.on('changed', replicate);
|
||||
LocalColor.on('deletedAll', replicate);
|
||||
|
||||
var messages = [];
|
||||
function flash(msg) {
|
||||
messages.push(msg);
|
||||
}
|
||||
|
||||
setInterval(function() {
|
||||
var msg = messages.shift();
|
||||
|
||||
if(msg) {
|
||||
$scope.status = msg;
|
||||
}
|
||||
if(!messages.length) {
|
||||
messages.push(msg);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
$scope.reset = function() {
|
||||
flash('reset');
|
||||
clearInterval(localIntervalId);
|
||||
clearInterval(remoteIntervalId);
|
||||
localIntervalId = setInterval(replicateFromRemote, interval);
|
||||
remoteIntervalId = setInterval(replicate, interval);
|
||||
replicate();
|
||||
replicateFromRemote();
|
||||
}
|
||||
|
||||
$scope.enable = function() {
|
||||
$scope.enabled = true;
|
||||
$scope.reset();
|
||||
}
|
||||
|
||||
$scope.disable = function() {
|
||||
$scope.enabled = false;
|
||||
$scope.reset();
|
||||
}
|
||||
|
||||
function replicate() {
|
||||
// reset the conflicts array
|
||||
|
@ -101,15 +64,14 @@ function ReplicationCtlr($scope) {
|
|||
if(network.available) {
|
||||
LocalColor.currentCheckpoint(function(err, cp) {
|
||||
setTimeout(function() {
|
||||
flash('replicating local to remote');
|
||||
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;
|
||||
console.log(conflict);
|
||||
var local = conflict.source && conflict.source.name;
|
||||
var remote = conflict.target && conflict.target.name;
|
||||
if(local !== remote) {
|
||||
$scope.conflicts.push(conflict)
|
||||
$scope.conflicts.push(conflict);
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
|
@ -123,8 +85,8 @@ function ReplicationCtlr($scope) {
|
|||
function replicateFromRemote() {
|
||||
if(network.available) {
|
||||
Color.currentCheckpoint(function(err, cp) {
|
||||
Color.replicate(0, LocalColor, {}, function() {
|
||||
// console.log('replicated remote to local');
|
||||
Color.replicate(cp, LocalColor, {}, function(err, conflicts) {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -144,7 +106,6 @@ function ListCtrl($scope) {
|
|||
LocalColor.on('deleted', update);
|
||||
|
||||
function update() {
|
||||
alert('change event');
|
||||
LocalColor.find({order: 'name ASC'}, function(err, colors) {
|
||||
$scope.colors = colors;
|
||||
$scope.$apply();
|
||||
|
@ -156,8 +117,35 @@ function ListCtrl($scope) {
|
|||
$scope.newColor = null;
|
||||
}
|
||||
|
||||
$scope.del = function(color) {
|
||||
color.destroy();
|
||||
update();
|
||||
}
|
||||
|
||||
function ChangeCtrl($scope) {
|
||||
var Change = LocalColor.getChangeModel();
|
||||
|
||||
Change.on('changed', update);
|
||||
Change.on('deleted', update);
|
||||
|
||||
function update() {
|
||||
Change.find({order: 'checkpoint ASC'}, function(err, changes) {
|
||||
$scope.changes = changes;
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
function RemoteChangeCtrl($scope) {
|
||||
var Change = Color.getChangeModel();
|
||||
|
||||
setInterval(update, 5000);
|
||||
|
||||
function update() {
|
||||
Change.find({order: 'checkpoint ASC'}, function(err, changes) {
|
||||
$scope.changes = changes;
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
update();
|
||||
|
|
|
@ -3,22 +3,16 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>My AngularJS App</title>
|
||||
<link rel="stylesheet" href="css/app.css"/>
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="app.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
body {font-size: 24px;}
|
||||
</style>
|
||||
<h1 ng-controller="NetworkCtrl">
|
||||
<input style="font-size: 48px;" type="checkbox" ng-model="network.available" />Enable Network
|
||||
<input style="font-size: 48px;" type="checkbox" ng-model="network.available" />Online
|
||||
</h1>
|
||||
<ul ng-controller="ReplicationCtlr">
|
||||
<h2>Status: {{status}}</h2>
|
||||
<button ng-click="reset()">Reset</button>
|
||||
<button ng-click="replicate()">Replicate</button>
|
||||
Auto Replication:
|
||||
<button ng-click="enable()" ng-disabled="enabled">Enable</button>
|
||||
<button ng-click="disable()" ng-disabled="!enabled">Disable</button>
|
||||
<button ng-click="replicate()">Push</button>
|
||||
<button ng-click="replicateFromRemote()">Pull</button>
|
||||
<h2 ng-show="conflicts.length">Conflicts:</h2>
|
||||
<li ng-repeat="conflict in conflicts">
|
||||
<button ng-click="conflict.resolve()">Use local {{conflict.source.name}}</button>
|
||||
|
@ -30,7 +24,7 @@
|
|||
<form ng-submit="color.save()">
|
||||
<input placeholder="{{color.name}}" ng-model="color.name" />
|
||||
{{color.conflict}}
|
||||
<a href="#" ng-click="del(color)">delete</a>
|
||||
<a href="#" ng-click="color.destroy()">delete</a>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -40,6 +34,47 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<h1>Local Changes</h1>
|
||||
<table ng-controller="ChangeCtrl">
|
||||
<thead>
|
||||
<th>type</th>
|
||||
<th>rev</th>
|
||||
<th>prev</th>
|
||||
<th>base</th>
|
||||
<th>checkpoint</th>
|
||||
<th>modelId</th>
|
||||
</thead>
|
||||
<tr ng-repeat="change in changes">
|
||||
<td>{{change.type()}}</h3>
|
||||
<td>{{change.rev}}</td>
|
||||
<td>{{change.prev}}</td>
|
||||
<td>{{change.base}}</td>
|
||||
<td>{{change.checkpoint}}</td>
|
||||
<td>{{change.modelId}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h1>Remote Changes</h1>
|
||||
<table ng-controller="RemoteChangeCtrl">
|
||||
<thead>
|
||||
<th>type</th>
|
||||
<th>rev</th>
|
||||
<th>prev</th>
|
||||
<th>base</th>
|
||||
<th>checkpoint</th>
|
||||
<th>modelId</th>
|
||||
</thead>
|
||||
<tr ng-repeat="change in changes">
|
||||
<td>{{change.type()}}</h3>
|
||||
<td>{{change.rev}}</td>
|
||||
<td>{{change.prev}}</td>
|
||||
<td>{{change.base}}</td>
|
||||
<td>{{change.checkpoint}}</td>
|
||||
<td>{{change.modelId}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<script src="loopback.js"></script>
|
||||
<script src="loopback-remote-models.js"></script>
|
||||
<script src="client.js"></script>
|
||||
|
|
|
@ -88,6 +88,10 @@ ServerConnector.prototype.buildModel = function(remoteModel) {
|
|||
var dataSource = this.dataSource;
|
||||
var connector = this;
|
||||
|
||||
if(remoteModel.settings && remoteModel.settings.trackChanges) {
|
||||
remoteModel.settings.trackChanges = false;
|
||||
}
|
||||
|
||||
var Model = loopback.createModel(
|
||||
modelName,
|
||||
remoteModel.properties || {},
|
||||
|
|
|
@ -17,6 +17,7 @@ var properties = {
|
|||
id: {type: String, id: true},
|
||||
rev: {type: String},
|
||||
prev: {type: String},
|
||||
base: {type: String},
|
||||
checkpoint: {type: Number},
|
||||
modelName: {type: String},
|
||||
modelId: {type: String}
|
||||
|
@ -131,10 +132,6 @@ Change.findOrCreate = function(modelName, modelId, callback) {
|
|||
var id = this.idForModel(modelName, modelId);
|
||||
var Change = this;
|
||||
|
||||
console.log(modelId);
|
||||
|
||||
if(!modelId) debugger;
|
||||
|
||||
this.findById(id, function(err, change) {
|
||||
if(err) return callback(err);
|
||||
if(change) {
|
||||
|
@ -160,13 +157,12 @@ Change.findOrCreate = function(modelName, modelId, callback) {
|
|||
|
||||
Change.prototype.rectify = function(cb) {
|
||||
var change = this;
|
||||
var lastKnownRevision = this.rev;
|
||||
var tasks = [
|
||||
updateRevision,
|
||||
updateCheckpoint
|
||||
];
|
||||
|
||||
if(this.rev) this.prev = this.rev;
|
||||
|
||||
async.parallel(tasks, function(err) {
|
||||
if(err) return cb(err);
|
||||
change.save(cb);
|
||||
|
@ -177,6 +173,13 @@ Change.prototype.rectify = function(cb) {
|
|||
change.currentRevision(function(err, rev) {
|
||||
if(err) return Change.handleError(err, cb);
|
||||
change.rev = rev;
|
||||
if(lastKnownRevision && lastKnownRevision !== change.rev) {
|
||||
change.prev = lastKnownRevision;
|
||||
}
|
||||
|
||||
if(change.type() === Change.CREATE) {
|
||||
change.base = change.rev;
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
@ -285,7 +288,8 @@ Change.prototype.equals = function(change) {
|
|||
*/
|
||||
|
||||
Change.prototype.isBasedOn = function(change) {
|
||||
return this.prev === change.rev;
|
||||
return this.prev === change.rev
|
||||
|| this.base === change.rev;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -455,6 +459,11 @@ Conflict.prototype.fetch = function(cb) {
|
|||
}
|
||||
|
||||
Conflict.prototype.resolve = function(cb) {
|
||||
this.sourceChange.prev = this.targetChange.rev;
|
||||
this.sourceChange.save(cb);
|
||||
var conflict = this;
|
||||
|
||||
this.sourceChange.reload(function() {
|
||||
conflict.sourceChange.prev = conflict.targetChange.rev;
|
||||
conflict.sourceChange.base = conflict.targetChange.rev;
|
||||
conflict.sourceChange.save(cb);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -380,6 +380,7 @@ Model.replicate = function(since, targetModel, options, callback) {
|
|||
getDiffFromTarget,
|
||||
createSourceUpdates,
|
||||
bulkUpdate,
|
||||
setBaseRevisions,
|
||||
checkpoint
|
||||
];
|
||||
|
||||
|
@ -411,10 +412,16 @@ Model.replicate = function(since, targetModel, options, callback) {
|
|||
sourceModel.createUpdates(diff.deltas, cb);
|
||||
}
|
||||
|
||||
function bulkUpdate(updates, cb) {
|
||||
function bulkUpdate(_updates, cb) {
|
||||
updates = _updates;
|
||||
targetModel.bulkUpdate(updates, cb);
|
||||
}
|
||||
|
||||
function setBaseRevisions() {
|
||||
var cb = arguments[arguments.length - 1];
|
||||
targetModel.setBaseRevisions(updates, cb);
|
||||
}
|
||||
|
||||
function checkpoint() {
|
||||
var cb = arguments[arguments.length - 1];
|
||||
sourceModel.checkpoint(cb);
|
||||
|
@ -486,8 +493,6 @@ Model.bulkUpdate = function(updates, callback) {
|
|||
switch(update.type) {
|
||||
case Change.UPDATE:
|
||||
case Change.CREATE:
|
||||
// var model = new Model(update.data);
|
||||
// tasks.push(model.save.bind(model));
|
||||
tasks.push(function(cb) {
|
||||
var model = new Model(update.data);
|
||||
model.save(cb);
|
||||
|
@ -505,6 +510,38 @@ Model.bulkUpdate = function(updates, callback) {
|
|||
async.parallel(tasks, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base revision for the changes in the given updates list.
|
||||
*
|
||||
* @param {Array} updates An updates list (usually from Model.createUpdates())
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Model.setBaseRevisions = function(updates, callback) {
|
||||
var tasks = [];
|
||||
var Model = this;
|
||||
var idName = this.dataSource.idName(this.modelName);
|
||||
var Change = this.getChangeModel();
|
||||
|
||||
console.log('setBaseRevisions');
|
||||
|
||||
updates.forEach(function(update) {
|
||||
tasks.push(function(cb) {
|
||||
Change.findById(update.change.id, function(err, change) {
|
||||
if(err) return cb(err);
|
||||
if(change) {
|
||||
change.base = update.change.rev;
|
||||
change.save(cb);
|
||||
} else {
|
||||
process.nextTick(cb);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async.parallel(tasks, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Change` model.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue