Skip to content

Commit

Permalink
Rewrite api middleware layer
Browse files Browse the repository at this point in the history
  • Loading branch information
baranga committed Jun 4, 2019
1 parent f14d9f3 commit c1e2d15
Show file tree
Hide file tree
Showing 9 changed files with 619 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/api/are-plugins-compatible.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Check if something looks like options
* @param {MongoTenantOptions} options
* @returns {boolean}
*/
const isPluginOptions = (options) => (
options &&
options.accessorMethod &&
options.tenantIdKey &&
true
);
/**
* Checks if instance is compatible to other plugin instance
*
* For population of referenced models it's necessary to detect if the tenant
* plugin installed in these models is compatible to the plugin of the host
* model. If they are compatible they are one the same "level".
*
* @param {MongoTenantOptions} a
* @param {MongoTenantOptions} b
* @returns {boolean}
*/
module.exports = (a, b) => {
return (
isPluginOptions(a) &&
isPluginOptions(b) &&
a.tenantIdKey === b.tenantIdKey
);
};
27 changes: 27 additions & 0 deletions src/api/are-plugins-compatible.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const arePluginsCompatible = require('./are-plugins-compatible');

describe('are-plugins-compatible', () => {
describe('when called with proper plugin options', () => {
const options = {
accessorMethod: 'byTenant',
tenantIdKey: 'tenantId',
};

it('returns true if they have equal tenantIdKey\'s', () => {
const a = {...options};
const b = {...options};
const result = arePluginsCompatible(a, b);
expect(result).toBe(true);
});

it('returns false if they have different tenantIdKey\'s', () => {
const a = {...options};
const b = {
...options,
tenantIdKey: 'dimensionId',
};
const result = arePluginsCompatible(a, b);
expect(result).toBe(false,);
});
});
});
27 changes: 27 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const buildModelCache = require('./tenant-aware-model-cache');
const createTenantAwareModel = require('./tenant-aware-model');

/**
* @param schema
* @param {MongoTenantOptions} options
*/
module.exports = ({schema, options}) => {
const {accessorMethod} = options;
const cache = buildModelCache();

Object.assign(schema.statics, {
[accessorMethod]: function (tenantId) {
if (!cache.has(this.modelName, tenantId)) {
const base = this.model(this.modelName);
const model = createTenantAwareModel({base, tenantId});
cache.set(this.modelName, tenantId, model);
}
return cache.get(this.modelName, tenantId);
},
get mongoTenant() {
return {...options};
}
});

return this;
};
25 changes: 25 additions & 0 deletions src/api/tenant-aware-db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const arePluginsCompatible = require('./are-plugins-compatible');

/**
* Create db connection bound to a specific tenant
*
* @param {Connection} db
* @param {*} tenantId
* @param {MongoTenantOptions} options
* @returns {Connection}
*/
module.exports = ({db, tenantId, options}) => {
const awareDb = Object.create(db);
awareDb.model = (name) => {
const unawareModel = db.model(name);
/** @type MongoTenantOptions */
const otherPluginOptions = unawareModel.mongoTenant;

if (!arePluginsCompatible(options, otherPluginOptions)) {
return unawareModel;
}

return unawareModel[otherPluginOptions.accessorMethod](tenantId);
};
return awareDb;
};
57 changes: 57 additions & 0 deletions src/api/tenant-aware-db.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const createTenantAwareDb = require('./tenant-aware-db');
const buildOptions = require('../options');

describe('tenant-aware-db', () => {
describe('when called with proper parameters', () => {
const tenantId = '23';
const options = buildOptions();

it('overwrites the model method', () => {
const db = {
model: () => {},
};
const result = createTenantAwareDb({db, tenantId, options});
expect(result).toHaveProperty('model');
expect(result.model).toBeInstanceOf(Function);
expect(result.model).not.toBe(db.model);
});

it('returns a tenant aware model if compatible', () => {
const awareModel = {
hasTenantContext: true,
mongoTenant: {...options},
};
const unawareModel = {
[options.accessorMethod]: () => awareModel,
mongoTenant: {...options},
};
const db = {
model: () => unawareModel,
};

const awareDb = createTenantAwareDb({db, tenantId, options});
const result = awareDb.model('test');

expect(result).toBe(awareModel);
});

it('returns a tenant unaware model if not compatible', () => {
const unawareModel = {
[options.accessorMethod]: () => { throw new Error; },
mongoTenant: {
...options,
tenantIdKey: 'dimension',
},
};
const db = {
model: () => unawareModel,
};

const awareDb = createTenantAwareDb({db, tenantId, options});
const result = awareDb.model('test');

expect(result).toBe(unawareModel);
});
});
});

13 changes: 13 additions & 0 deletions src/api/tenant-aware-model-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = () => {
const cache = {};
return {
has: (name, id) => (cache[name] && cache[name][id] && true || false),
get: (name, id) => (cache[name] && cache[name][id]),
set: (name, id, value) => {
cache[name] = {
...(cache[name] || {}),
[id]: value,
};
}
};
};
44 changes: 44 additions & 0 deletions src/api/tenant-aware-model-cache.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const buildTenantAwareModelCache = require('./tenant-aware-model-cache');

describe('tenant-aware-model-cache', () => {
describe('builds a cache that', () => {
it('returns a cached model', () => {
const model = {};
const cache = buildTenantAwareModelCache();

cache.set('test', '23', model);
const result = cache.get('test', '23');

expect(result).toBe(model);
});

it('reports a stored model as cached', () => {
const model = {};
const cache = buildTenantAwareModelCache();

cache.set('test', '23', model);
const result = cache.has('test', '23');

expect(result).toBe(true);
});

it('reports a unknown model as not cached', () => {
const cache = buildTenantAwareModelCache();

const result = cache.has('test', '23');

expect(result).toBe(false);
});

it('reports a unknown tenant as not cached', () => {
const model = {};
const cache = buildTenantAwareModelCache();

cache.set('test', '23', model);
const result = cache.has('test', '42');

expect(result).toBe(false);
});
});
});

Loading

0 comments on commit c1e2d15

Please sign in to comment.