mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-02 07:43:11 +03:00
Merge pull request #6596 from ErisDS/export-clean
Exporter cleanup & tests
This commit is contained in:
commit
39dfb2c09a
@ -2,7 +2,7 @@
|
||||
// API for DB operations
|
||||
var _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
dataExport = require('../data/export'),
|
||||
exporter = require('../data/export'),
|
||||
importer = require('../data/importer'),
|
||||
backupDatabase = require('../data/migration').backupDatabase,
|
||||
models = require('../models'),
|
||||
@ -38,7 +38,7 @@ db = {
|
||||
|
||||
// Export data, otherwise send error 500
|
||||
function exportContent() {
|
||||
return dataExport().then(function (exportedData) {
|
||||
return exporter.doExport().then(function (exportedData) {
|
||||
return {db: [exportedData]};
|
||||
}).catch(function (error) {
|
||||
return Promise.reject(new errors.InternalServerError(error.message || error));
|
||||
|
@ -21,7 +21,7 @@ var _ = require('lodash'),
|
||||
slugs = require('./slugs'),
|
||||
authentication = require('./authentication'),
|
||||
uploads = require('./upload'),
|
||||
dataExport = require('../data/export'),
|
||||
exporter = require('../data/export'),
|
||||
|
||||
http,
|
||||
addHeaders,
|
||||
@ -138,7 +138,7 @@ locationHeader = function locationHeader(req, result) {
|
||||
* @return {string}
|
||||
*/
|
||||
contentDispositionHeader = function contentDispositionHeader() {
|
||||
return dataExport.fileName().then(function then(filename) {
|
||||
return exporter.fileName().then(function then(filename) {
|
||||
return 'Attachment; filename="' + filename + '"';
|
||||
});
|
||||
};
|
||||
|
@ -167,8 +167,6 @@ ConfigManager.prototype.set = function (config) {
|
||||
|
||||
adminViews: path.join(corePath, '/server/views/'),
|
||||
helperTemplates: path.join(corePath, '/server/helpers/tpl/'),
|
||||
exportPath: path.join(corePath, '/server/data/export/'),
|
||||
lang: path.join(corePath, '/shared/lang/'),
|
||||
|
||||
availableThemes: this._config.paths.availableThemes || {},
|
||||
availableApps: this._config.paths.availableApps || {},
|
||||
|
@ -2,7 +2,7 @@ var connection;
|
||||
|
||||
Object.defineProperty(exports, 'knex', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
configurable: true,
|
||||
get: function get() {
|
||||
connection = connection || require('./connection');
|
||||
return connection;
|
||||
|
@ -8,15 +8,22 @@ var _ = require('lodash'),
|
||||
settings = require('../../api/settings'),
|
||||
i18n = require('../../i18n'),
|
||||
|
||||
excludedTables = ['accesstokens', 'refreshtokens', 'clients'],
|
||||
exporter,
|
||||
excludedTables = ['accesstokens', 'refreshtokens', 'clients', 'client_trusted_domains'],
|
||||
modelOptions = {context: {internal: true}},
|
||||
|
||||
// private
|
||||
getVersionAndTables,
|
||||
exportTable,
|
||||
|
||||
// public
|
||||
doExport,
|
||||
exportFileName;
|
||||
|
||||
exportFileName = function () {
|
||||
exportFileName = function exportFileName() {
|
||||
var datetime = (new Date()).toJSON().substring(0, 10),
|
||||
title = '';
|
||||
|
||||
return settings.read({key: 'title', context: {internal: true}}).then(function (result) {
|
||||
return settings.read(_.extend({}, {key: 'title'}, modelOptions)).then(function (result) {
|
||||
if (result) {
|
||||
title = serverUtils.safeString(result.settings[0].value) + '.';
|
||||
}
|
||||
@ -27,37 +34,51 @@ exportFileName = function () {
|
||||
});
|
||||
};
|
||||
|
||||
exporter = function () {
|
||||
return Promise.join(versioning.getDatabaseVersion(), commands.getTables()).then(function (results) {
|
||||
var version = results[0],
|
||||
tables = results[1],
|
||||
selectOps = _.map(tables, function (name) {
|
||||
if (excludedTables.indexOf(name) < 0) {
|
||||
return db.knex(name).select();
|
||||
}
|
||||
});
|
||||
getVersionAndTables = function getVersionAndTables() {
|
||||
var props = {
|
||||
version: versioning.getDatabaseVersion(),
|
||||
tables: commands.getTables()
|
||||
};
|
||||
|
||||
return Promise.all(selectOps).then(function (tableData) {
|
||||
var exportData = {
|
||||
meta: {
|
||||
exported_on: new Date().getTime(),
|
||||
version: version
|
||||
},
|
||||
data: {
|
||||
// Filled below
|
||||
}
|
||||
};
|
||||
return Promise.props(props);
|
||||
};
|
||||
|
||||
_.each(tables, function (name, i) {
|
||||
exportData.data[name] = tableData[i];
|
||||
});
|
||||
exportTable = function exportTable(tableName) {
|
||||
if (excludedTables.indexOf(tableName) < 0) {
|
||||
return db.knex(tableName).select();
|
||||
}
|
||||
};
|
||||
|
||||
return exportData;
|
||||
}).catch(function (err) {
|
||||
errors.logAndThrowError(err, i18n.t('errors.data.export.errorExportingData'), '');
|
||||
doExport = function doExport() {
|
||||
var tables, version;
|
||||
|
||||
return getVersionAndTables().then(function exportAllTables(result) {
|
||||
tables = result.tables;
|
||||
version = result.version;
|
||||
|
||||
return Promise.mapSeries(tables, exportTable);
|
||||
}).then(function formatData(tableData) {
|
||||
var exportData = {
|
||||
meta: {
|
||||
exported_on: new Date().getTime(),
|
||||
version: version
|
||||
},
|
||||
data: {
|
||||
// Filled below
|
||||
}
|
||||
};
|
||||
|
||||
_.each(tables, function (name, i) {
|
||||
exportData.data[name] = tableData[i];
|
||||
});
|
||||
|
||||
return exportData;
|
||||
}).catch(function (err) {
|
||||
errors.logAndThrowError(err, i18n.t('errors.data.export.errorExportingData'), '');
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = exporter;
|
||||
module.exports.fileName = exportFileName;
|
||||
module.exports = {
|
||||
doExport: doExport,
|
||||
fileName: exportFileName
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ var _ = require('lodash'),
|
||||
schema = require('../schema').tables,
|
||||
commands = require('../schema').commands,
|
||||
versioning = require('../schema').versioning,
|
||||
dataExport = require('../export'),
|
||||
exporter = require('../export'),
|
||||
config = require('../../config'),
|
||||
errors = require('../../errors'),
|
||||
models = require('../../models'),
|
||||
@ -46,9 +46,9 @@ populateDefaultSettings = function populateDefaultSettings() {
|
||||
|
||||
backupDatabase = function backupDatabase() {
|
||||
logInfo('Creating database backup');
|
||||
return dataExport().then(function (exportedData) {
|
||||
return exporter.doExport().then(function (exportedData) {
|
||||
// Save the exported data to the file system for download
|
||||
return dataExport.fileName().then(function (fileName) {
|
||||
return exporter.fileName().then(function (fileName) {
|
||||
fileName = path.resolve(config.paths.contentPath + '/data/' + fileName);
|
||||
|
||||
return Promise.promisify(fs.writeFile)(fileName, JSON.stringify(exportedData)).then(function () {
|
||||
|
@ -7,7 +7,7 @@ var testUtils = require('../utils/index'),
|
||||
|
||||
// Stuff we are testing
|
||||
versioning = require('../../server/data/schema').versioning,
|
||||
exporter = require('../../server/data/export/index'),
|
||||
exporter = require('../../server/data/export'),
|
||||
|
||||
DEF_DB_VERSION = versioning.getDefaultDatabaseVersion(),
|
||||
sandbox = sinon.sandbox.create();
|
||||
@ -28,7 +28,7 @@ describe('Exporter', function () {
|
||||
return Promise.resolve(DEF_DB_VERSION);
|
||||
});
|
||||
|
||||
exporter().then(function (exportData) {
|
||||
exporter.doExport().then(function (exportData) {
|
||||
var tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles',
|
||||
'permissions_users', 'settings', 'tags', 'posts_tags'],
|
||||
dbVersionSetting;
|
||||
|
@ -72,8 +72,6 @@ describe('Config', function () {
|
||||
'imagesRelPath',
|
||||
'adminViews',
|
||||
'helperTemplates',
|
||||
'exportPath',
|
||||
'lang',
|
||||
'availableThemes',
|
||||
'availableApps',
|
||||
'clientAssets'
|
||||
|
143
core/test/unit/exporter_spec.js
Normal file
143
core/test/unit/exporter_spec.js
Normal file
@ -0,0 +1,143 @@
|
||||
/*globals describe, afterEach, beforeEach, it*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
|
||||
// Stuff we're testing
|
||||
db = require('../../server/data/db'),
|
||||
errors = require('../../server/errors'),
|
||||
exporter = require('../../server/data/export'),
|
||||
schema = require('../../server/data/schema'),
|
||||
settings = require('../../server/api/settings'),
|
||||
|
||||
schemaTables = Object.keys(schema.tables),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
require('should-sinon');
|
||||
|
||||
describe('Exporter', function () {
|
||||
var versionStub, tablesStub, queryMock, knexMock, knexStub;
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
knexStub.restore();
|
||||
});
|
||||
|
||||
describe('doExport', function () {
|
||||
beforeEach(function () {
|
||||
versionStub = sandbox.stub(schema.versioning, 'getDatabaseVersion').returns(new Promise.resolve('004'));
|
||||
tablesStub = sandbox.stub(schema.commands, 'getTables').returns(schemaTables);
|
||||
|
||||
queryMock = {
|
||||
select: sandbox.stub()
|
||||
};
|
||||
|
||||
knexMock = sandbox.stub().returns(queryMock);
|
||||
|
||||
// this MUST use sinon, not sandbox, see sinonjs/sinon#781
|
||||
knexStub = sinon.stub(db, 'knex', {get: function () { return knexMock; }});
|
||||
});
|
||||
|
||||
it('should try to export all the correct tables', function (done) {
|
||||
// Setup for success
|
||||
queryMock.select.returns(new Promise.resolve({}));
|
||||
|
||||
// Execute
|
||||
exporter.doExport().then(function (exportData) {
|
||||
// No tables, less the number of excluded tables
|
||||
var expectedCallCount = schemaTables.length - 4;
|
||||
|
||||
should.exist(exportData);
|
||||
|
||||
versionStub.should.be.calledOnce();
|
||||
tablesStub.should.be.calledOnce();
|
||||
knexStub.get.should.be.called();
|
||||
knexMock.should.be.called();
|
||||
queryMock.select.should.be.called();
|
||||
|
||||
knexMock.should.have.callCount(expectedCallCount);
|
||||
queryMock.select.should.have.callCount(expectedCallCount);
|
||||
|
||||
knexMock.getCall(0).should.be.calledWith('posts');
|
||||
knexMock.getCall(1).should.be.calledWith('users');
|
||||
knexMock.getCall(2).should.be.calledWith('roles');
|
||||
knexMock.getCall(3).should.be.calledWith('roles_users');
|
||||
knexMock.getCall(4).should.be.calledWith('permissions');
|
||||
knexMock.getCall(5).should.be.calledWith('permissions_users');
|
||||
knexMock.getCall(6).should.be.calledWith('permissions_roles');
|
||||
knexMock.getCall(7).should.be.calledWith('permissions_apps');
|
||||
knexMock.getCall(8).should.be.calledWith('settings');
|
||||
knexMock.getCall(9).should.be.calledWith('tags');
|
||||
knexMock.getCall(10).should.be.calledWith('posts_tags');
|
||||
knexMock.getCall(11).should.be.calledWith('apps');
|
||||
knexMock.getCall(12).should.be.calledWith('app_settings');
|
||||
knexMock.getCall(13).should.be.calledWith('app_fields');
|
||||
|
||||
knexMock.should.not.be.calledWith('clients');
|
||||
knexMock.should.not.be.calledWith('client_trusted_domains');
|
||||
knexMock.should.not.be.calledWith('refreshtokens');
|
||||
knexMock.should.not.be.calledWith('accesstokens');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should catch and log any errors', function (done) {
|
||||
// Setup for failure
|
||||
var errorStub = sandbox.stub(errors, 'logAndThrowError');
|
||||
queryMock.select.returns(new Promise.reject({}));
|
||||
|
||||
// Execute
|
||||
exporter.doExport().then(function (exportData) {
|
||||
should.not.exist(exportData);
|
||||
errorStub.should.be.calledOnce();
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('exportFileName', function () {
|
||||
it('should return a correctly structured filename', function (done) {
|
||||
var settingsStub = sandbox.stub(settings, 'read').returns(
|
||||
new Promise.resolve({settings: [{value: 'testblog'}]})
|
||||
);
|
||||
|
||||
exporter.fileName().then(function (result) {
|
||||
should.exist(result);
|
||||
settingsStub.should.be.calledOnce();
|
||||
result.should.match(/^testblog\.ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}\.json$/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should return a correctly structured filename if settings is empty', function (done) {
|
||||
var settingsStub = sandbox.stub(settings, 'read').returns(
|
||||
new Promise.resolve()
|
||||
);
|
||||
|
||||
exporter.fileName().then(function (result) {
|
||||
should.exist(result);
|
||||
settingsStub.should.be.calledOnce();
|
||||
result.should.match(/^ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}\.json$/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should return a correctly structured filename if settings errors', function (done) {
|
||||
var settingsStub = sandbox.stub(settings, 'read').returns(
|
||||
new Promise.reject()
|
||||
);
|
||||
|
||||
exporter.fileName().then(function (result) {
|
||||
should.exist(result);
|
||||
settingsStub.should.be.calledOnce();
|
||||
result.should.match(/^ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}\.json$/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user