Generated migration files manages attribute's type update

This commit is contained in:
Aurélien Georget 2016-03-23 16:07:39 +01:00
parent cb433f57c0
commit aad090c62d
2 changed files with 119 additions and 124 deletions

View File

@ -76,148 +76,133 @@ module.exports = function (scope, cb) {
}
});
let history;
try {
history = JSON.parse(fs.readFileSync(path.resolve(scope.rootPath, 'data', 'migrations', '.history'), 'utf8'));
} catch (err) {
// File not existing
history = {};
}
const history = () => {
try {
return JSON.parse(fs.readFileSync(path.resolve(scope.rootPath, 'data', 'migrations', '.history'), 'utf8'));
} catch (err) {
// File not existing
return {};
}
}();
// Register every model.
const migrations = glob.sync(path.resolve(scope.rootPath, 'api', '**', 'models', '*.json')).map((file) => {
let modelName;
const migrations = glob.sync(path.resolve(scope.rootPath, 'api', '**', 'models', '*.json')).map((filepath) => {
try {
const file = JSON.parse(fs.readFileSync(path.resolve(filepath)));
// Only create migration file for the models with the specified connection.
if (JSON.parse(fs.readFileSync(path.resolve(file))).connection === scope.connection) {
// Only create migration file for the models with the specified connection.
if (_.get(file, 'connection') === _.get(scope, 'connection')) {
// Save the model name thanks to the given table name.
const modelName = _.get(file, 'tableName');
scope.models[modelName] = file;
// Save the model name thanks to the given table name.
modelName = JSON.parse(fs.readFileSync(path.resolve(file))).tableName;
scope.models[modelName] = JSON.parse(fs.readFileSync(path.resolve(file)));
if (!_.isEmpty(history) && history.hasOwnProperty(_.capitalize(modelName))) {
_.set(scope.models, modelName + '.oldAttributes', _.get(history, _.capitalize(modelName) + '.attributes'));
} else {
_.set(scope.models, modelName + '.oldAttributes', {});
}
// First, we need to know if the table already exists.
scope.db.schema.hasTable(modelName).then(function (exists) {
// If the table doesn't exist.
if (!exists) {
// Builder: add needed options specified in the model
// for each option.
_.forEach(scope.models[modelName].options, function (value, option) {
builder.options(scope.models, modelName, value, option);
});
// Builder: create template for each attribute-- either with a column type
// or with a relationship.
_.forEach(scope.models[modelName].attributes, function (details, attribute) {
if (details.type && _.isString(details.type)) {
builder.types(scope.models, modelName, details, attribute);
} else if (_.isString(details.collection) || _.isString(details.model)) {
builder.relations(scope.models, modelName, details, attribute);
}
});
// Builder: create and drop the table.
builder.createTable(scope.models, modelName);
if (!_.isEmpty(history) && history.hasOwnProperty(_.capitalize(modelName))) {
_.set(scope.models, modelName + '.oldAttributes', _.get(history, _.capitalize(modelName) + '.attributes'));
} else {
_.set(scope.models, modelName + '.oldAttributes', {});
}
// If the table already exists.
else {
// First, we need to know if the table already exists.
scope.db.schema.hasTable(modelName).then(function (exists) {
// If the table doesn't exist.
if (!exists) {
// Builder: add needed options specified in the model
// for each option.
_.forEach(scope.models[modelName].options, function (value, option) {
builder.options(scope.models, modelName, value, option);
});
// Ideally, we need to verify the table properties here
// to see if they still are the same.
// Parse every attribute.
_.forEach(scope.models[modelName].attributes, function (details, attribute) {
// TODO:
// - Column is existing ?
// -- YES:
// --- Compare current type with last one (nullable, maxLenght, type, defaultValue).
// ---- Updated ? Drop column, and create a new one.
// ---- Not updated ? Do nothing.
// -- NO:
// --- Add the new column
// scope.db.schema.hasColumn(modelName, attribute).then(function (exists) {
//
// }).catch(function (err) {
// console.log(err);
// });
// Verify if a column already exists for the attribute.
scope.models[modelName].newAttributes = {};
// If it's a new attribute.
if (!scope.models[modelName].oldAttributes.hasOwnProperty(attribute)) {
// Save the attribute as a new attribute.
scope.models[modelName].newAttributes[attribute] = _.cloneDeep(details);
// Builder: create template for each attribute-- either with a column type
// or with a relationship.
// Builder: create template for each attribute-- either with a column type
// or with a relationship.
_.forEach(scope.models[modelName].attributes, function (details, attribute) {
if (details.type && _.isString(details.type)) {
builder.types(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute);
builder.types(scope.models, modelName, details, attribute);
} else if (_.isString(details.collection) || _.isString(details.model)) {
builder.relations(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute);
builder.relations(scope.models, modelName, details, attribute);
}
});
// Builder: select the table.
builder.selectTable(scope.models, modelName);
} else {
// If the column already exists.
// Builder: create and drop the table.
builder.createTable(scope.models, modelName);
} else {
// If the table already exists.
let toDrop = false;
// Set new attributes object
_.set(scope.models[modelName], 'newAttributes', {});
// Try to identify relation attribute update
if (details.hasOwnProperty('collection') && details.hasOwnProperty('via') &&
(_.get(scope.models[modelName].oldAttributes[attribute], 'collection') !== details.collection || _.get(scope.models[modelName].oldAttributes[attribute], 'via') !== details.via)) {
toDrop = true;
} else if (details.hasOwnProperty('model') && details.hasOwnProperty('via') &&
(_.get(scope.models[modelName].oldAttributes[attribute], 'model') !== details.model || _.get(scope.models[modelName].oldAttributes[attribute], 'via') !== details.via)) {
toDrop = true;
} else if (details.hasOwnProperty('model') &&
(_.get(scope.models[modelName].oldAttributes[attribute], 'model') !== details.model)) {
toDrop = true;
} else if (!_.isUndefined(details.type) && _.get(scope.models[modelName].oldAttributes[attribute], 'type') !== _.get(details, 'type')) {
toDrop = true;
} else if (!_.isUndefined(details.defaultValue) && _.get(scope.models[modelName].oldAttributes[attribute], 'defaultValue') === _.get(details, 'defaultValue')) {
toDrop = true;
} else if (!_.isUndefined(details.maxLength) && _.get(scope.models[modelName].oldAttributes[attribute], 'maxLength') === _.get(details, 'maxLength')) {
toDrop = true;
} else if (!_.isUndefined(details.nullable) && _.get(scope.models[modelName].oldAttributes[attribute], 'nullable') === _.get(details, 'nullable')) {
toDrop = true;
}
// The attribute has been updated.
// We will drop it then create it again with the new options.
if (toDrop) {
// Parse every attribute.
_.forEach(scope.models[modelName].attributes, function (details, attribute) {
// If it's a new attribute.
if (!scope.models[modelName].oldAttributes.hasOwnProperty(attribute)) {
// Save the attribute as a new attribute.
scope.models[modelName].newAttributes[attribute] = _.cloneDeep(details);
// Builder: create template for each attribute-- either with a column type
// or with a relationship.
if (details.type && _.isString(details.type)) {
builder.types(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute, toDrop);
builder.types(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute);
} else if (_.isString(details.collection) || _.isString(details.model)) {
builder.relations(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute, toDrop);
builder.relations(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute);
}
} else {
// If it's an existing attribute.
// Builder: select the table.
builder.selectTable(scope.models, modelName);
// Try to identify attribute updates
const toDrop = () => {
if (details.hasOwnProperty('collection') && details.hasOwnProperty('via') &&
(_.get(scope.models[modelName].oldAttributes[attribute], 'collection') !== details.collection || _.get(scope.models[modelName].oldAttributes[attribute], 'via') !== details.via)) {
return true;
} else if (details.hasOwnProperty('model') && details.hasOwnProperty('via') &&
(_.get(scope.models[modelName].oldAttributes[attribute], 'model') !== details.model || _.get(scope.models[modelName].oldAttributes[attribute], 'via') !== details.via)) {
return true;
} else if (details.hasOwnProperty('model') &&
(_.get(scope.models[modelName].oldAttributes[attribute], 'model') !== details.model)) {
return true;
} else if (!_.isUndefined(details.type) && _.get(scope.models[modelName].oldAttributes[attribute], 'type') !== _.get(details, 'type')) {
return true;
} else if (!_.isUndefined(details.defaultValue) && _.get(scope.models[modelName].oldAttributes[attribute], 'defaultValue') === _.get(details, 'defaultValue')) {
return true;
} else if (!_.isUndefined(details.maxLength) && _.get(scope.models[modelName].oldAttributes[attribute], 'maxLength') === _.get(details, 'maxLength')) {
return true;
} else if (!_.isUndefined(details.nullable) && _.get(scope.models[modelName].oldAttributes[attribute], 'nullable') === _.get(details, 'nullable')) {
return true;
} else {
return false;
}
}();
// The attribute has been updated.
// We will drop it then create it again with the new options.
if (toDrop) {
// Save the attribute as a new attribute.
scope.models[modelName].newAttributes[attribute] = _.cloneDeep(details);
// Builder: create template for each attribute-- either with a column type
// or with a relationship.
if (details.type && _.isString(details.type)) {
builder.types(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute, toDrop);
} else if (_.isString(details.collection) || _.isString(details.model)) {
builder.relations(scope.models, modelName, scope.models[modelName].newAttributes[attribute], attribute, toDrop);
}
}
}
}
});
}
});
});
return new Promise((resolve) => {
asyncFunction(file, resolve);
});
// For lightweight migration file,
// only call this when new attributes are detected.
if (!_.isEmpty(scope.models[modelName].newAttributes)) {
// Builder: select the table.
builder.selectTable(scope.models, modelName);
}
}
});
return new Promise((resolve) => {
asyncFunction(filepath, resolve);
});
}
} catch (e) {
return cb.invalid(e);
}
});

View File

@ -17,38 +17,48 @@ const _ = require('lodash');
module.exports = function (models, modelName) {
models[modelName].up = {};
if (!models[modelName].hasOwnProperty('up')) {
models[modelName].up = {
drop: '',
others: ''
};
}
// Template: select the table for the `up` export.
// Every attribute with `create` key will be added in this template.
const tplSelectTableUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'tables', 'select', 'up.template'), 'utf8');
models[modelName].up.drop = _.unescape(_.template(tplSelectTableUp)({
models[modelName].up.drop += _.unescape(_.template(tplSelectTableUp)({
models: models,
tableName: modelName,
attributes: models[modelName].newAttributes,
toDrop: true
}));
models[modelName].up.others = _.unescape(_.template(tplSelectTableUp)({
models[modelName].up.others += _.unescape(_.template(tplSelectTableUp)({
models: models,
tableName: modelName,
attributes: models[modelName].newAttributes,
toDrop: false
}));
models[modelName].down = {};
if (!models[modelName].hasOwnProperty('down')) {
models[modelName].down = {
drop: '',
others: ''
};
}
// Template: select the table for the `down` export.
// Every attribute with `delete` key will be added in this template.
const tplSelectTableDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'tables', 'select', 'down.template'), 'utf8');
models[modelName].down.drop = _.unescape(_.template(tplSelectTableDown)({
models[modelName].down.drop += _.unescape(_.template(tplSelectTableDown)({
models: models,
tableName: modelName,
attributes: models[modelName].newAttributes,
toDrop: true
}));
models[modelName].down.others = _.unescape(_.template(tplSelectTableDown)({
models[modelName].down.others += _.unescape(_.template(tplSelectTableDown)({
models: models,
tableName: modelName,
attributes: models[modelName].newAttributes,