sails-hook-orm
Version:
The ORM hook from Sails core.
633 lines (502 loc) • 29.2 kB
JavaScript
/**
* Module dependencies
*/
var util = require('util');
var _ = require('@sailshq/lodash');
var flaverr = require('flaverr');
var async = require('async');
var prompt = require('prompt');
var chalk = require('chalk');
var loadModelsAndCustomAdapters = require('./load-models-and-custom-adapters');
var validateDatastoreConfig = require('./validate-datastore-config');
var validateModelDef = require('./validate-model-def');
var buildOntologyAndRunAutoMigrations = require('./build-ontology-and-run-auto-migrations');
var loadAdapterFromAppDependencies = require('./load-adapter-from-app-dependencies');
var buildRegisteredDatastoreInstance = require('./build-registered-datastore-instance');
var checkAdapterCompatibility = require('./check-adapter-compatibility');
/**
* `initialize()`
*
* Initialize this hook.
*
* @required {Dictionary} hook
* @required {SailsApp} sails
* @required {Function} done
*/
module.exports = function initialize(hook, sails, done){
////////////////////////////////////////////////////////////////////////////
// NOTE: If a user hook needs to add or modify model definitions,
// the hook should wait until `hook:orm:loaded`, then reload the original
// model modules `orm/loadAppModules`. Finally, the ORM should be flushed using
// `reload()` below.
////////////////////////////////////////////////////////////////////////////
// Now do a number of things, some of them in parallel.
async.auto({
// Load model and adapter definitions which are defined in the project.
// (this is from e.g. `api/models/` and `api/adapters/`--
// note that this does NOT include adapters which need to be loaded from the node_modules directory!)
_loadModelsAndCustomAdapters: function (next) {
loadModelsAndCustomAdapters(hook, sails, next);
},
// Warning! This feature is undocumented/experimental and may change at any time!
_mergeInProgrammaticModuleDefs: ['_loadModelsAndCustomAdapters',function (unused, next) {
_.extend(hook.models, sails.config.orm.moduleDefinitions.models);
_.extend(hook.adapters, sails.config.orm.moduleDefinitions.adapters);
return next();
}],
// Get an array of datastore identities.
//
// Note that we do not attempt to validate/normalize model defs or datastore configs here.
// If model defs or datastore configs cannot be parsed, we simply ignore them.
_determineRelevantDatastoreNames: ['_mergeInProgrammaticModuleDefs', function (unused, next){
var relevantDatastoreNames = [];
try {
_.each(sails.config.datastores, function _eachDatastoreConfig(datastoreConfig, datastoreName) {
// If the datastore config is even remotely valid, then fail with a fatal error.
// (note that this will also be more thoroughly checked later)
if (!_.isObject(datastoreConfig) || (!_.isString(datastoreConfig.adapter) && !_.isObject(datastoreConfig.adapter))) {
throw flaverr({ name: 'userError', code: 'E_INVALID_DATASTORE_CONFIG' }, new Error('Invalid configuration for datastore `'+datastoreName+'`:\n'+util.inspect(datastoreConfig, {depth:5})+''));
}
// Now, if we made it here, then we know this datastore is relevant.
relevantDatastoreNames.push(datastoreName);
});//</_.each>
} catch (e) {
return next(e);
}
// Always put "default" last. This way when loading adapters, we can have it use
// the 'sails-disk' adapter declared in another datastore (if any) instead of loading
// the default one.
relevantDatastoreNames = _.without(relevantDatastoreNames, 'default').concat(['default']);
return next(undefined, relevantDatastoreNames);
}],
// ╦ ╔═╗╔═╗╔╦╗ ╔═╗╔╦╗╔═╗╔═╗╔╦╗╔═╗╦═╗┌─┐
// ║ ║ ║╠═╣ ║║ ╠═╣ ║║╠═╣╠═╝ ║ ║╣ ╠╦╝└─┐
// ╩═╝╚═╝╩ ╩═╩╝ ╩ ╩═╩╝╩ ╩╩ ╩ ╚═╝╩╚═└─┘
// ┌─ ┌─┐┬─┐┌─┐┌┬┐ ┌─┐┌─┐┌─┐ ┌┐┌┌─┐┌┬┐┌─┐ ┌┬┐┌─┐┌┬┐┬ ┬┬ ┌─┐┌─┐ ┌─┐┌─┐┬ ┌┬┐┌─┐┬─┐ ─┐
// │───├┤ ├┬┘│ ││││ ├─┤├─┘├─┘ ││││ │ ││├┤ ││││ │ │││ ││ ├┤ └─┐ ├┤ │ ││ ││├┤ ├┬┘───│
// └─ └ ┴└─└─┘┴ ┴ ┴ ┴┴ ┴ ┘└┘└─┘─┴┘└─┘────┴ ┴└─┘─┴┘└─┘┴─┘└─┘└─┘ └ └─┘┴─┘─┴┘└─┘┴└─ ─┘
//
// For every valid datastore config which is relevant (i.e. referenced by at least one model, or app-wide defaults, or with `forceLoadAdapter` set)
// ensure its referenced adapter is loaded. Note that we do not attempt to validate/normalize stuff here-- the goal is just to ensure we
// have the referenced adapters.
//
// If we find a not-yet-loaded adapter being referenced from an in-use datastore, then attempt to require it from the `node_modules/`
// directory of this Sails application.
_attemptToLoadUnrecognizedAdapters: ['_determineRelevantDatastoreNames', function (aaData, next){
try {
_.each(aaData._determineRelevantDatastoreNames, function _eachRelevantDatastoreName(datastoreName) {
// Now, if we made it here, then we're ready to take a look at this datastore config and check up on its adapter.
var datastoreConfig = sails.config.datastores[datastoreName];
// Check if the referenced adapter has aready been loaded one way or another.
if (_.isObject(datastoreConfig) && _.isString(datastoreConfig.adapter)) {
var referencedAdapter = hook.adapters[datastoreConfig.adapter];
// If it hasn't...
if (!referencedAdapter) {
try {
// Otherwise, we'll try and load it as a dependency from the app's `node_modules/` folder,
// and also validate and normalize it.
hook.adapters[datastoreConfig.adapter] = loadAdapterFromAppDependencies(datastoreConfig.adapter, datastoreName, sails);
} catch (e) {
// Special case -- the default adapter will load the sails-disk bundled with sails-hook-orm if it's not installed locally.
if (datastoreName === 'default' && datastoreConfig.adapter === 'sails-disk' && e.code === 'E_ADAPTER_NOT_INSTALLED') {
hook.adapters[datastoreConfig.adapter] = require('sails-disk');
}
else {
throw e;
}
}
}//</ if the adapter string references an adapter that we haven't loaded yet >-
}//</ if datastore config is an object and its `adapter` prop is a string >-
});//</_.each() :: each relevant datastore identity>
} catch (e) { return next(e); }
return next();
}],
// Validate and normalize datastore configurations.
_validateDatastoreConfigsAndBuildAccessors: ['_attemptToLoadUnrecognizedAdapters', function (aaData, next){
try {
// Loop over relevant datastore configs and validate/normalize the raw config of each one,
// saving the normalized config on `hook.normalizedDSConfigs` for use in subsequent steps.
hook.normalizedDSConfigs = {};
_.each(aaData._determineRelevantDatastoreNames, function _eachRelevantDatastoreName(datastoreName) {
hook.normalizedDSConfigs[datastoreName] = validateDatastoreConfig(datastoreName, hook, sails);
});//</_.each() datastore name>
} catch (e) { return next(e); }
return next();
}],
_checkForGlobalMongoSettings: ['_validateDatastoreConfigsAndBuildAccessors', function (aaData, next) {
try {
// Get the default adapter.
var defaultAdapterIdentity = hook.normalizedDSConfigs.default.adapter;
// Get the default primary key attribute name.
var defaultPrimaryKey = sails.config.models.primaryKey;
// Get the default primary key attribute def (if any)
var defaultPrimaryKeyAttr = sails.config.models.attributes[defaultPrimaryKey];
// If no attribute exists, we're done (for now). Later on, each model will be checked
// individually to ensure that if it's using Mongo, it has a correctly-configured PK attribute.
// So no reason to get our panties all in a bunch about it just yet.
if (!defaultPrimaryKeyAttr) {
throw flaverr('E_NO_ATTR_DEF_FOR_DEFAULT_PKA', new Error('This model does not have an attribute for the default primary key. That does not mean it is necessarily broken (it might have a custom PKA -- we just haven\'t bothered to look into it further yet, since we\'ll be checking again momentarily anyway.'));
}
if (defaultAdapterIdentity === 'sails-mongo' && (defaultPrimaryKeyAttr.autoIncrement || (defaultPrimaryKeyAttr.type !== 'string' && sails.config.models.dontUseObjectIds !== true) || defaultPrimaryKeyAttr.columnName !== '_id')) {
sails.log.debug('It looks like the default datastore for this app is `sails-mongo`,');
sails.log.debug('but the default primary key attribute (`' + defaultPrimaryKey + '`) is not set up correctly.' );
sails.log.debug('When using `sails-mongo`, primary keys MUST have `columnName: \'_id\'`,');
sails.log.debug('and must _not_ have `autoIncrement: true`.');
sails.log.debug('Also, if `dontUseObjectIds` is not set to `true` for the model,');
sails.log.debug('then the `type` of the primary key must be `string`.');
sails.log.debug();
sails.log.debug('We\'ll set this up for you this time...');
sails.log.debug();
delete defaultPrimaryKeyAttr.autoIncrement;
defaultPrimaryKeyAttr.type = 'string';
defaultPrimaryKeyAttr.columnName = '_id';
}
} catch (e) {
switch(e.code) {
case 'E_NO_ATTR_DEF_FOR_DEFAULT_PKA': return next();
default: return next(e);
}
}//>-•
return next();
}],
// Normalize model definitions and merge in defaults from `sails.config.models.*`.
// This is what will be passed in to Waterline when building the ontology.
_normalizeModelDefs: ['_validateDatastoreConfigsAndBuildAccessors', function (unused, next) {
try {
_.each(_.keys(hook.models), function (identity) {
var normalizedModelDef = validateModelDef(hook.models[identity], identity, hook, sails);
// Note: prior to March 2016, the normalized def was merged back into
// the original model def (`hook.models[identity]`) rather than replacing it.
hook.models[identity] = normalizedModelDef;
// Ensure that the identity that is set in sails.hook.models is equal to the
// model's identity so that you never end up with a global model id that is
// different from the model identity.
if (identity.toLowerCase() !== normalizedModelDef.identity) {
throw new Error('A model was found that has different values for it\'s globalId and it\'s model identity. You should never manually set these values, they will be inferred for you.');
}
});
} catch (e) {
return next(e);
}
return next();
}],
// ╔═╗╦═╗╔═╗╔╦╗╦ ╦╔═╗╔╦╗╦╔═╗╔╗╔ ╔═╗╦ ╦╔═╗╔═╗╦╔═
// ╠═╝╠╦╝║ ║ ║║║ ║║ ║ ║║ ║║║║ ║ ╠═╣║╣ ║ ╠╩╗
// ╩ ╩╚═╚═╝═╩╝╚═╝╚═╝ ╩ ╩╚═╝╝╚╝ ╚═╝╩ ╩╚═╝╚═╝╩ ╩
// ┌─ ┬ ┬┌─┐┬─┐┌┐┌┬┌┐┌┌─┐┌─┐ ─┐
// │───│││├─┤├┬┘││││││││ ┬└─┐ ───│
// └─ └┴┘┴ ┴┴└─┘└┘┴┘└┘└─┘└─┘ ─┘
//
// If NODE_ENV is "production", check if any models are using
// a datastore running on `sails-disk`. If so, show a warning.
_productionCheck: ['_normalizeModelDefs', function (unused, next) {
try {
// We use `process.env.NODE_ENV` instead of `sails.config.environment`
// to allow for the environment to be set to e.g. "staging" while the
// NODE_ENV is set to "production".
if (process.env.NODE_ENV === 'production') {
// > **Remember:**
// > In a production environment, regardless of your logical `environment`
// > config, the NODE_ENV environment variable should be set. Setting
// > `sails.config.environment` to production does this automatically.
// e.g. ['default', 'foobar']
var datastoresUsingSailsDisk = _.reduce(sails.config.datastores, function(memo, datastoreConf, identity){
if (datastoreConf.adapter === 'sails-disk') {
memo.push(identity);
}
return memo;
}, []);
// e.g. ['user', 'product']
var modelsUsingSailsDisk = _.reduce(hook.models, function(memo, normalizedModelDef, identity){
// Look up the referenced datastore for this model, and then check to see if
// it matches any of the datastores using the sails-disk adapter.
var referencedDatastore = normalizedModelDef.datastore;
if (_.contains(datastoresUsingSailsDisk, referencedDatastore)) {
memo.push(identity);
}
return memo;
}, []);
if (modelsUsingSailsDisk.length > 0) {
sails.log.warn('The default `sails-disk` adapter is not designed for use as a production database.');
sails.log.warn('Instead, please use another adapter like sails-postgresql or sails-mongo.');
sails.log.warn('For more info, see: http://sailsjs.com/docs/concepts/deployment');
sails.log.warn('To hide this warning message, enable `sails.config.orm.skipProductionWarnings`.');
sails.log.warn(' [?] If you\'re unsure, see https://sailsjs.com/support');
}
}//>-
} catch (e) { return next(e); }
// Otherwise it worked!
return next();
}],
// Before continuing any further to actually start up the ORM,
// check the migrate settings for each model to prompt the user
// to make a decision if no migrate configuration is present.
//
// Note that, if this is a production environment, the `migrate`
// setting will always be forced to "safe" in Waterline.
_doubleCheckMigration: ['_productionCheck', function (unused, next) {
// If there are no models, we're good.
if (_.keys(hook.models).length === 0) {
return next();
}
// If a project-wide migrate setting (sails.config.models.migrate) is defined, we're good.
if (!_.isUndefined(sails.config.models.migrate)) {
return next();
}
// If this is a production NODE_ENV, show a slightly different message and skip the prompt.
if (process.env.NODE_ENV === 'production') {
console.log('');
sails.log.info('A project-wide `sails.config.models.migrate` setting has not been configured for this app.');
sails.log.info('Since the NODE_ENV env variable is set to "production", auto-migration will be disabled automatically.');
sails.log.info('(i.e. `migrate: \'safe\'`)');
return next();
}
// Otherwise show a prompt
console.log('--------------------------------------------------------------------------------');
// console.log();
prompt.start();
console.log(
' Excuse my interruption, but it looks like this app\n'+
' does not have a "migrate" setting configured yet.\n'+
' (perhaps this is the first time you\'re lifting it with models?)\n'+
''
// '\n'+
// ' In short, this setting controls whether/how Sails attempts to\n'+
// ' automatically rebuild your database(s) every time you lift.\n'+
// ' You can read more about that here:\n'+
// ' '+chalk.underline('sailsjs.com/docs/concepts/models-and-orm/model-settings#?migrate')+'\n'
// // ' command(⌘)+click to open links in the terminal'
);
// console.log();
console.log(chalk.gray(' Tired of seeing this prompt? Edit '+chalk.bold('config/models.js')+'.'));
// console.log(chalk.gray(' Or for a one-time override: '+chalk.bold('sails lift --models.migrate=\'alter\'')+''));
// console.log();
// console.log(chalk.gray(
// ' In short, this setting controls whether/how Sails attempts to\n'+
// ' automatically rebuild your database(s) every time you lift.\n'+
// ' You can read more about that here:\n'+
// ' '+chalk.underline('sailsjs.com/docs/concepts/models-and-orm/model-settings#?migrate')
// // ' command(⌘)+click to open links in the terminal'
// ));
console.log();
console.log(
' In a production environment (NODE_ENV=production) Sails always uses'+'\n'+
' migrate:\'safe\' to protect against inadvertent deletion of your data.\n'+
' But '+chalk.bold('during development')+', you have a few different options:\n'+
'\n'+
' 1. FOR DEV: '+chalk.bold.cyan('alter')+chalk.reset(' wipe/drop and try to re-insert ALL my data ')+chalk.bold('(recommended)')+'\n'+
' 2. FOR TESTS: '+chalk.bold.yellow('drop')+chalk.reset(' wipe/drop ALL my data every time I lift Sails\n')+
' 3. FOR STAGING: '+chalk.bold.red('safe')+chalk.reset(' don\'t auto-migrate my data. I will do it myself\n')
);
// console.log('What would you like Sails to do '+chalk.bold('this time')+'?');
console.log(chalk.gray(' Read more: '+chalk.underline('sailsjs.com/docs/concepts/models-and-orm/model-settings#?migrate')));
console.log('--------------------------------------------------------------------------------');
console.log();
console.log('What would you like Sails to do '+chalk.bold('this time')+'?');
console.log(chalk.gray(' ** NEVER CHOOSE "alter" or "drop" IF YOU ARE WORKING WITH PRODUCTION DATA **'));
console.log();
// console.log();
prompt.get(['?'], function(err, result) {
if (err) {
console.log('_.keys(err)',_.keys(err));
console.log(chalk.bgRed.white.bold('<canceled, probably with CTRL+C>'));
console.log('--------------------------------------------------------------------------------');
return next(err);
}//-•
result = result['?'];
switch (result) {
case 'alter':
case '1':
sails.config.models.migrate = 'alter';
break;
case 'drop':
case '2':
sails.config.models.migrate = 'drop';
break;
default:
sails.config.models.migrate = 'safe';
break;
}
console.log('--------------------------------------------------------------------------------');
// console.log();
console.log(
' OK! Temporarily using '+chalk.bold('migrate:\''+sails.config.models.migrate+'\'')+'...'
);
// console.log(' (press CTRL+C to cancel-- continuing automatically in 0.5 seconds...)');
console.log(chalk.gray(' To skip this prompt in the future, edit '+chalk.bold('config/models.js')+'.'));
console.log('--------------------------------------------------------------------------------');
console.log();
setTimeout(function (){
return next();
},300);
});
}],
// Verify that this adapter is compatible w/ this version of Sails / Waterline.
// (if not, go ahead and throw)
_checkAdapterCompatibility: ['_doubleCheckMigration', function (aaData, next) {
_.each(aaData._determineRelevantDatastoreNames, function _eachRelevantDatastoreName(datastoreName) {
// Look up the normalized config for this datastore.
var normalizedDatastoreConfig = hook.normalizedDSConfigs[datastoreName];
// Find the adapter used by this datastore
var adapter = hook.adapters[normalizedDatastoreConfig.adapter];
checkAdapterCompatibility(datastoreName, adapter);
});
return next();
}],
// Once all user model and adapter definitions are loaded
// and normalized, go ahead and initialize the ORM.
_buildOntology: ['_checkAdapterCompatibility', function (aaData, next) {
// If `sails` is already exiting due to previous errors, bail out.
if (sails._exiting) {
// This is possible since we are doing asynchronous things in the initialize function,
// and e.g. another hook may have failed to load in the mean time since we began initializing.
// Also note that `reload()` below calls initialize again, so this could happen during that
// process as well.
return next(flaverr('E_SAILS_IS_ALREADY_EXITING', new Error('SAILS EXITING')));
}
if (sails.config.models.migrate === 'alter') {
sails.log.info(chalk.cyan.bold('·• ')+chalk.bold('Auto-migrating...')+chalk.reset(' (alter)'));
sails.log.info(' '+chalk.gray('Hold tight, this could take a moment.'));
// sails.log.info(' '+chalk.gray('(Please don\'t press CTRL+C until this is finished.)'));
}
else if (sails.config.models.migrate === 'drop') {
sails.log.info(chalk.yellow.bold('·• ')+chalk.bold('Auto-migrating...')+chalk.reset(' (drop)'));
// sails.log.info(' '+chalk.gray('(Please don\'t press CTRL+C until this is finished.)'));
}
else {}
buildOntologyAndRunAutoMigrations(hook, sails, function (err, freshOntology) {
if (err) { return next(err); }
if (sails.config.models.migrate === 'alter') {
sails.log.info(' ✓ Auto-migration complete.');
sails.log.blank();
}
else if (sails.config.models.migrate === 'drop') {
sails.log.info(' ✓ Auto-migration complete.');
sails.log.blank();
}
// Finally, continue onward, passing the ontology through for use below.
return next(undefined, freshOntology);
});
}],
// Expose `hook.datastores`; a dictionary indexed by datastore identity.
// We only build datastore dictionaries for datastore configs that are in use by one or more models
// (or have the `forceLoadAdapter` setting enabled).
_buildRDIs: ['_buildOntology', function (aaData, next){
try {
// Start building `hook.datastores`
hook.datastores = {};
// Loop over relevant datastore configs.
_.each(aaData._determineRelevantDatastoreNames, function _eachRelevantDatastoreName(datastoreName) {
// Look up the normalized config for this datastore.
var normalizedDatastoreConfig = hook.normalizedDSConfigs[datastoreName];
// Find the adapter used by this datastore
var adapter = hook.adapters[normalizedDatastoreConfig.adapter];
// Build our registered datastore instance (s.k.a. "rdi") - a dictionary
// with public methods as well as other important state. Then expose
// our new rdi on `hook.datastores`.
hook.datastores[datastoreName] = buildRegisteredDatastoreInstance(datastoreName, normalizedDatastoreConfig, adapter);
});//</_.each() datastore name>
} catch (e) { return next(e); }
// ╔╦╗╔═╗╔╦╗╔═╗╔═╗╔╦╗╔═╗╦═╗╔═╗ ┌─┐┌─┐┌┬┐┌┬┐┌─┐┬─┐
// ║║╠═╣ ║ ╠═╣╚═╗ ║ ║ ║╠╦╝║╣ │ ┬├┤ │ │ ├┤ ├┬┘
// ═╩╝╩ ╩ ╩ ╩ ╩╚═╝ ╩ ╚═╝╩╚═╚═╝ └─┘└─┘ ┴ ┴ └─┘┴└─
// Attach a getter function to the hook for returning the correct datastore
// by name.
/**
* @param {String?} datastoreName
* defaults to "default"
*
* @returns {Dictionary}
* The registered datastore instance (RDI)
*/
hook.getDatastore = function getDatastore(datastoreName) {
// If no datastore name was specified, then assume the "default" datastore.
if (_.isUndefined(datastoreName)) {
datastoreName = 'default';
}//>-
var foundDatastore = hook.datastores[datastoreName];
if (!foundDatastore) {
throw new Error('Could not find a datastore by that name. Perhaps it hasn\'t been defined in the configuration?');
}//-•
// Return the registered datastore instance (RDI).
// This includes public methods, plus state like `manager`, as well as static
// metadata such as `config`, `driver`, and `name`.
return foundDatastore;
};
// Also expose this as sails.getDatastore().
sails.getDatastore = hook.getDatastore;
// And finally, go ahead and grab a (singleton) reference to the
// default datastore, and use it to expose a couple of the most
// common datastore methods directly on the `sails` instance,
// purely for convenience.
// (These methods will always use the default datastore.)
var defaultRDISingleton = hook.getDatastore();
sails.sendNativeQuery = defaultRDISingleton.sendNativeQuery;
sails.transaction = defaultRDISingleton.transaction;
// All done w/ this step.
return next();
}],
// Now take each of the "collection" instances returned by Waterline and modify them a bit for Sails.
// Then stuff them back onto `hook.models`.
_augmentAndExposeFinalModels: ['_buildRDIs', function (aaData, next){
try {
_.each(aaData._buildOntology.collections, function _eachInstantiatedModel(wlModel, modelIdentity) {
// Bind context (`this`) for models.
// (this allows `this` to be used in custom model methods)
// Skip `customToJSON` since `this` inside a record's `toJSON` should
// always refer to the record!
_.each(wlModel, function(prop, propName) {
if (_.isFunction(prop) && propName !== 'customToJSON') {
_.bind(prop, wlModel);
}
});
// Derive information about this model's associations from its schema
// and attach/expose the metadata as `SomeModel.associations` (an array)
wlModel.associations = _.reduce(wlModel.attributes, function _eachAttribute(memo, attrDef, attrName) {
// Skip non-associations.
if (!_.isObject(attrDef) || (!attrDef.model && !attrDef.collection)) {
return memo;
}
// Build an informational dictionary describing this association.
var assocInfo = { alias: attrName };
if (attrDef.model) {
assocInfo.type = 'model';
assocInfo.model = attrDef.model;
}
else if (attrDef.collection) {
assocInfo.type = 'collection';
assocInfo.collection = attrDef.collection;
if (attrDef.via) {
assocInfo.via = attrDef.via;
}
}
memo.push(assocInfo);
return memo;
}, []);
// Set `hook.models.*` reference to our instantiated model.
// Exposed as `hook.models[modelIdentity]`.
hook.models[modelIdentity] = wlModel;
// If configured to do so (based on `sails.config.globals.models`), then expose a reference
// to this model as a global variable (based on its `globalId`).
if (_.isObject(sails.config.globals) && sails.config.globals.models === true) {
// If this is an internal model introduced by Waterline (indicated by the `_private` flag)
// and not intended for userland access/manipulation, then don't globalize it.
// (e.g. this flag is attached to implicit junction models.)
if (hook.models[modelIdentity]._private) {
// Don't globalize.
}
// Otherwise use the `globalId` to determine what to globalize it as.
else if (_.isString(hook.models[modelIdentity].globalId) && hook.models[modelIdentity].globalId !== '') {
global[hook.models[modelIdentity].globalId] = wlModel;
}
// If there is no `globalId`, fall back to the identity.
else {
global[modelIdentity] = wlModel;
}
}
});//</each collection from Waterline>
} catch (e) { return next(e); }
return next();
}],
}, done);//</async.auto>
};