diff --git a/core/server/services/url/Resource.js b/core/server/services/url/Resource.js index 2e5e830ac9..893e4c0bc5 100644 --- a/core/server/services/url/Resource.js +++ b/core/server/services/url/Resource.js @@ -38,7 +38,7 @@ class Resource extends EventEmitter { } update(obj) { - this.data = obj; + Object.assign(this.data, obj); if (!this.isReserved()) { return; diff --git a/core/server/services/url/Resources.js b/core/server/services/url/Resources.js index 46fe8e3f50..373e332727 100644 --- a/core/server/services/url/Resources.js +++ b/core/server/services/url/Resources.js @@ -16,8 +16,32 @@ const resourcesConfig = [ modelOptions: { modelName: 'Post', filter: 'visibility:public+status:published+page:false', - reducedFields: true, + exclude: [ + 'title', + 'mobiledoc', + 'html', + 'plaintext', + 'amp', + 'codeinjection_head', + 'codeinjection_foot', + 'meta_title', + 'meta_description', + 'custom_excerpt', + 'og_image', + 'og_title', + 'og_description', + 'twitter_image', + 'twitter_title', + 'twitter_description', + 'custom_template', + 'feature_image', + 'locale' + ], withRelated: ['tags', 'authors'], + withRelatedPrimary: { + primary_tag: 'tags', + primary_author: 'authors' + }, withRelatedFields: { tags: ['tags.id', 'tags.slug'], authors: ['users.id', 'users.slug'] @@ -33,7 +57,31 @@ const resourcesConfig = [ type: 'pages', modelOptions: { modelName: 'Post', - reducedFields: true, + exclude: [ + 'title', + 'mobiledoc', + 'html', + 'plaintext', + 'amp', + 'codeinjection_head', + 'codeinjection_foot', + 'meta_title', + 'meta_description', + 'custom_excerpt', + 'og_image', + 'og_title', + 'og_description', + 'twitter_image', + 'twitter_title', + 'twitter_description', + 'custom_template', + 'feature_image', + 'locale', + 'tags', + 'authors', + 'primary_tag', + 'primary_author' + ], filter: 'visibility:public+status:published+page:true' }, events: { @@ -47,7 +95,11 @@ const resourcesConfig = [ keep: ['id', 'slug', 'updated_at', 'created_at'], modelOptions: { modelName: 'Tag', - reducedFields: true, + exclude: [ + 'description', + 'meta_title', + 'meta_description' + ], filter: 'visibility:public' }, events: { @@ -60,7 +112,17 @@ const resourcesConfig = [ type: 'users', modelOptions: { modelName: 'User', - reducedFields: true, + exclude: [ + 'bio', + 'website', + 'location', + 'facebook', + 'twitter', + 'accessibility', + 'meta_title', + 'meta_description', + 'tour' + ], filter: 'visibility:public' }, events: { @@ -161,7 +223,44 @@ class Resources { } _onResourceAdded(type, model) { - const resource = new Resource(type, model.toJSON()); + const resourceConfig = _.find(resourcesConfig, {type: type}); + const exclude = resourceConfig.modelOptions.exclude; + const withRelatedFields = resourceConfig.modelOptions.withRelatedFields; + const obj = _.omit(model.toJSON(), exclude); + + if (withRelatedFields) { + _.each(withRelatedFields, (fields, key) => { + if (!obj[key]) { + return; + } + + obj[key] = _.map(obj[key], (relation) => { + const relationToReturn = {}; + + _.each(fields, (field) => { + const fieldSanitized = field.replace(/^\w+./, ''); + relationToReturn[fieldSanitized] = relation[fieldSanitized]; + }); + + return relationToReturn; + }); + }); + + const withRelatedPrimary = resourceConfig.modelOptions.withRelatedPrimary; + + if (withRelatedPrimary) { + _.each(withRelatedPrimary, (relation, primaryKey) => { + if (!obj[primaryKey] || !obj[relation]) { + return; + } + + const targetTagKeys = Object.keys(obj[relation].find((item) => {return item.id === obj[primaryKey].id;})); + obj[primaryKey] = _.pick(obj[primaryKey], targetTagKeys); + }); + } + } + + const resource = new Resource(type, obj); debug('_onResourceAdded', type); this.data[type].push(resource); @@ -195,7 +294,44 @@ class Resources { this.data[type].every((resource) => { if (resource.data.id === model.id) { - resource.update(model.toJSON()); + const resourceConfig = _.find(resourcesConfig, {type: type}); + const exclude = resourceConfig.modelOptions.exclude; + const withRelatedFields = resourceConfig.modelOptions.withRelatedFields; + const obj = _.omit(model.toJSON(), exclude); + + if (withRelatedFields) { + _.each(withRelatedFields, (fields, key) => { + if (!obj[key]) { + return; + } + + obj[key] = _.map(obj[key], (relation) => { + const relationToReturn = {}; + + _.each(fields, (field) => { + const fieldSanitized = field.replace(/^\w+./, ''); + relationToReturn[fieldSanitized] = relation[fieldSanitized]; + }); + + return relationToReturn; + }); + }); + + const withRelatedPrimary = resourceConfig.modelOptions.withRelatedPrimary; + + if (withRelatedPrimary) { + _.each(withRelatedPrimary, (relation, primaryKey) => { + if (!obj[primaryKey] || !obj[relation]) { + return; + } + + const targetTagKeys = Object.keys(obj[relation].find((item) => {return item.id === obj[primaryKey].id;})); + obj[primaryKey] = _.pick(obj[primaryKey], targetTagKeys); + }); + } + } + + resource.update(obj); // CASE: pretend it was added if (!resource.isReserved()) { @@ -238,7 +374,7 @@ class Resources { return; } - delete this.data[type][index]; + this.data[type].splice(index, 1); resource.remove(); } diff --git a/core/test/unit/services/url/Resources_spec.js b/core/test/unit/services/url/Resources_spec.js index d2bcada03f..b6059e1656 100644 --- a/core/test/unit/services/url/Resources_spec.js +++ b/core/test/unit/services/url/Resources_spec.js @@ -83,13 +83,47 @@ describe('Unit: services/url/Resources', function () { queue.start.callsFake(function (options) { options.event.should.eql('added'); + const obj = _.find(resources.data.posts, {data: {slug: 'test-1234'}}).data; + + Object.keys(obj).should.eql([ + 'id', + 'uuid', + 'slug', + 'featured', + 'page', + 'status', + 'visibility', + 'created_at', + 'updated_at', + 'published_at', + 'published_by', + 'created_by', + 'updated_by', + 'tags', + 'authors', + 'author', + 'primary_author', + 'primary_tag', + 'url', + 'comment_id' + ]); + should.exist(resources.getByIdAndType(options.eventData.type, options.eventData.id)); + obj.tags.length.should.eql(1); + Object.keys(obj.tags[0]).should.eql(['id', 'slug']); + obj.authors.length.should.eql(1); + Object.keys(obj.authors[0]).should.eql(['id', 'slug']); + should.exist(obj.primary_author); + Object.keys(obj.primary_author).should.eql(['id', 'slug']); + should.exist(obj.primary_tag); + Object.keys(obj.primary_tag).should.eql(['id', 'slug']); done(); }); models.Post.add({ - title: 'test', - status: 'published' + slug: 'test-1234', + status: 'published', + tags: [{slug: 'tag-1', name: 'tag-name'}] }, testUtils.context.owner) .then(function () { onEvents['post.published'](emitEvents['post.published']); @@ -110,12 +144,12 @@ describe('Unit: services/url/Resources', function () { randomResource.reserve(); randomResource.addListener('updated', function () { - randomResource.data.title.should.eql('new title, wow'); + randomResource.data.slug.should.eql('tada'); done(); }); models.Post.edit({ - title: 'new title, wow' + slug: 'tada' }, _.merge({id: randomResource.data.id}, testUtils.context.owner)) .then(function () { onEvents['post.published.edited'](emitEvents['post.published.edited']); @@ -132,19 +166,62 @@ describe('Unit: services/url/Resources', function () { queue.start.callsFake(function (options) { options.event.should.eql('init'); - const randomResource = resources.getAll().posts[Math.floor(Math.random() * (resources.getAll().posts.length - 0) + 0)]; + const resourceToUpdate = _.find(resources.getAll().posts, (resource) => { + if (resource.data.tags.length && resource.data.authors.length) { + return true; + } - randomResource.update = sandbox.stub(); + return false; + }); + + sandbox.spy(resourceToUpdate, 'update'); queue.start.callsFake(function (options) { options.event.should.eql('added'); - randomResource.update.calledOnce.should.be.true(); + + resourceToUpdate.update.calledOnce.should.be.true(); + resourceToUpdate.data.slug.should.eql('eins-zwei'); + + const obj = _.find(resources.data.posts, {data: {id: resourceToUpdate.data.id}}).data; + + Object.keys(obj).should.eql([ + 'id', + 'uuid', + 'slug', + 'featured', + 'page', + 'status', + 'visibility', + 'created_at', + 'created_by', + 'updated_at', + 'updated_by', + 'published_at', + 'published_by', + 'tags', + 'authors', + 'author', + 'primary_author', + 'primary_tag', + 'url', + 'comment_id' + ]); + + should.exist(obj.tags); + Object.keys(obj.tags[0]).should.eql(['id', 'slug']); + should.exist(obj.authors); + Object.keys(obj.authors[0]).should.eql(['id', 'slug']); + should.exist(obj.primary_author); + Object.keys(obj.primary_author).should.eql(['id', 'slug']); + should.exist(obj.primary_tag); + Object.keys(obj.primary_tag).should.eql(['id', 'slug']); + done(); }); models.Post.edit({ - title: 'new title, wow' - }, _.merge({id: randomResource.data.id}, testUtils.context.owner)) + slug: 'eins-zwei' + }, _.merge({id: resourceToUpdate.data.id}, testUtils.context.owner)) .then(function () { onEvents['post.published.edited'](emitEvents['post.published.edited']); })