diff --git a/core/server/api/endpoints/comments-comments.js b/core/server/api/endpoints/comments-comments.js index adb3497042..443a061de1 100644 --- a/core/server/api/endpoints/comments-comments.js +++ b/core/server/api/endpoints/comments-comments.js @@ -2,7 +2,7 @@ const Promise = require('bluebird'); const tpl = require('@tryghost/tpl'); const errors = require('@tryghost/errors'); const models = require('../../models'); -const ALLOWED_INCLUDES = ['post', 'member']; +const ALLOWED_INCLUDES = ['post', 'member', 'likes']; const UNSAFE_ATTRS = ['status']; const messages = { diff --git a/core/server/api/endpoints/utils/serializers/output/mappers/comments.js b/core/server/api/endpoints/utils/serializers/output/mappers/comments.js index b971058a4a..3d859f1e4e 100644 --- a/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +++ b/core/server/api/endpoints/utils/serializers/output/mappers/comments.js @@ -26,5 +26,14 @@ module.exports = (model, frame) => { response.member = null; } + if (jsonModel.likes) { + response.likes_count = jsonModel.likes.length; + } else { + response.likes_count = 0; + } + + // todo + response.liked = false; + return response; }; diff --git a/core/server/models/comment.js b/core/server/models/comment.js index 69a07cabab..338209c76b 100644 --- a/core/server/models/comment.js +++ b/core/server/models/comment.js @@ -31,6 +31,10 @@ const Comment = ghostBookshelf.Model.extend({ return this.belongsTo('Comment', 'parent_id'); }, + likes() { + return this.hasMany('CommentLike', 'comment_id'); + }, + emitChange: function emitChange(event, options) { const eventToTrigger = 'comment' + '.' + event; ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options); @@ -100,6 +104,21 @@ const Comment = ghostBookshelf.Model.extend({ } return hasMemberPermission; + }, + + /** + * We have to ensure consistency. If you listen on model events (e.g. `member.added`), you can expect that you always + * receive all fields including relations. Otherwise you can't rely on a consistent flow. And we want to avoid + * that event listeners have to re-fetch a resource. This function is used in the context of inserting + * and updating resources. We won't return the relations by default for now. + */ + defaultRelations: function defaultRelations(methodName, options) { + // @todo: the default relations are not working for 'add' when we add it below + if (['findAll', 'findPage', 'edit'].indexOf(methodName) !== -1) { + options.withRelated = _.union(['member', 'likes'], options.withRelated || []); + } + + return options; } }); diff --git a/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap b/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap index c931c36fbb..0a5b72ab78 100644 --- a/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap +++ b/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap @@ -8,6 +8,8 @@ Object { "edited_at": null, "html": "

First.

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "liked": Any, + "likes_count": Any, "member": Object { "avatar_image": null, "bio": null, @@ -21,6 +23,8 @@ Object { "edited_at": null, "html": "

Really original

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "liked": Any, + "likes_count": Any, "member": Object { "avatar_image": null, "bio": null, @@ -34,6 +38,8 @@ Object { "edited_at": null, "html": "This is a message", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "liked": Any, + "likes_count": Any, "member": Object { "avatar_image": null, "bio": null, @@ -60,7 +66,7 @@ exports[`Comments API when authenticated Can browse all comments of a post 2: [h Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "782", + "content-length": "872", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -76,6 +82,8 @@ Object { "edited_at": null, "html": "This is a message", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "liked": false, + "likes_count": 0, "member": null, "status": "published", }, @@ -87,7 +95,7 @@ exports[`Comments API when authenticated Can comment on a post 2: [headers] 1`] Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "167", + "content-length": "197", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, diff --git a/test/e2e-api/members-comments/comments.test.js b/test/e2e-api/members-comments/comments.test.js index 45477f951c..cd465fc9a8 100644 --- a/test/e2e-api/members-comments/comments.test.js +++ b/test/e2e-api/members-comments/comments.test.js @@ -1,5 +1,5 @@ const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework'); -const {anyEtag, anyObjectId, anyLocationFor, anyISODateTime, anyUuid} = matchers; +const {anyEtag, anyObjectId, anyLocationFor, anyISODateTime, anyUuid, anyNumber, anyBoolean} = matchers; let membersAgent, membersService, postId, commentId; @@ -13,7 +13,9 @@ const commentMatcherWithMember = { created_at: anyISODateTime, member: { id: anyObjectId - } + }, + likes_count: anyNumber, + liked: anyBoolean }; describe('Comments API', function () { @@ -58,8 +60,8 @@ describe('Comments API', function () { }); it('Can browse all comments of a post', async function () { - await membersAgent - .get(`/api/comments/?filter=post_id:${postId}&include=member`) + const {body} = await membersAgent + .get(`/api/comments/?filter=post_id:${postId}`) .expectStatus(200) .matchHeaderSnapshot({ etag: anyEtag diff --git a/test/utils/e2e-framework.js b/test/utils/e2e-framework.js index a023b1e05c..5231ccae4f 100644 --- a/test/utils/e2e-framework.js +++ b/test/utils/e2e-framework.js @@ -313,6 +313,7 @@ module.exports = { } }, matchers: { + anyBoolean: any(Boolean), anyString: any(String), anyArray: any(Array), anyNumber: any(Number),