mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-13 22:41:32 -05:00
35e2387541
Closes #1977, Refs #3473 - Ensure that import operations are run in sequence. Previously the operations were started in order but subsequent ops were allowed to begin before the previous finished, which would result in out-of-order execution. - Fix bug in attach() where a model property was being passed in instead of a transaction object. If the call was made when a transaction was in process, it could cause bookshelf/knex to hang and never finish the transaction.
131 lines
3.9 KiB
JavaScript
131 lines
3.9 KiB
JavaScript
var Promise = require('bluebird'),
|
|
_ = require('lodash'),
|
|
validation = require('../validation'),
|
|
errors = require('../../errors'),
|
|
uuid = require('node-uuid'),
|
|
validator = require('validator'),
|
|
tables = require('../schema').tables,
|
|
validate,
|
|
handleErrors,
|
|
sanitize,
|
|
cleanError;
|
|
|
|
cleanError = function cleanError(error) {
|
|
var temp,
|
|
message,
|
|
offendingProperty,
|
|
value;
|
|
|
|
if (error.raw.message.toLowerCase().indexOf('unique') !== -1) {
|
|
// This is a unique constraint failure
|
|
if (error.raw.message.indexOf('ER_DUP_ENTRY') !== -1) {
|
|
temp = error.raw.message.split('\'');
|
|
if (temp.length === 5) {
|
|
value = temp[1];
|
|
temp = temp[3].split('_');
|
|
offendingProperty = temp.length === 3 ? temp[0] + '.' + temp[1] : error.model;
|
|
}
|
|
} else if (error.raw.message.indexOf('SQLITE_CONSTRAINT') !== -1) {
|
|
temp = error.raw.message.split('failed: ');
|
|
offendingProperty = temp.length === 2 ? temp[1] : error.model;
|
|
temp = offendingProperty.split('.');
|
|
value = temp.length === 2 ? error.data[temp[1]] : 'unknown';
|
|
} else if (error.raw.detail) {
|
|
value = error.raw.detail;
|
|
offendingProperty = error.model;
|
|
}
|
|
message = 'Duplicate entry found. Multiple values of "' + value + '" found for ' + offendingProperty + '.';
|
|
}
|
|
|
|
offendingProperty = offendingProperty || error.model;
|
|
value = value || 'unknown';
|
|
message = message || error.raw.message;
|
|
|
|
return new errors.DataImportError(message, offendingProperty, value);
|
|
};
|
|
|
|
|
|
handleErrors = function handleErrors(errorList) {
|
|
var processedErrors = [];
|
|
|
|
if (!_.isArray(errorList)) {
|
|
return Promise.reject(errorList);
|
|
}
|
|
|
|
_.each(errorList, function (error) {
|
|
if (!error.raw) {
|
|
// These are validation errors
|
|
processedErrors.push(error);
|
|
} else if (_.isArray(error.raw)) {
|
|
processedErrors = processedErrors.concat(error.raw);
|
|
} else {
|
|
processedErrors.push(cleanError(error));
|
|
}
|
|
});
|
|
|
|
return Promise.reject(processedErrors);
|
|
};
|
|
|
|
sanitize = function sanitize(data) {
|
|
|
|
// Check for correct UUID and fix if neccessary
|
|
_.each(_.keys(data.data), function (tableName) {
|
|
_.each(data.data[tableName], function (importValues) {
|
|
|
|
var uuidMissing = (!importValues.uuid && tables[tableName].uuid) ? true : false,
|
|
uuidMalformed = (importValues.uuid && !validator.isUUID(importValues.uuid)) ? true : false;
|
|
|
|
if (uuidMissing || uuidMalformed) {
|
|
importValues.uuid = uuid.v4();
|
|
}
|
|
});
|
|
});
|
|
|
|
return data;
|
|
};
|
|
|
|
validate = function validate(data) {
|
|
var validateOps = [];
|
|
|
|
_.each(_.keys(data.data), function (tableName) {
|
|
_.each(data.data[tableName], function (importValues) {
|
|
validateOps.push(validation.validateSchema(tableName, importValues));
|
|
});
|
|
});
|
|
|
|
return Promise.settle(validateOps).then(function (descriptors) {
|
|
var errorList = [];
|
|
|
|
_.each(descriptors, function (d) {
|
|
if (d.isRejected()) {
|
|
errorList = errorList.concat(d.reason());
|
|
}
|
|
});
|
|
|
|
if (!_.isEmpty(errorList)) {
|
|
return Promise.reject(errorList);
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports = function (version, data) {
|
|
var importer;
|
|
|
|
data = sanitize(data);
|
|
|
|
return validate(data).then(function () {
|
|
try {
|
|
importer = require('./' + version);
|
|
} catch (ignore) {
|
|
// Zero effs given
|
|
}
|
|
|
|
if (!importer) {
|
|
return Promise.reject('No importer found');
|
|
}
|
|
|
|
return importer.importData(data);
|
|
}).catch(function (result) {
|
|
return handleErrors(result);
|
|
});
|
|
};
|