Added option to import clients and trusted domains

refs #9742, refs #8719

- make it possible to import more tables (optional)
  - available tables: clients, trusted domains
- by default we won't import these tables, you have to tell Ghost using `include` (same syntax on export)
- we won't announce this ability for now (stays hidden)
This commit is contained in:
kirrg001 2018-07-31 12:50:11 +02:00
parent 40c8eacd44
commit 75cc60c20a
8 changed files with 283 additions and 11 deletions

View File

@ -1,6 +1,7 @@
// # DB API
// API for DB operations
const Promise = require('bluebird'),
_ = require('lodash'),
pipeline = require('../lib/promise/pipeline'),
localUtils = require('./utils'),
exporter = require('../data/export'),
@ -86,7 +87,7 @@ db = {
options = options || {};
function importContent(options) {
return importer.importFromFile(options)
return importer.importFromFile(_.omit(options, 'include'), {include: options.include})
// NOTE: response can contain 2 objects if images are imported
.then((response) => {
return {
@ -98,6 +99,7 @@ db = {
tasks = [
localUtils.handlePermissions(docName, 'importContent'),
localUtils.convertOptions(exporter.EXCLUDED_TABLES, null, {forModel: false}),
importContent
];

View File

@ -0,0 +1,100 @@
const debug = require('ghost-ignition').debug('importer:clients'),
Promise = require('bluebird'),
_ = require('lodash'),
BaseImporter = require('./base'),
models = require('../../../../models');
class ClientsImporter extends BaseImporter {
constructor(allDataFromFile) {
super(allDataFromFile, {
modelName: 'Client',
dataKeyToImport: 'clients'
});
this.errorConfig = {
allowDuplicates: false,
returnDuplicates: true,
showNotFoundWarning: false
};
}
fetchExisting(modelOptions) {
return models.Client.findAll(_.merge({columns: ['id', 'slug']}, modelOptions))
.then((existingData) => {
this.existingData = existingData.toJSON();
});
}
beforeImport() {
debug('beforeImport');
return super.beforeImport();
}
doImport(options, importOptions = {}) {
debug('doImport', this.dataToImport.length);
let ops = [];
if (!importOptions.include || importOptions.include.indexOf(this.dataKeyToImport) === -1) {
return Promise.resolve().reflect();
}
_.each(this.dataToImport, (obj) => {
ops.push(models[this.modelName].findOne({slug: obj.slug}, options)
.then((client) => {
if (client) {
return models[this.modelName]
.edit(_.omit(obj, 'id'), Object.assign({id: client.id}, options))
.then((importedModel) => {
obj.model = {
id: importedModel.id
};
if (importOptions.returnImportedData) {
this.importedDataToReturn.push(importedModel.toJSON());
}
// for identifier lookup
this.importedData.push({
id: importedModel.id,
slug: importedModel.get('slug'),
originalSlug: obj.slug
});
return importedModel;
})
.catch((err) => {
return this.handleError(err, obj);
});
}
return models[this.modelName].add(obj, options)
.then((importedModel) => {
obj.model = {
id: importedModel.id
};
if (importOptions.returnImportedData) {
this.importedDataToReturn.push(importedModel.toJSON());
}
// for identifier lookup
this.importedData.push({
id: importedModel.id,
slug: importedModel.get('slug'),
originalSlug: obj.slug
});
return importedModel;
})
.catch((err) => {
return this.handleError(err, obj);
});
}).reflect());
});
return Promise.all(ops);
}
}
module.exports = ClientsImporter;

View File

@ -6,6 +6,8 @@ var _ = require('lodash'),
PostsImporter = require('./posts'),
TagsImporter = require('./tags'),
SettingsImporter = require('./settings'),
ClientsImporter = require('./clients'),
TrustedDomainsImporter = require('./trusted-domains'),
UsersImporter = require('./users'),
RolesImporter = require('./roles'),
importers = {},
@ -26,6 +28,8 @@ DataImporter = {
importers.subscribers = new SubscribersImporter(importData.data);
importers.posts = new PostsImporter(importData.data);
importers.settings = new SettingsImporter(importData.data);
importers.clients = new ClientsImporter(importData.data);
importers.trustedDomains = new TrustedDomainsImporter(importData.data);
return importData;
},

View File

@ -90,14 +90,6 @@ class SettingsImporter extends BaseImporter {
return Promise.all(ops);
}
/**
* We only update existing settings models.
* Nothing todo here.
*/
afterImport() {
return Promise.resolve();
}
}
module.exports = SettingsImporter;

View File

@ -0,0 +1,75 @@
const debug = require('ghost-ignition').debug('importer:clients'),
Promise = require('bluebird'),
_ = require('lodash'),
BaseImporter = require('./base');
class TrustedDomainsImporter extends BaseImporter {
constructor(allDataFromFile) {
super(allDataFromFile, {
modelName: 'ClientTrustedDomain',
dataKeyToImport: 'client_trusted_domains',
requiredExistingData: ['clients'],
requiredFromFile: ['clients'],
requiredImportedData: ['clients']
});
this.errorConfig = {
allowDuplicates: false,
returnDuplicates: true,
showNotFoundWarning: false
};
}
beforeImport() {
debug('beforeImport');
return super.beforeImport();
}
replaceIdentifiers(modelOptions, importOptions = {}) {
debug('replaceIdentifiers');
if (!importOptions.include || importOptions.include.indexOf(this.dataKeyToImport) === -1) {
return super.replaceIdentifiers(modelOptions, importOptions);
}
const randomClientId = this.requiredExistingData.clients[0].id;
_.each(this.dataToImport, (domainToImport, index) => {
let existingClient = _.find(this.requiredFromFile.clients, {id: domainToImport.client_id.toString()});
// CASE: client is in file, look if it was imported or updated
if (existingClient) {
existingClient = _.find(this.requiredImportedData.clients, {slug: existingClient.slug});
if (existingClient) {
this.dataToImport[index].client_id = existingClient.id;
}
} else {
existingClient = _.find(this.requiredExistingData.clients, {id: domainToImport.client_id.toString()});
if (!existingClient) {
this.dataToImport[index].client_id = randomClientId;
}
}
});
return super.replaceIdentifiers(modelOptions, importOptions);
}
generateIdentifier() {
return Promise.resolve();
}
doImport(options, importOptions = {}) {
debug('doImport', this.dataToImport.length);
if (!importOptions.include || importOptions.include.indexOf(this.dataKeyToImport) === -1) {
return Promise.resolve().reflect();
}
return super.doImport(options, importOptions);
}
}
module.exports = TrustedDomainsImporter;

View File

@ -354,8 +354,7 @@ _.extend(ImportManager.prototype, {
* @param {importOptions} importOptions to allow override of certain import features such as locking a user
* @returns {Promise}
*/
importFromFile: function (file, importOptions) {
importOptions = importOptions || {};
importFromFile: function (file, importOptions = {}) {
var self = this;
// Step 1: Handle converting the file to usable data

View File

@ -1909,6 +1909,65 @@ describe('Import (new test structure)', function () {
});
});
describe('import clients/trusted_domains', function () {
beforeEach(function doImport() {
return testUtils.initFixtures('roles', 'owner', 'settings', 'clients');
});
it('skips importing clients, trusted domains by default', function () {
return testUtils.fixtures.loadExportFixture('export-clients-domains')
.then((exported) => {
exportData = exported.db[0];
return dataImporter.doImport(exportData, importOptions);
})
.then(() => {
return models.Client.findOne({slug: 'ghost-something'}, testUtils.context.internal);
})
.then((model)=> {
should.not.exist(model);
return models.ClientTrustedDomain.findOne({trusted_domain: 'https://test.com'}, testUtils.context.internal);
})
.then(function (model) {
should.not.exist(model);
});
});
it('forward option to import clients, trusted domains', function () {
let somethingClient;
return testUtils.fixtures.loadExportFixture('export-clients-domains')
.then((exported) => {
exportData = exported.db[0];
return dataImporter.doImport(exportData, Object.assign({include: ['clients', 'client_trusted_domains']}, importOptions));
})
.then(() => {
return models.Client.findOne({slug: 'ghost-something'}, testUtils.context.internal);
})
.then((model)=> {
should.exist(model);
somethingClient = model;
return models.Client.findOne({slug: 'ghost-frontend'}, testUtils.context.internal);
})
.then((model) => {
should.exist(model);
model.get('secret').should.eql('11111');
return models.ClientTrustedDomain.findOne({trusted_domain: 'https://test.com'}, testUtils.context.internal);
})
.then(function (model) {
should.exist(model);
model.get('client_id').should.eql(testUtils.DataGenerator.forKnex.clients[0].id);
return models.ClientTrustedDomain.findOne({trusted_domain: 'https://example.com'}, testUtils.context.internal);
})
.then((model) => {
should.exist(model);
model.get('client_id').should.eql(somethingClient.id);
});
});
});
describe('authors', function () {
before(function doImport() {
// initialize the blog with some data

View File

@ -0,0 +1,41 @@
{
"db": [
{
"meta": {
"exported_on": 1504269105806,
"version": "1.25.3"
},
"data": {
"posts": [],
"posts_tags": [],
"tags": [],
"users": [],
"posts_authors": [],
"clients": [
{
"id": "59a952be7d79ed06b0d21127",
"slug": "ghost-something",
"name": "Something",
"secret": "678910"
},
{
"id": "59a952be7d79ed06b0d21128",
"slug": "ghost-frontend",
"name": "Frontend",
"secret": "11111"
}
],
"client_trusted_domains": [
{
"client_id": 1,
"trusted_domain": "https://test.com"
},
{
"client_id": "59a952be7d79ed06b0d21127",
"trusted_domain": "https://example.com"
}
]
}
}
]
}