mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Merge pull request #4673 from ErisDS/importer-imprv
Data importer improvements and fixes
This commit is contained in:
commit
8714822d48
15 changed files with 803 additions and 834 deletions
|
@ -1,8 +0,0 @@
|
||||||
var Importer000 = require('./000');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Importer001: Importer000,
|
|
||||||
importData: function (data) {
|
|
||||||
return new Importer000.importData(data);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
var Importer000 = require('./000');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Importer002: Importer000,
|
|
||||||
importData: function (data) {
|
|
||||||
return new Importer000.importData(data);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
var Importer000 = require('./000');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Importer003: Importer000,
|
|
||||||
importData: function (data) {
|
|
||||||
return new Importer000.importData(data);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -3,37 +3,15 @@ var Promise = require('bluebird'),
|
||||||
models = require('../../models'),
|
models = require('../../models'),
|
||||||
utils = require('./utils'),
|
utils = require('./utils'),
|
||||||
|
|
||||||
Importer000;
|
DataImporter;
|
||||||
|
|
||||||
Importer000 = function () {
|
DataImporter = function () {};
|
||||||
_.bindAll(this, 'doImport');
|
|
||||||
|
|
||||||
this.version = '000';
|
DataImporter.prototype.importData = function (data) {
|
||||||
|
return this.doImport(data);
|
||||||
this.importFrom = {
|
|
||||||
'000': this.doImport,
|
|
||||||
'001': this.doImport,
|
|
||||||
'002': this.doImport,
|
|
||||||
'003': this.doImport
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Importer000.prototype.importData = function (data) {
|
DataImporter.prototype.loadUsers = function () {
|
||||||
return this.canImport(data)
|
|
||||||
.then(function (importerFunc) {
|
|
||||||
return importerFunc(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Importer000.prototype.canImport = function (data) {
|
|
||||||
if (data.meta && data.meta.version && this.importFrom[data.meta.version]) {
|
|
||||||
return Promise.resolve(this.importFrom[data.meta.version]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject('Unsupported version of data: ' + data.meta.version);
|
|
||||||
};
|
|
||||||
|
|
||||||
Importer000.prototype.loadUsers = function () {
|
|
||||||
var users = {all: {}};
|
var users = {all: {}};
|
||||||
|
|
||||||
return models.User.findAll({include: ['roles']}).then(function (_users) {
|
return models.User.findAll({include: ['roles']}).then(function (_users) {
|
||||||
|
@ -52,11 +30,7 @@ Importer000.prototype.loadUsers = function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Importer000.prototype.importerFunction = function (t) {
|
DataImporter.prototype.doUserImport = function (t, tableData, users, errors) {
|
||||||
//
|
|
||||||
// };
|
|
||||||
|
|
||||||
Importer000.prototype.doUserImport = function (t, tableData, users, errors) {
|
|
||||||
var userOps = [],
|
var userOps = [],
|
||||||
imported = [];
|
imported = [];
|
||||||
|
|
||||||
|
@ -89,7 +63,7 @@ Importer000.prototype.doUserImport = function (t, tableData, users, errors) {
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
};
|
};
|
||||||
|
|
||||||
Importer000.prototype.doImport = function (data) {
|
DataImporter.prototype.doImport = function (data) {
|
||||||
var self = this,
|
var self = this,
|
||||||
tableData = data.data,
|
tableData = data.data,
|
||||||
imported = {},
|
imported = {},
|
||||||
|
@ -171,8 +145,8 @@ Importer000.prototype.doImport = function (data) {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Importer000: Importer000,
|
DataImporter: DataImporter,
|
||||||
importData: function (data) {
|
importData: function (data) {
|
||||||
return new Importer000().importData(data);
|
return new DataImporter().importData(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -3,7 +3,7 @@ var Promise = require('bluebird'),
|
||||||
validation = require('../validation'),
|
validation = require('../validation'),
|
||||||
errors = require('../../errors'),
|
errors = require('../../errors'),
|
||||||
uuid = require('node-uuid'),
|
uuid = require('node-uuid'),
|
||||||
validator = require('validator'),
|
importer = require('./data-importer'),
|
||||||
tables = require('../schema').tables,
|
tables = require('../schema').tables,
|
||||||
validate,
|
validate,
|
||||||
handleErrors,
|
handleErrors,
|
||||||
|
@ -89,14 +89,14 @@ sanitize = function sanitize(data) {
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each(tableNames, function (tableName) {
|
_.each(tableNames, function (tableName) {
|
||||||
// Sanitize the table data for duplicates and valid uuid values
|
// Sanitize the table data for duplicates and valid uuid and created_at values
|
||||||
var sanitizedTableData = _.transform(data.data[tableName], function (memo, importValues) {
|
var sanitizedTableData = _.transform(data.data[tableName], function (memo, importValues) {
|
||||||
var uuidMissing = (!importValues.uuid && tables[tableName].uuid) ? true : false,
|
var uuidMissing = (!importValues.uuid && tables[tableName].uuid) ? true : false,
|
||||||
uuidMalformed = (importValues.uuid && !validator.isUUID(importValues.uuid)) ? true : false,
|
uuidMalformed = (importValues.uuid && !validation.validator.isUUID(importValues.uuid)) ? true : false,
|
||||||
isDuplicate,
|
isDuplicate,
|
||||||
problemTag;
|
problemTag;
|
||||||
|
|
||||||
// Check for correct UUID and fix if neccessary
|
// Check for correct UUID and fix if necessary
|
||||||
if (uuidMissing || uuidMalformed) {
|
if (uuidMissing || uuidMalformed) {
|
||||||
importValues.uuid = uuid.v4();
|
importValues.uuid = uuid.v4();
|
||||||
}
|
}
|
||||||
|
@ -184,25 +184,12 @@ validate = function validate(data) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = function (version, data) {
|
module.exports = function (data) {
|
||||||
var importer,
|
var sanitizeResults = sanitize(data);
|
||||||
sanitizeResults;
|
|
||||||
|
|
||||||
sanitizeResults = sanitize(data);
|
|
||||||
|
|
||||||
data = sanitizeResults.data;
|
data = sanitizeResults.data;
|
||||||
|
|
||||||
return validate(data).then(function () {
|
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);
|
return importer.importData(data);
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
return sanitizeResults;
|
return sanitizeResults;
|
||||||
|
|
|
@ -185,34 +185,25 @@ utils = {
|
||||||
var ops = [];
|
var ops = [];
|
||||||
|
|
||||||
tableData = stripProperties(['id'], tableData);
|
tableData = stripProperties(['id'], tableData);
|
||||||
|
|
||||||
_.each(tableData, function (post) {
|
_.each(tableData, function (post) {
|
||||||
// Validate minimum post fields
|
// Validate minimum post fields
|
||||||
if (areEmpty(post, 'title', 'slug', 'markdown')) {
|
if (areEmpty(post, 'title', 'slug', 'markdown')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops.push(function () {
|
// The post importer has auto-timestamping disabled
|
||||||
return models.Post.add(post, _.extend(internal, {transacting: transaction, importing: true}))
|
if (!post.created_at) {
|
||||||
|
post.created_at = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
ops.push(models.Post.add(post, _.extend(internal, {transacting: transaction, importing: true}))
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
return Promise.reject({raw: error, model: 'post', data: post});
|
return Promise.reject({raw: error, model: 'post', data: post});
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.reduce(ops, function (results, op) {
|
return Promise.settle(ops);
|
||||||
return op().then(function (result) {
|
|
||||||
results.push(result);
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}).catch(function (error) {
|
|
||||||
if (error) {
|
|
||||||
results.push(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
}, []).settle();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importUsers: function importUsers(tableData, existingUsers, transaction) {
|
importUsers: function importUsers(tableData, existingUsers, transaction) {
|
||||||
|
|
|
@ -20,7 +20,11 @@ JSONHandler = {
|
||||||
importData = JSON.parse(fileData);
|
importData = JSON.parse(fileData);
|
||||||
|
|
||||||
// if importData follows JSON-API format `{ db: [exportedData] }`
|
// if importData follows JSON-API format `{ db: [exportedData] }`
|
||||||
if (_.keys(importData).length === 1 && Array.isArray(importData.db)) {
|
if (_.keys(importData).length === 1) {
|
||||||
|
if (!importData.db || !Array.isArray(importData.db)) {
|
||||||
|
throw new Error('Invalid JSON format, expected `{ db: [exportedData] }`');
|
||||||
|
}
|
||||||
|
|
||||||
importData = importData.db[0];
|
importData = importData.db[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ DataImporter = {
|
||||||
return importData;
|
return importData;
|
||||||
},
|
},
|
||||||
doImport: function (importData) {
|
doImport: function (importData) {
|
||||||
return importer('003', importData);
|
return importer(importData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,8 @@ validator.extend('isEmptyOrURL', function (str) {
|
||||||
return (_.isEmpty(str) || validator.isURL(str, {require_protocol: false}));
|
return (_.isEmpty(str) || validator.isURL(str, {require_protocol: false}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Validation validation against schema attributes
|
// Validation against schema attributes
|
||||||
// values are checked against the validation objects
|
// values are checked against the validation objects from schema.js
|
||||||
// form schema.js
|
|
||||||
validateSchema = function (tableName, model) {
|
validateSchema = function (tableName, model) {
|
||||||
var columns = _.keys(schema[tableName]),
|
var columns = _.keys(schema[tableName]),
|
||||||
validationErrors = [];
|
validationErrors = [];
|
||||||
|
@ -163,6 +162,7 @@ validate = function (value, key, validations) {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
validator: validator,
|
||||||
validateSchema: validateSchema,
|
validateSchema: validateSchema,
|
||||||
validateSettings: validateSettings,
|
validateSettings: validateSettings,
|
||||||
validateActiveTheme: validateActiveTheme
|
validateActiveTheme: validateActiveTheme
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,10 @@
|
||||||
/*globals describe, afterEach, it*/
|
/*globals describe, afterEach, it*/
|
||||||
/*jshint expr:true*/
|
/*jshint expr:true*/
|
||||||
var should = require('should'),
|
var should = require('should'),
|
||||||
sinon = require('sinon'),
|
sinon = require('sinon'),
|
||||||
Promise = require('bluebird'),
|
Promise = require('bluebird'),
|
||||||
_ = require('lodash'),
|
_ = require('lodash'),
|
||||||
|
testUtils = require('../utils'),
|
||||||
|
|
||||||
// Stuff we are testing
|
// Stuff we are testing
|
||||||
ImportManager = require('../../server/data/importer'),
|
ImportManager = require('../../server/data/importer'),
|
||||||
|
@ -179,6 +180,32 @@ describe('Importer', function () {
|
||||||
JSONHandler.types.should.containEql('application/json');
|
JSONHandler.types.should.containEql('application/json');
|
||||||
JSONHandler.loadFile.should.be.instanceof(Function);
|
JSONHandler.loadFile.should.be.instanceof(Function);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('correctly handles a valid db api wrapper', function (done) {
|
||||||
|
var file = [{
|
||||||
|
path: testUtils.fixtures.getExportFixturePath('export-003-api-wrapper'),
|
||||||
|
name: 'export-003-api-wrapper.json'
|
||||||
|
}];
|
||||||
|
JSONHandler.loadFile(file).then(function (result) {
|
||||||
|
_.keys(result).should.containEql('meta');
|
||||||
|
_.keys(result).should.containEql('data');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly errors when given a bad db api wrapper', function (done) {
|
||||||
|
var file = [{
|
||||||
|
path: testUtils.fixtures.getExportFixturePath('export-003-api-wrapper-bad'),
|
||||||
|
name: 'export-003-api-wrapper-bad.json'
|
||||||
|
}];
|
||||||
|
|
||||||
|
JSONHandler.loadFile(file).then(function () {
|
||||||
|
done(new Error('Didn\'t error for bad db api wrapper'));
|
||||||
|
}).catch(function (response) {
|
||||||
|
response.type.should.equal('BadRequestError');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DataImporter', function () {
|
describe('DataImporter', function () {
|
||||||
|
|
31
core/test/utils/fixtures/export-003-api-wrapper-bad.json
Normal file
31
core/test/utils/fixtures/export-003-api-wrapper-bad.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{"asdadas":[{
|
||||||
|
"meta": {
|
||||||
|
"exported_on": 1388318311015,
|
||||||
|
"version": "003"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"posts": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Welcome to Ghost",
|
||||||
|
"slug": "welcome-to-ghost",
|
||||||
|
"markdown": "You're live! Nice.",
|
||||||
|
"html": "<p>You're live! Nice.</p>",
|
||||||
|
"image": null,
|
||||||
|
"featured": 0,
|
||||||
|
"page": 0,
|
||||||
|
"status": "published",
|
||||||
|
"language": "en_US",
|
||||||
|
"meta_title": null,
|
||||||
|
"meta_description": null,
|
||||||
|
"author_id": 2,
|
||||||
|
"created_at": 1388318310782,
|
||||||
|
"created_by": 1,
|
||||||
|
"updated_at": 1388318310782,
|
||||||
|
"updated_by": 1,
|
||||||
|
"published_at": 1388318310783,
|
||||||
|
"published_by": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]}
|
31
core/test/utils/fixtures/export-003-api-wrapper.json
Normal file
31
core/test/utils/fixtures/export-003-api-wrapper.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{"db":[{
|
||||||
|
"meta": {
|
||||||
|
"exported_on": 1388318311015,
|
||||||
|
"version": "003"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"posts": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Welcome to Ghost",
|
||||||
|
"slug": "welcome-to-ghost",
|
||||||
|
"markdown": "You're live! Nice.",
|
||||||
|
"html": "<p>You're live! Nice.</p>",
|
||||||
|
"image": null,
|
||||||
|
"featured": 0,
|
||||||
|
"page": 0,
|
||||||
|
"status": "published",
|
||||||
|
"language": "en_US",
|
||||||
|
"meta_title": null,
|
||||||
|
"meta_description": null,
|
||||||
|
"author_id": 2,
|
||||||
|
"created_at": 1388318310782,
|
||||||
|
"created_by": 1,
|
||||||
|
"updated_at": 1388318310782,
|
||||||
|
"updated_by": 1,
|
||||||
|
"published_at": 1388318310783,
|
||||||
|
"published_by": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]}
|
15
core/test/utils/fixtures/export-003-minimal.json
Normal file
15
core/test/utils/fixtures/export-003-minimal.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"exported_on": 1388318311015,
|
||||||
|
"version": "003"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"posts": [
|
||||||
|
{
|
||||||
|
"title": "Welcome to Ghost",
|
||||||
|
"slug": "welcome-to-ghost",
|
||||||
|
"markdown": "You're live! Nice."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -280,11 +280,15 @@ fixtures = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getExportFixturePath: function (filename) {
|
||||||
|
return path.resolve(__dirname + '/fixtures/' + filename + '.json');
|
||||||
|
},
|
||||||
|
|
||||||
loadExportFixture: function loadExportFixture(filename) {
|
loadExportFixture: function loadExportFixture(filename) {
|
||||||
var filepath = path.resolve(__dirname + '/fixtures/' + filename + '.json'),
|
var filePath = this.getExportFixturePath(filename),
|
||||||
readFile = Promise.promisify(fs.readFile);
|
readFile = Promise.promisify(fs.readFile);
|
||||||
|
|
||||||
return readFile(filepath).then(function (fileContents) {
|
return readFile(filePath).then(function (fileContents) {
|
||||||
var data;
|
var data;
|
||||||
|
|
||||||
// Parse the json data
|
// Parse the json data
|
||||||
|
|
Loading…
Add table
Reference in a new issue