🐛 Fixed import for tag without slugs that belongs to a post (#10917)

closes #10785

- The behavior for tags will now be similar to posts' one described in the docs
- "The only strictly required field when importing posts is the title. Ghost will automatically generate slugs and set every other field to the default or empty."
- The breaking change was introduced with: 68d8154d4f (diff-e712df50c0dc7cf33746eeff0564003cR97) (assumed there's always slug in the imported object which is not true)
- Added originalIdMap to the importer base class to track id
substitution so it can be used when dealing with relational resource
updates
- Removed explicit use of 'this.stripProperties(['id']);' in
beforeImport of base class because we need to assign and remove the id
property in the same place to track this change
- Only calling 'this.stripProperties(['id']);' in
settings/trusted_domain imports as the method won't be called otherwise
- Expanded regression tests with new supported import case
This commit is contained in:
Naz Gargol 2019-07-16 12:01:44 +02:00 committed by GitHub
parent c2e4d0f6be
commit 9dcc17a017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 4 deletions

View File

@ -21,6 +21,7 @@ class Base {
this.dataKeyToImport = options.dataKeyToImport;
this.dataToImport = _.cloneDeep(allDataFromFile[this.dataKeyToImport] || []);
this.originalIdMap = {};
this.importedDataToReturn = [];
this.importedData = [];
@ -53,7 +54,7 @@ class Base {
}
/**
* Never ever import these attributes!
* Strips attributes of the object
*/
stripProperties(properties) {
_.each(this.dataToImport, (obj) => {
@ -86,7 +87,13 @@ class Base {
generateIdentifier() {
_.each(this.dataToImport, (obj) => {
obj.id = ObjectId.generate();
const newId = ObjectId.generate();
if (obj.id) {
this.originalIdMap[newId] = obj.id;
}
obj.id = newId;
});
}
@ -95,7 +102,6 @@ class Base {
}
beforeImport() {
this.stripProperties(['id']);
this.sanitizeValues();
this.generateIdentifier();
return Promise.resolve();
@ -317,6 +323,7 @@ class Base {
// for identifier lookup
this.importedData.push({
id: importedModel.id,
originalId: this.originalIdMap[importedModel.id],
slug: importedModel.get('slug'),
originalSlug: obj.slug,
email: importedModel.get('email')

View File

@ -100,7 +100,11 @@ class PostsImporter extends BaseImporter {
// CASE: search through imported data.
// EDGE CASE: uppercase tag slug was imported and auto modified
let importedObject = _.find(this.requiredImportedData[tableName], {originalSlug: objectInFile.slug});
let importedObject = null;
if (objectInFile.id) {
importedObject = _.find(this.requiredImportedData[tableName], {originalId: objectInFile.id});
}
if (importedObject) {
this.dataToImport[postIndex][targetProperty][index].id = importedObject.id;

View File

@ -109,6 +109,7 @@ class SettingsImporter extends BaseImporter {
}
generateIdentifier() {
this.stripProperties(['id']);
return Promise.resolve();
}

View File

@ -58,6 +58,7 @@ class TagsImporter extends BaseImporter {
// for identifier lookup
this.importedData.push({
id: importedModel.id,
originalId: this.originalIdMap[importedModel.id],
slug: importedModel.get('slug'),
originalSlug: obj.slug
});

View File

@ -75,6 +75,7 @@ class TrustedDomainsImporter extends BaseImporter {
}
generateIdentifier() {
this.stripProperties(['id']);
return Promise.resolve();
}

View File

@ -809,6 +809,55 @@ describe('Integration: Importer', function () {
});
});
it('can handle related tags with missing optional fields', function () {
const exportData = exportedLatestBody().db[0];
exportData.data.posts[0] = testUtils.DataGenerator.forKnex.createPost();
// NOTE: not including slug, description etc. fields as the only required field
// to handle the import of tags is 'name'
exportData.data.tags[0] = {
id: ObjectId.generate(),
name: 'first tag'
};
exportData.data.tags[1] = {
id: ObjectId.generate(),
name: 'second tag'
};
exportData.data.tags[2] = {
id: ObjectId.generate(),
name: 'third tag'
};
exportData.data.posts_tags = [
testUtils.DataGenerator.forKnex.createPostsTags(exportData.data.posts[0].id, exportData.data.tags[0].id),
testUtils.DataGenerator.forKnex.createPostsTags(exportData.data.posts[0].id, exportData.data.tags[1].id),
testUtils.DataGenerator.forKnex.createPostsTags(exportData.data.posts[0].id, exportData.data.tags[2].id)
];
return dataImporter.doImport(exportData, importOptions)
.then(function (imported) {
imported.problems.length.should.eql(0);
return Promise.all([
models.Tag.findPage(Object.assign({order: 'slug ASC'}, testUtils.context.internal)),
models.Post.findPage(Object.assign({withRelated: ['tags']}, testUtils.context.internal))
]);
}).then(function (result) {
const tags = result[0].data.map(model => model.toJSON());
const posts = result[1].data.map(model => model.toJSON());
posts.length.should.eql(1);
tags.length.should.eql(3);
posts[0].tags.length.should.eql(3);
tags[0].name.should.eql('first tag');
tags[0].slug.should.eql('first-tag');
tags[1].name.should.eql('second tag');
tags[2].name.should.eql('third tag');
});
});
it('can handle uppercase tags', function () {
const exportData = exportedLatestBody().db[0];