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

Update to support migrate per datastore #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
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
211 changes: 122 additions & 89 deletions lib/build-ontology-and-run-auto-migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,116 +24,149 @@ var WaterlineUtils = require('waterline-utils');
*/
module.exports = function buildOntologyAndRunAutoMigrations(hook, sails, done) {

// This variable is used below to hold a fresh ORM instance.
var orm;

try {

sails.log.silly('Starting ORM...');

// First, instantiate a fresh, empty Waterline ORM instance.
orm = Waterline();
// object to keep track of each datastore and all its models, etc
var datastores = {};

// Next, iterate through each normalized model definition and register it with a datastore specific
// Waterline instance
_.each(hook.models, function _loadEachModelDefIntoWaterline(model, identity) {
var datastore = model.datastore;
if (!datastores.hasOwnProperty(datastore)) {
datastores[datastore] = {
name: datastore,
models: [],
orm: Waterline(),
dsConfig: hook.normalizedDSConfigs[datastore],
migrate: hook.normalizedDSConfigs[datastore].migrate
};
}

// Next, iterate through each normalized model definition and register it with Waterline
// (using the `loadCollection()` method).
_.each(hook.models, function _loadEachModelDefIntoWaterline(normalizedModelDef, identity) {
// Create a Waterline "Collection" instance for each model, then register it w/ the ORM.
sails.log.silly('Registering model `%s` in Waterline', identity);
orm.registerModel(Waterline.Model.extend(normalizedModelDef));
sails.log.silly('Registering model `%s` in Waterline for `%s` datastore', identity, datastore);
datastores[datastore].models.push(model);
datastores[datastore].orm.registerModel(Waterline.Model.extend(model));
});

} catch (e) { return done(e); }


// Save a private reference to the Waterline `orm` instance.
hook._orm = orm;
// Save a private reference to the Waterline `datastores` array.
hook._datastores = datastores;

Object.keys(datastores).forEach(createMigration);

// Now, tell Waterline to initialize the ORM by calling its `.initialize()` method.
// This performs tasks like interpretating the physical-layer schema, validating associations,
// hooking up models to their datastores (fka "connections"), and performing auto-migrations.
orm.initialize({
function createMigration(datastoreName) {
var datastore = datastores[datastoreName];

// Pass in the app's known adapters.
adapters: hook.adapters,
// Now, tell Waterline to initialize the ORM by calling its `.initialize()` method.
// This performs tasks like interpretating the physical-layer schema, validating associations,
// hooking up models to their datastores (fka "connections"), and performing auto-migrations.
datastore.orm.initialize({

// We build and pass in a dictionary of normalized datastore configurations (fka connections)
// which _are actually in use_ (this is to avoid unnecessary work in Waterline).
datastores: hook.normalizedDSConfigs,
// e.g.
// ```
// { default: { schema: false, filePath: '.tmp/', adapter: 'sails-disk' } }
// ```
// Pass in the app's known adapters.
adapters: hook.adapters,

// `defaults` are a set of default properties for every model definition.
// They are defined in `sails.config.models`.
// Note that the ORM hook takes care of this to some degree, but we also pass them in here.
// This may change in future versions of Sails.
defaults: sails.config.models
// We build and pass in a dictionary of normalized datastore configurations (fka connections)
// which _are actually in use_ (this is to avoid unnecessary work in Waterline).
datastores: { [datastoreName]: datastore.dsConfig },
// e.g.
// ```
// { default: { schema: false, filePath: '.tmp/', adapter: 'sails-disk' } }
// ```

}, function _afterInitializingWaterline (err, freshOntology) {
if (err) { return done(err); }
// `defaults` are a set of default properties for every model definition.
// They are defined in `sails.config.models`.
// Note that the ORM hook takes care of this to some degree, but we also pass them in here.
// This may change in future versions of Sails.
defaults: sails.config.models

if (!_.isObject(freshOntology.collections) || _.isArray(freshOntology.collections) || _.isFunction(freshOntology.collections)) {
// Note that prior to March 2016, the second arg of the callback was used instead of relying on the existing `freshOntology` we already
// have instantiated above (however we've always _sent back_ the existing `freshOntology`-- we just used to use the second arg of the callback
// for the set of collections)
return done(new Error('Consistency violation: Expected `collections` property of ontology instance returned from Waterline to be a dictionary.\nInstead, here is what the ontology instance looks like:\n'+(util.inspect(freshOntology,{depth:null}))));
}
}, function _afterInitializingWaterline (err, freshOntology) {
if (err) { return done(err); }

try {

// Now that `waterline-schema` has validated that all of our associations are valid,
// loop through each model again to set the correct `columnType` for each singular association.
_.each(freshOntology.collections, function eachModel(WLModel) {

// Loop through the normalized model and set a column type for each attribute
_.each(WLModel.attributes, function eachAttribute(attrDef, attrName) {

// If this attribute is a plural association, or not an association at all, skip it.
if (!attrDef.model) { return; }

// Otherwise, this is a singular association.
//
// Find out the column type of the primary key attribute of the associated model
// so that we can use it as the column type of this singular association.
// (This is for the purpose of automigrations.)
var otherModelIdentity = attrDef.model;
var otherWLModel = hook.models[otherModelIdentity];
var associatedPKAttrName = otherWLModel.primaryKey;
var associatedPKAttrDef = otherWLModel.attributes[associatedPKAttrName];
var derivedColumnType = associatedPKAttrDef.autoMigrations.columnType;

// Set the column type, mutating our WLModel inline.
// > Also set the column type within the `schema`
attrDef.autoMigrations.columnType = derivedColumnType;
WLModel.schema[attrName].autoMigrations.columnType = derivedColumnType;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: remove the necessity for this last step by doing away with
// `schema` and having everything simply edit the WLModel inline.
// (^this depends on changes in Waterline core)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

});//</each attribute>

});//</each model>

} catch (e) { return done(e); }

// If we don't have a global `migrate` setting at this point, it's because we don't
// have any models (so the end user wasn't forced to choose a setting on lift).
// So we can just skip migrations and return.
if (!sails.config.models.migrate) {
return done(undefined, freshOntology);
if (!_.isObject(freshOntology.collections) || _.isArray(freshOntology.collections) || _.isFunction(freshOntology.collections)) {
// Note that prior to March 2016, the second arg of the callback was used instead of relying on the existing `freshOntology` we already
// have instantiated above (however we've always _sent back_ the existing `freshOntology`-- we just used to use the second arg of the callback
// for the set of collections)
return done(new Error('Consistency violation: Expected `collections` property of ontology instance returned from Waterline to be a dictionary.\nInstead, here is what the ontology instance looks like:\n'+(util.inspect(freshOntology,{depth:null}))));
}

try {

// Now that `waterline-schema` has validated that all of our associations are valid,
// loop through each model again to set the correct `columnType` for each singular association.
_.each(freshOntology.collections, function eachModel(WLModel) {

// Loop through the normalized model and set a column type for each attribute
_.each(WLModel.attributes, function eachAttribute(attrDef, attrName) {

// If this attribute is a plural association, or not an association at all, skip it.
if (!attrDef.model) { return; }

// Otherwise, this is a singular association.
//
// Find out the column type of the primary key attribute of the associated model
// so that we can use it as the column type of this singular association.
// (This is for the purpose of automigrations.)
var otherModelIdentity = attrDef.model;
var otherWLModel = hook.models[otherModelIdentity];
var associatedPKAttrName = otherWLModel.primaryKey;
var associatedPKAttrDef = otherWLModel.attributes[associatedPKAttrName];
var derivedColumnType = associatedPKAttrDef.autoMigrations.columnType;

// Set the column type, mutating our WLModel inline.
// > Also set the column type within the `schema`
attrDef.autoMigrations.columnType = derivedColumnType;
WLModel.schema[attrName].autoMigrations.columnType = derivedColumnType;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: remove the necessity for this last step by doing away with
// `schema` and having everything simply edit the WLModel inline.
// (^this depends on changes in Waterline core)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

});//</each attribute>

});//</each model>


} catch (e) { return done(e); }

datastore.freshOntology = freshOntology;

// If we don't have a global `migrate` setting at this point, it's because we don't
// have any models (so the end user wasn't forced to choose a setting on lift).
// So we can just skip migrations and return.
if (!sails.config.models.migrate) {
return done(undefined, freshOntology);
}

// Now that all relevant attributes have `autoMigrations` properties set, go ahead
// and perform any requested migrations.
});//</waterline.initialize()>
}
console.log('--------------------------------- migrating ------------------------------------')
var migrations = Object.keys(datastores).map(function(datastoreName) {
var datastore = datastores[datastoreName];
var migrate
if (datastore.migrate) {
migrate = datastore.migrate
console.log('Using migrate: ' + datastore.migrate + ' for datastore: ' + datastoreName)
}
else {
migrate = sails.config.models.migrate
console.log('Using sails.config.models.migrate: ' + migrate + ' for datastore: ' + datastoreName)

// Now that all relevant attributes have `autoMigrations` properties set, go ahead
// and perform any requested migrations.
WaterlineUtils.autoMigrations(sails.config.models.migrate, freshOntology, function(err) {
if (err) { return done(err); }
}
return WaterlineUtils.autoMigrations(migrate, datastore.orm)
});

return done(undefined, freshOntology);
});
Promise.all(migrations).then(function(response, err) {
if (err) { return done(err); }
console.log('--------------------------------- migrated -------------------------------------')
return done(undefined, datastores);
})

});//</waterline.initialize()>
};