Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Shared model definitions #63

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
97fdb78
feat(shared model defs): Implemented sharing model definitions among …
ThisIsNoZaku Sep 28, 2018
6e6f4b5
feat(shared model defs): Don't override model connection.
Damienmarble Feb 14, 2019
7680992
feat(shared model defs): Prevent cross-connection definition pollution.
Damienmarble Feb 14, 2019
1faf3f6
feat(shared model defs): Prevent overriding existing properties.
Damienmarble Feb 15, 2019
382bf5b
Merge branch 'fix/migration_scope_error' into feature/shared_model_de…
Damienmarble Feb 15, 2019
b1213a0
feat(shared model defs): Added test for model sharing.
Damienmarble Feb 15, 2019
a6e2ea0
feat(shared model defs): Updated .travis.yml
Damienmarble Feb 15, 2019
844b917
fix(migration scope error): Added feature documentation.
Damienmarble Feb 15, 2019
90b168d
fix(migration scope error): Ensure default connection goes first.
Damienmarble Feb 15, 2019
fa0f500
fix(migration scope error): Fixed an error caused by scope change.
Damienmarble Feb 15, 2019
7131345
fix(migration scope error): Documentation for correct association.
Damienmarble Feb 15, 2019
280a627
fix(migration scope error): Always set the connection property.
Damienmarble Feb 15, 2019
6c26dc6
Custom version.
Damienmarble Feb 19, 2019
5970a0c
feat(shared model defs): Renamed multitenant to shared-models
Damienmarble Feb 20, 2019
887c1eb
feat(shared model defs): Install fixtures in new test dir
Damienmarble Mar 21, 2019
0339283
feat(shared model defs): Missed package-lock.json
Damienmarble Mar 21, 2019
2da50be
feat(shared model defs): Undo erroneously committed local changes
Damienmarble Mar 21, 2019
b62488e
feat(shared model defs): Added back previous version tests
Damienmarble Mar 21, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ addons:
postgresql: "9.6"
before_install:
- psql -c 'create database sequelize;' -U postgres
- psql -c 'create database sequelize2;' -U postgres
- npm install -g [email protected]
script:
- 'npm test'
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,34 @@ module.exports = {
};
```

## Sharing models among connections

If you set the sequelize configuration flag `shareModelsAmongConnections` to true, any sails model definitions
which do not explicitly specify a connection will be used with all configured datasources.

If sails models are configured to be exposed globally, you may access the Model for a particular connection
by via `Model.connection_name` or `Model['connection_name']`. Just `Model` will access the model for the default
datasource normally.

When creating associations, use the model definition object passed into `associations` to make sure you use
the model for the right connections.

For example, instead of:

```
associations() {
User.belongsTo(Group);
}
```

Do something like:

```
associations(modelDef) {
User[modelDef.connection].belongsTo(Group[modelDef.connection]);
}
```

# Contributors
This project was originally created by Gergely Munkácsy (@festo).
Now is maintained by Konstantin Burkalev (@KSDaemon).
Expand Down
100 changes: 69 additions & 31 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,64 +138,102 @@ module.exports = sails => {
return connections;
},

initModels () {
initModels() {
if (typeof (sails.models) === 'undefined') {
sails.models = {};
}
},

defineModels (models, connections) {
let modelDef, modelName, modelClass, cm, im, connectionName;
let modelDef, modelName, modelClass, cm, im, connectionName, connection;
const sequelizeMajVersion = parseInt(Sequelize.version.split('.')[0], 10);

// Try to read settings from old Sails then from the new.
// 0.12: sails.config.models.connection
// 1.00: sails.config.models.datastore
const defaultConnection = sails.config.models.connection || sails.config.models.datastore || 'default';

for (modelName in models) {
modelDef = models[modelName];
const initialModels = Object.assign({}, models);

// Skip models without options provided (possible Waterline models)
if (!modelDef.options) {
continue;
Object.keys(connections).sort((connA, connB) => {
if (connA === defaultConnection) {
return -1;
} else if (connB === defaultConnection) {
return 1;
} else {
return 0;
}
}).forEach(function(connection){
const connectionModels = {};

sails.log.verbose('Loading Sequelize model \'' + modelDef.globalId + '\'');
connectionName = modelDef.connection || modelDef.datastore || defaultConnection;
modelClass = connections[connectionName].define(modelDef.globalId, modelDef.attributes, modelDef.options);
for (modelName in initialModels) {
modelDef = Object.assign({}, initialModels[modelName]);

if (sequelizeMajVersion >= 4) {
for (cm in modelDef.options.classMethods) {
modelClass[cm] = modelDef.options.classMethods[cm];
// Skip models without options provided (possible Waterline models)
if (!modelDef.options) {
continue;
}

for (im in modelDef.options.instanceMethods) {
modelClass.prototype[im] = modelDef.options.instanceMethods[im];
if (sails.config[this.configKey].shareModelsAmongConnections && !modelDef.connection) {
modelDef.connection = connection;

sails.log.verbose('Loading Sequelize model \'' + modelDef.globalId + '\' for connection \'' + connection +'\'');
} else {
sails.log.verbose('Loading Sequelize model \'' + modelDef.globalId + '\'');
}
}

if (sails.config.globals.models) {
sails.log.verbose('Exposing model \'' + modelDef.globalId + '\' globally');
global[modelDef.globalId] = modelClass;
}
sails.models[modelDef.globalId.toLowerCase()] = modelClass;
}
connectionName = modelDef.connection || modelDef.datastore || defaultConnection;
if (connectionName !== connection) {
continue;
}
modelClass = connections[connectionName].define(modelDef.globalId, modelDef.attributes, modelDef.options);

for (modelName in models) {
modelDef = models[modelName];
if (sequelizeMajVersion >= 4) {
for (cm in modelDef.options.classMethods) {
modelClass[cm] = modelDef.options.classMethods[cm];
}

// Skip models without options provided (possible Waterline models)
if (!modelDef.options) {
continue;
for (im in modelDef.options.instanceMethods) {
modelClass.prototype[im] = modelDef.options.instanceMethods[im];
}
}

if (sails.config.globals.models) {
sails.log.verbose('Exposing model \'' + modelDef.globalId + '\' globally');
if (sails.config[this.configKey].shareModelsAmongConnections) {
if (connection === defaultConnection) {
global[modelDef.globalId] = modelClass;
global[modelDef.globalId][defaultConnection] = modelClass;
} else {
if (global[modelDef.globalId][connection]) {
throw new Error(`Attaching a namespaced model for connection ${connection} would overwrite an existing property on ${modelDef.globalId}. For safety, this is not allowed.\n` +
'To avoid this, please rename your connection.');
}
global[modelDef.globalId][connection] = modelClass;
}
} else {
global[modelDef.globalId] = modelClass;
}
}
sails.models[modelDef.globalId.toLowerCase()] = modelClass;
connectionModels[modelName] = modelDef;
}
for (modelName in connectionModels) {
modelDef = connectionModels[modelName];

this.setAssociation(modelDef);
this.setDefaultScope(modelDef, sails.models[modelDef.globalId.toLowerCase()]);
}
// Skip models without options provided (possible Waterline models)
if (!modelDef.options) {
continue;
}

this.setAssociation(modelDef);
this.setDefaultScope(modelDef, sails.models[modelDef.globalId.toLowerCase()]);
}
}.bind(this));
},

setAssociation (modelDef) {

if (modelDef.associations !== null) {
sails.log.verbose('Loading associations for \'' + modelDef.globalId + '\'');
if (typeof modelDef.associations === 'function') {
Expand Down Expand Up @@ -250,7 +288,7 @@ module.exports = sails => {

// Skip waterline connections
if (connectionDescription.adapter) {
continue;
return;
}

sails.log.verbose('Migrating schema in \'' + connectionName + '\' connection');
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sails-hook-sequelize",
"version": "1.2.0",
"version": "1.2.0-r1",
"description": "Sails.js hook to use sequelize ORM",
"main": "index.js",
"sails": {
Expand All @@ -11,7 +11,7 @@
"lint:fix": "node ./node_modules/.bin/eslint --fix index.js test/*.js test/unit/*.js",
"test": "node ./node_modules/.bin/mocha --exit test/overall.test.js",
"cover": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha --exit test/overall.test.js",
"pretest": "cd ./test/fixtures/v0.11-app && npm i --prefix ./ && cd - && cd ./test/fixtures/v0.12-sequelize-waterline-app && npm i --prefix ./ && cd - && cd ./test/fixtures/v1.0-app && npm i --prefix ./ && cd -",
"pretest": "cd ./test/fixtures/v0.11-app && npm i --prefix ./ && cd - && cd ./test/fixtures/v0.12-sequelize-waterline-app && npm i --prefix ./ && cd - && cd ./test/fixtures/v1.0-app && npm i --prefix ./ && cd - && cd ./test/fixtures/v1.0-app-shared-models && npm i --prefix ./ && cd -",
"posttest": "git checkout -- ./test/fixtures/v0.12-sqlite3-app/db/sequelize.sqlite && git checkout -- ./test/fixtures/v0.12-sequelize-waterline-app/db/sequelize.sqlite"
},
"keywords": [
Expand Down
42 changes: 42 additions & 0 deletions test/bootstrap.v1.0.modelsharing.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
describe('Sails.js v1.0 Sequelize hook shared models tests', () => {

let Sails, rc, sails;

// Before running any tests, attempt to lift Sails
before(function (done) {

// Hook will timeout in 10 seconds
this.timeout(11000);

Sails = require('./fixtures/v1.0-app-shared-models/app').sails;
rc = require('rc');

const config = rc('sails');
config.hooks.sequelize = require('../index');

// Attempt to lift sails
Sails().lift(config, (err, _sails) => {
if (err) { return done(err); }
sails = _sails;
return done(err, sails);
});
});

// Test that Sails can lift with the hook in place
it('sails does not crash', () => true);

require('./unit/create.test');
require('./unit/associations.test');
require('./unit/scope.test');
require('./unit/model-sharing.test.js');

after(done => {
sails.lower(err => {
if (err) {
return console.log('Error occurred lowering Sails app: ', err);
}
console.log('Sails app lowered successfully!');
done();
});
});
});
132 changes: 132 additions & 0 deletions test/fixtures/v1.0-app-shared-models/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
################################################
# ┌─┐┬┌┬┐╦╔═╗╔╗╔╔═╗╦═╗╔═╗
# │ ┬│ │ ║║ ╦║║║║ ║╠╦╝║╣
# o└─┘┴ ┴ ╩╚═╝╝╚╝╚═╝╩╚═╚═╝
#
# > Files to exclude from your app's repo.
#
# This file (`.gitignore`) is only relevant if
# you are using git.
#
# It exists to signify to git that certain files
# and/or directories should be ignored for the
# purposes of version control.
#
# This keeps tmp files and sensitive credentials
# from being uploaded to your repository. And
# it allows you to configure your app for your
# machine without accidentally committing settings
# which will smash the local settings of other
# developers on your team.
#
# Some reasonable defaults are included below,
# but, of course, you should modify/extend/prune
# to fit your needs!
#
################################################




################################################
# Local Configuration
#
# Explicitly ignore files which contain:
#
# 1. Sensitive information you'd rather not push to
# your git repository.
# e.g., your personal API keys or passwords.
#
# 2. Environment-specific configuration
# Basically, anything that would be annoying
# to have to change every time you do a
# `git pull`
# e.g., your local development database, or
# the S3 bucket you're using for file uploads
# development.
#
################################################

config/local.js





################################################
# Dependencies
#
# Most of the time, the node_modules folder is
# excluded from your code repository.
#
# When releasing a production app, you might
# consider including your node_modules directory
# in your git repo, but during development, it
# is always best to exclude it, since different
# developers may be working on different kernels,
# where dependencies would need to be recompiled
# anyway.
#
# More on that here about node_modules dir:
# http://www.futurealoof.com/posts/nodemodules-in-git.html
# (credit Mikeal Rogers, @mikeal)
#
# > Do you use bower?
# > re: the bower_components dir, see this:
# > http://addyosmani.com/blog/checking-in-front-end-dependencies/
# > (credit Addy Osmani, @addyosmani)
#
################################################

node_modules




################################################
# Sails.js / Waterline / Grunt
#
# Files generated by Sails and Grunt, or related
# tasks and adapters.
################################################

.tmp





################################################
# Node.js / NPM
#
# Common files generated by Node, NPM, and the
# related ecosystem.
################################################

lib-cov
*.seed
*.log
*.out
*.pid
npm-debug.log





################################################
# Miscellaneous
#
# Common files generated by text editors,
# operating systems, file systems, dbs, etc.
################################################

*~
*#
.DS_STORE
.netbeans
nbproject
.idea
.node_history
dump.rdb

Loading