Skip to content

Commit 5acecc8

Browse files
buildymcbuildykobaska
authored andcommitted
Add multitenancy
1 parent 5200b28 commit 5acecc8

File tree

5 files changed

+261
-54
lines changed

5 files changed

+261
-54
lines changed

common/models/change.js

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -186,27 +186,51 @@ module.exports = function(Change) {
186186

187187
cb = cb || utils.createPromiseCallback();
188188

189-
change.currentRevision(function(err, rev) {
189+
var model = this.getModelCtor();
190+
var id = this.getModelId();
191+
192+
model.findById(id, function(err, inst) {
190193
if (err) return cb(err);
191194

192-
// avoid setting rev and prev to the same value
193-
if (currentRev === rev) {
194-
change.debug('rev and prev are equal (not updating anything)');
195-
return cb(null, change);
196-
}
195+
change.tenant = getTenant(model, inst);
197196

198-
// FIXME(@bajtos) Allo callers to pass in the checkpoint value
199-
// (or even better - a memoized async function to get the cp value)
200-
// That will enable `rectifyAll` to cache the checkpoint value
201-
change.constructor.getCheckpointModel().current(
202-
function(err, checkpoint) {
203-
if (err) return cb(err);
204-
doRectify(checkpoint, rev);
197+
change.currentRevision(inst, function(err, rev) {
198+
if (err) return cb(err);
199+
200+
// avoid setting rev and prev to the same value
201+
if (currentRev === rev) {
202+
change.debug('rev and prev are equal (not updating anything)');
203+
return cb(null, change);
205204
}
206-
);
205+
206+
// FIXME(@bajtos) Allow callers to pass in the checkpoint value
207+
// (or even better - a memoized async function to get the cp value)
208+
// That will enable `rectifyAll` to cache the checkpoint value
209+
change.constructor.getCheckpointModel().current(
210+
function(err, checkpoint) {
211+
if (err) return cb(err);
212+
doRectify(checkpoint, rev);
213+
}
214+
);
215+
});
207216
});
208217
return cb.promise;
209218

219+
function getTenant(model, inst) {
220+
var tenant = null;
221+
var tenantProperty;
222+
223+
if (model && model.settings && model.settings.tenantProperty) {
224+
tenantProperty = model.settings.tenantProperty;
225+
}
226+
227+
if (tenantProperty && inst && inst[tenantProperty]) {
228+
tenant = inst[tenantProperty];
229+
}
230+
231+
return tenant;
232+
}
233+
210234
function doRectify(checkpoint, rev) {
211235
if (rev) {
212236
if (currentRev === rev) {
@@ -230,7 +254,7 @@ module.exports = function(Change) {
230254
if (currentRev) {
231255
change.prev = currentRev;
232256
} else if (!change.prev) {
233-
change.debug('ERROR - could not determing prev');
257+
change.debug('ERROR - could not determining prev');
234258
change.prev = Change.UNKNOWN;
235259
}
236260
change.debug('updated prev');
@@ -254,23 +278,35 @@ module.exports = function(Change) {
254278

255279
/**
256280
* Get a change's current revision based on current data.
281+
* @param {Object} instance Optional instance object to get the revision string for, if not provided it will be queried
257282
* @callback {Function} callback
258283
* @param {Error} err
259284
* @param {String} rev The current revision
260285
*/
261286

262-
Change.prototype.currentRevision = function(cb) {
287+
Change.prototype.currentRevision = function(instance, cb) {
288+
// if only one argument was provided, then set that as the callback
289+
if (typeof instance === 'function') {
290+
cb = instance;
291+
instance = null;
292+
}
293+
263294
cb = cb || utils.createPromiseCallback();
264-
var model = this.getModelCtor();
265-
var id = this.getModelId();
266-
model.findById(id, function(err, inst) {
267-
if (err) return cb(err);
268-
if (inst) {
269-
cb(null, Change.revisionForInst(inst));
270-
} else {
271-
cb(null, null);
272-
}
273-
});
295+
296+
if (instance) {
297+
cb(null, Change.revisionForInst(instance));
298+
} else {
299+
var model = this.getModelCtor();
300+
var id = this.getModelId();
301+
model.findById(id, function(err, inst) {
302+
if (err) return cb(err);
303+
if (inst) {
304+
cb(null, Change.revisionForInst(inst));
305+
} else {
306+
cb(null, null);
307+
}
308+
});
309+
}
274310
return cb.promise;
275311
};
276312

common/models/change.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
},
2121
"modelId": {
2222
"type": "string"
23+
},
24+
"tenant": {
25+
"type": "string",
26+
"default": null
2327
}
2428
}
2529
}

lib/persisted-model.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -740,8 +740,8 @@ module.exports = function(registry) {
740740
accepts: [
741741
{ arg: 'id', type: 'any', description: 'Model id', required: true,
742742
http: { source: 'path' }},
743-
{ arg: 'data', type: 'object', model: typeName, http: { source: 'body' }, description:
744-
'Model instance data' },
743+
{ arg: 'data', type: 'object', model: typeName, http: { source: 'body' }, description:
744+
'Model instance data' },
745745
],
746746
returns: { arg: 'data', type: typeName, root: true },
747747
http: [{ verb: 'post', path: '/:id/replace' }],
@@ -813,7 +813,7 @@ module.exports = function(registry) {
813813
description: 'Delete a model instance by {{id}} from the data source.',
814814
accessType: 'WRITE',
815815
accepts: {arg: 'id', type: 'any', description: 'Model id', required: true,
816-
http: {source: 'path'}},
816+
http: {source: 'path'}},
817817
http: {verb: 'del', path: '/:id'},
818818
returns: {arg: 'count', type: 'object', root: true}
819819
});
@@ -865,7 +865,8 @@ module.exports = function(registry) {
865865
accessType: 'READ',
866866
accepts: [
867867
{arg: 'since', type: 'number', description: 'Only return changes since this checkpoint'},
868-
{arg: 'filter', type: 'object', description: 'Only include changes that match this filter'}
868+
{arg: 'filter', type: 'object', description: 'Only include changes that match this filter'},
869+
{arg: 'tenant', type: 'string', description: 'Only include changes that match this tenant'},
869870
],
870871
returns: {arg: 'changes', type: 'array', root: true},
871872
http: {verb: 'get', path: '/changes'}
@@ -996,12 +997,13 @@ module.exports = function(registry) {
996997
* to reduce the number of results returned.
997998
* @param {Number} since Return only changes since this checkpoint.
998999
* @param {Object} filter Include only changes that match this filter, the same as for [#persistedmodel-find](find()).
1000+
* @param {String} tenant Include only changes for this tenant
9991001
* @callback {Function} callback Callback function called with `(err, changes)` arguments. Required.
10001002
* @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object).
10011003
* @param {Array} changes An array of [Change](#change) objects.
10021004
*/
10031005

1004-
PersistedModel.changes = function(since, filter, callback) {
1006+
PersistedModel.changes = function(since, filter, tenant, callback) {
10051007
if (typeof since === 'function') {
10061008
filter = {};
10071009
callback = since;
@@ -1012,21 +1014,32 @@ module.exports = function(registry) {
10121014
since = -1;
10131015
filter = {};
10141016
}
1017+
if (typeof tenant === 'function') {
1018+
callback = tenant;
1019+
tenant = null;
1020+
}
10151021

10161022
var idName = this.dataSource.idName(this.modelName);
10171023
var Change = this.getChangeModel();
10181024
var model = this;
1025+
var changeFilter = {
1026+
where: {
1027+
checkpoint: {gte: since},
1028+
modelName: this.modelName
1029+
}
1030+
};
1031+
1032+
if (tenant) {
1033+
changeFilter.where.tenant = tenant;
1034+
}
10191035

10201036
filter = filter || {};
10211037
filter.fields = {};
10221038
filter.where = filter.where || {};
10231039
filter.fields[idName] = true;
10241040

10251041
// TODO(ritch) this whole thing could be optimized a bit more
1026-
Change.find({ where: {
1027-
checkpoint: { gte: since },
1028-
modelName: this.modelName
1029-
}}, function(err, changes) {
1042+
Change.find(changeFilter, function(err, changes) {
10301043
if (err) return callback(err);
10311044
if (!Array.isArray(changes) || changes.length === 0) return callback(null, []);
10321045
var ids = changes.map(function(change) {
@@ -1176,7 +1189,7 @@ module.exports = function(registry) {
11761189
async.waterfall(tasks, done);
11771190

11781191
function getSourceChanges(cb) {
1179-
sourceModel.changes(since.source, options.filter, debug.enabled ? log : cb);
1192+
sourceModel.changes(since.source, options.filter, options.tenant, debug.enabled ? log : cb);
11801193

11811194
function log(err, result) {
11821195
if (err) return cb(err);

0 commit comments

Comments
 (0)