Skip to content

Commit

Permalink
Merge branch 'develop' into fix-issue-16
Browse files Browse the repository at this point in the history
  • Loading branch information
baranga committed Mar 20, 2019
2 parents 9960536 + ddf5b6e commit e1b10ca
Show file tree
Hide file tree
Showing 11 changed files with 2,732 additions and 39 deletions.
24 changes: 22 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ node_js:
- "8"
- "6"
- "4"
env:
- MONGOOSE_VERSION=5
- MONGOOSE_VERSION=4
services: mongodb
script: "npm run hint && npm run-script test-and-cover"
after_script: "npm run coveralls"
cache: yarn
git:
depth: 3
before_script:
- sleep 15
install:
- yarn
- yarn add --dev mongoose@^$MONGOOSE_VERSION
script:
- yarn hint
- yarn test-and-cover
jobs:
include:
- stage: deploy
if: env(MONGOOSE_VERSION) = 5
node_js: "8"
script:
- yarn test-and-cover
- yarn coveralls
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ Many thanks for contributing to [node-mongo-tenant](https://github.com/craftup/n

* [baranga](https://github.com/baranga)
* [ivanseidel](https://github.com/ivanseidel)
* [felipe-augusto](https://github.com/felipe-augusto)

A full list of contributors can be viewed [here](https://github.com/craftup/node-mongo-tenant/graphs/contributors).
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License

Copyright (c) 2016-2017 craftup
Copyright (c) 2016-2018 craftup

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,32 @@ is a question of a single line of config.

## Requirements

Tested with mongoose from version >= 4.3.0.
Mongo tenant is compatible with mongoose 4 and 5.

## Incompatibilities

* Mongo Tenant is incomapatible with mongoose 4.8.1-4.8.2 see [Automattic/mongoose#4947](https://github.com/Automattic/mongoose/issues/4947).
* Mongo Tenant does not work with mongoose 4.8.1-4.8.2 see [Automattic/mongoose#4947](https://github.com/Automattic/mongoose/issues/4947).

## Install

`$ npm install --save mongo-tenant`
```sh
$ npm i -S mongo-tenant
// or
$ yarn add mongo-tenant
```

## Use

Register the plugin on the relevant mongoose schema.

```javascript
const
mongoose = require('mongoose'),
mongoTenant = require('mongo-tenant');
const mongoose = require('mongoose');
const mongoTenant = require('mongo-tenant');

let MySchema = new mongoose.Schema({});
const MySchema = new mongoose.Schema({});
MySchema.plugin(mongoTenant);

let MyModel = mongoose.model('MyModel', MySchema);
const MyModel = mongoose.model('MyModel', MySchema);
```

Retrieve the model in tenant scope with static `byTenant` method. This will return
Expand All @@ -59,7 +62,7 @@ It has the exactly same interface as any other mongoose model but prevents
the access to other tenant scopes.

```javascript
let MyTenantBoundModel = MyModel.byTenant('some-tenant-id');
const MyTenantBoundModel = MyModel.byTenant('some-tenant-id');

(new MyTenantBoundModel()).getTenantId() === 'some-tenant-id'; // true

Expand All @@ -79,7 +82,7 @@ the bound tenant scope with `getTenantId()` method.
// With enabled mongo-tenant on a schema, all tenant bound models
// and there instances provide the hasTenantContext flag
if (SomeModelClassOrInstance.hasTenantContext) {
let tenantId = SomeModelClassOrInstance.getTenantId();
const tenantId = SomeModelClassOrInstance.getTenantId();
...
}
```
Expand All @@ -99,7 +102,7 @@ To skip the automatic unique key extension of mongo-tenant for a specific
index you can set the `preserveUniqueKey` config option to true.

```javascript
let MySchema = new mongoose.Schema({
const MySchema = new mongoose.Schema({
someField: {
unique: true,
preserveUniqueKey: true
Expand Down Expand Up @@ -147,7 +150,7 @@ But you have the ability to adjust the behavior and api of the mongo tenant
to your needs.

```javascript
let config = {
const config = {
/**
* Whether the mongo tenant plugin MAGIC is enabled. Default: true
*/
Expand Down
31 changes: 28 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class MongoTenant {
// delete the old index
path._index = null;
delete path.options.unique;

// prepare new options
let indexOptions = {
unique: true
Expand Down Expand Up @@ -271,6 +271,9 @@ class MongoTenant {

if (Array.isArray(operations[0])) {
pipeline = operations[0];
} else if (arguments.length === 1 || typeof arguments[1] === 'function') {
// mongoose 5.x compatibility
pipeline = operations[0] = [operations[0]];
}

pipeline.unshift({
Expand All @@ -282,6 +285,18 @@ class MongoTenant {
return super.aggregate.apply(this, operations);
}

static deleteOne(conditions, callback) {
conditions[tenantIdKey] = this[tenantIdGetter]();

return super.deleteOne(conditions, callback);
}

static deleteMany(conditions, options, callback) {
conditions[tenantIdKey] = this[tenantIdGetter]();

return super.deleteMany(conditions, options, callback);
}

static remove(conditions, callback) {
if (arguments.length === 1 && typeof conditions === 'function') {
callback = conditions;
Expand Down Expand Up @@ -338,7 +353,17 @@ class MongoTenant {
continue;
}

MongoTenantModel[staticProperty] = BaseModel[staticProperty];
let descriptor = Object.getOwnPropertyDescriptor(BaseModel, staticProperty);
Object.defineProperty(MongoTenantModel, staticProperty, descriptor);
}

// create tenant models for discriminators if they exist
if (BaseModel.discriminators) {
MongoTenantModel.discriminators = {};

for (let key in BaseModel.discriminators) {
MongoTenantModel.discriminators[key] = this.createTenantAwareModel(BaseModel.discriminators[key], tenantId);
}
}

return MongoTenantModel;
Expand Down Expand Up @@ -453,7 +478,7 @@ class MongoTenant {
query._conditions[tenantIdKey] = tenantId;

// avoid jumping tenant context when overwriting a model.
if (query.options.overwrite) {
if ((tenantIdKey in query._update) || query.options.overwrite) {
query._update[tenantIdKey] = tenantId;
}

Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mongo-tenant",
"version": "1.3.2",
"version": "1.5.0",
"description": "The mongo-tenant is a multi-tenancy plugin for mongoose.",
"author": "Craftup Coding Company <[email protected]>",
"contributors": [
Expand Down Expand Up @@ -36,11 +36,11 @@
"chai": "^4.1.2",
"coveralls": "^3.0.0",
"jshint": "^2.9.5",
"mocha": "^4.0.1",
"mocha": "^5.0.4",
"mocha-mongoose": "^1.2.0",
"mongodb": "^2.2.33",
"mongoose": "^4.13.0",
"nyc": "^11.3.0"
"mongodb": "^2.2.35",
"mongoose": "^5.0.11",
"nyc": "^11.6.0"
},
"peerDependencies": {
"mongoose": ">=4.3.0 <=4.8.0 || >=4.8.3"
Expand Down
10 changes: 10 additions & 0 deletions release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ releases:
changed:
- Models with tenant context (`Model.byTenant(...)`) will have a modified db connection object
returning Models bound to same context.
- version: 1.5.0
date: 2018-07-06
fixed:
- Fix discriminator handling, properly preserve tenant scope. ([#38](https://github.com/craftup/node-mongo-tenant/issues/38))
- version: 1.4.0
date: 2018-03-20
description: Mongoose 5 compatibility
added:
- >
Make mongo tenant compatible with mongoose 5. :tada: ([#34](https://github.com/craftup/node-mongo-tenant/issues/34))
- version: 1.3.2
date: 2017-11-10
fixed:
Expand Down
21 changes: 12 additions & 9 deletions test/_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,24 @@

'use strict';

const
MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost/mongo-tenant-test',
mongoose = require('mongoose'),
mochaMongoose = require('mocha-mongoose'),
mongoTenantPlugin = require('../index.js'),
Schema = mongoose.Schema;
const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost/mongo-tenant-test';
const mongoose = require('mongoose');
const mochaMongoose = require('mocha-mongoose');
const mongoTenantPlugin = require('../index.js');
const Schema = mongoose.Schema;

let testModelUnifier = 0;

mongoose.Promise = Promise;

function createTestModel(schemaDefinition, options) {
let schema = new Schema(schemaDefinition);

options = Object.assign({
applyOnSchema: void 0,
mongoTenant: void 0,
withPlugin: true
}, options);

let schema = new Schema(schemaDefinition, options.schemaOptions);

if (typeof options.applyOnSchema === 'function') {
options.applyOnSchema(schema);
Expand All @@ -44,7 +43,11 @@ function clearDatabase() {
beforeEach(function(done) {
if (mongoose.connection.db) return done();

mongoose.connect(MONGO_URI, { useMongoClient: true }, done);
if (mongoose.version[0] === '4') {
mongoose.connect(MONGO_URI, { useMongoClient: true }, done);
} else {
mongoose.connect(MONGO_URI, done);
}
});
}

Expand Down
70 changes: 70 additions & 0 deletions test/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,75 @@ describe('MongoTenant', function() {
);
});
});

it('should not be able to delete across tenants', (done) => {
const TestModel = utils.createTestModel({
test: {
type: String,
required: true,
trim: true
}});

const ModelClassT1 = TestModel.byTenant('tenant1');
const ModelClassT2 = TestModel.byTenant('tenant2');

const t1Instance = new ModelClassT1({ test: 't1Instance' });
const t2Instance = new ModelClassT2({ test: 't2Instance' });

t1Instance.save((err1) => {
assert(!err1, 'save t1 instance should work');
t2Instance.save((err2)=> {
assert(!err2, 'save t2 instance should work');
ModelClassT2.deleteOne({ _id: t1Instance._id}, (err) => {
assert(!err, 'error should occour'); // I guess it's fine that no error occours. that is just mongo behaviour
// however the document should not be deleted, since ModelClassT2 should have no access to elements of tenant1
ModelClassT1.findOne(t1Instance._id, (err, modelInst) => {
assert(modelInst, 'modelInstance should still be available, since it should not be able to delete across tenants');
done();
});
});
});
});
});

it('should bind Model.deleteOne(conditions, cb) to correct tenant context.', function(done) {
let TestModel = utils.createTestModel({});

TestModel.create({tenantId: 'tenant1'}, {tenantId: 'tenant2'}, (err) => {
assert(!err, 'Expected creation of 2 test entities to work.');

TestModel.byTenant('tenant1').deleteOne({tenantId: 'tenant2'}, (deletionError) => {
assert(!deletionError, 'Expected Model.deleteMany() to work');

TestModel.find({}, (lookupErr, entities) => {
assert(!lookupErr, 'Expected Model.find() to work.');
assert.equal(entities.length, 1, 'Expected to find only one entity.');
assert.equal(entities[0].tenantId, 'tenant2', 'Expected tenant2 scope on entity.');

done();
});
});
});
});

it('should bind Model.deleteMany(conditions, options, cb) to correct tenant context.', function(done) {
let TestModel = utils.createTestModel({num: Number});

TestModel.create({tenantId: 'tenant1', num: 1}, {tenantId: 'tenant1', num: 1}, {tenantId: 'tenant2', num: 1}, (err) => {
assert(!err, 'Expected creation of 3 test entities to work.');

TestModel.byTenant('tenant1').deleteMany({num: 1}, (deletionError) => {
assert(!deletionError, 'Expected Model.deleteMany() to work');

TestModel.find({}, (lookupErr, entities) => {
assert(!lookupErr, 'Expected Model.find() to work.');
assert.equal(entities.length, 1, 'Expected to find only one entity.');
assert.equal(entities[0].tenantId, 'tenant2', 'Expected tenant2 scope on entity.');

done();
});
});
});
});
});
});
Loading

0 comments on commit e1b10ca

Please sign in to comment.