mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
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:
parent
40c8eacd44
commit
75cc60c20a
8 changed files with 283 additions and 11 deletions
|
@ -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
|
||||
];
|
||||
|
||||
|
|
100
core/server/data/importer/importers/data/clients.js
Normal file
100
core/server/data/importer/importers/data/clients.js
Normal 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;
|
|
@ -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;
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
75
core/server/data/importer/importers/data/trusted-domains.js
Normal file
75
core/server/data/importer/importers/data/trusted-domains.js
Normal 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;
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
41
core/test/utils/fixtures/export/export-clients-domains.json
Normal file
41
core/test/utils/fixtures/export/export-clients-domains.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Add table
Reference in a new issue