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),