🐛 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:
parent
c2e4d0f6be
commit
9dcc17a017
@ -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')
|
||||
|
@ -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;
|
||||
|
@ -109,6 +109,7 @@ class SettingsImporter extends BaseImporter {
|
||||
}
|
||||
|
||||
generateIdentifier() {
|
||||
this.stripProperties(['id']);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -75,6 +75,7 @@ class TrustedDomainsImporter extends BaseImporter {
|
||||
}
|
||||
|
||||
generateIdentifier() {
|
||||
this.stripProperties(['id']);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user