0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-08 02:52:39 -05:00

Handle comment replies in the browse API

refs https://github.com/TryGhost/Team/issues/1664
- replies are a sub-record inside of a comment
This commit is contained in:
Hannah Wolfe 2022-07-06 17:47:21 +02:00 committed by Simon Backx
parent cf529111f6
commit 14020f46d2
5 changed files with 60 additions and 16 deletions

View file

@ -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', 'likes'];
const ALLOWED_INCLUDES = ['post', 'member', 'likes', 'replies'];
const UNSAFE_ATTRS = ['status'];
const messages = {
@ -171,7 +171,7 @@ module.exports = {
};
const existing = await models.CommentLike.findOne(data, frame.options);
if (existing) {
throw new errors.BadRequestError({
message: tpl(messages.alreadyLiked)

View file

@ -16,7 +16,7 @@ const memberFields = [
'avatar_image'
];
module.exports = (model, frame) => {
const commentMapper = (model, frame) => {
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
const response = _.pick(jsonModel, commentFields);
@ -33,6 +33,11 @@ module.exports = (model, frame) => {
response.likes_count = 0;
}
if (jsonModel.replies) {
response.replies = jsonModel.replies.map(reply => commentMapper(reply, frame));
}
// todo
response.liked = false;
if (jsonModel.likes && frame.original.context.member && frame.original.context.member.id) {
const id = frame.original.context.member.id;
@ -41,3 +46,5 @@ module.exports = (model, frame) => {
return response;
};
module.exports = commentMapper;

View file

@ -35,6 +35,10 @@ const Comment = ghostBookshelf.Model.extend({
return this.hasMany('CommentLike', 'comment_id');
},
replies() {
return this.hasMany('Comment', 'parent_id');
},
emitChange: function emitChange(event, options) {
const eventToTrigger = 'comment' + '.' + event;
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
@ -124,7 +128,7 @@ const Comment = ghostBookshelf.Model.extend({
defaultRelations: function defaultRelations(methodName, options) {
// @todo: the default relations are not working for 'add' when we add it below
if (['findAll', 'findPage', 'edit', 'findOne'].indexOf(methodName) !== -1) {
options.withRelated = _.union(['member', 'likes'], options.withRelated || []);
options.withRelated = _.union(['member', 'likes', 'replies', 'replies.member', 'replies.likes'], options.withRelated || []);
}
return options;

View file

@ -15,8 +15,26 @@ Object {
"bio": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "Mr Egg",
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
"uuid": "f6f91461-d7d8-4a3f-aa5d-8e582c40b340",
},
"replies": Array [
Object {
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"edited_at": null,
"html": "<p>Really original</p>",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"liked": Any<Boolean>,
"likes_count": Any<Number>,
"member": Object {
"avatar_image": null,
"bio": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": null,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
"status": "published",
},
],
"status": "published",
},
Object {
@ -33,6 +51,7 @@ Object {
"name": null,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
"replies": Array [],
"status": "published",
},
],
@ -53,7 +72,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": "704",
"content-length": "1035",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
@ -108,6 +127,7 @@ Object {
"name": null,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
"replies": Array [],
"status": "published",
},
],
@ -149,7 +169,7 @@ exports[`Comments API when authenticated Can like a comment 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": "315",
"content-length": "328",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
@ -183,6 +203,7 @@ Object {
"name": null,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
"replies": Array [],
"status": "published",
},
],
@ -193,7 +214,7 @@ exports[`Comments API when authenticated Can like a comment 5: [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": "314",
"content-length": "327",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
@ -227,6 +248,7 @@ Object {
"name": null,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
"replies": Array [],
"status": "published",
},
],
@ -237,7 +259,7 @@ exports[`Comments API when authenticated Can remove a like 3: [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": "315",
"content-length": "328",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",

View file

@ -4,12 +4,12 @@ require('should');
let membersAgent, membersService, postId, commentId;
const commentMatcher = {
const commentMatcherNoMember = {
id: anyObjectId,
created_at: anyISODateTime
};
const commentMatcherWithMember = {
const commentMatcher = {
id: anyObjectId,
created_at: anyISODateTime,
member: {
@ -20,6 +20,17 @@ const commentMatcherWithMember = {
liked: anyBoolean
};
const commentMatcherWithReply = {
id: anyObjectId,
created_at: anyISODateTime,
member: {
id: anyObjectId
},
likes_count: anyNumber,
liked: anyBoolean,
replies: [commentMatcher]
};
describe('Comments API', function () {
before(async function () {
membersAgent = await agentProvider.getMembersAPIAgent();
@ -55,7 +66,7 @@ describe('Comments API', function () {
location: anyLocationFor('comments')
})
.matchBodySnapshot({
comments: [commentMatcher]
comments: [commentMatcherNoMember]
});
// Save for other tests
commentId = body.comments[0].id;
@ -69,7 +80,7 @@ describe('Comments API', function () {
etag: anyEtag
})
.matchBodySnapshot({
comments: new Array(2).fill(commentMatcherWithMember)
comments: [commentMatcherWithReply, commentMatcher]
});
});
@ -82,7 +93,7 @@ describe('Comments API', function () {
etag: anyEtag
})
.matchBodySnapshot({
comments: new Array(1).fill(commentMatcherWithMember)
comments: new Array(1).fill(commentMatcher)
})
.expect(({body}) => {
body.comments[0].liked.should.eql(false);
@ -105,7 +116,7 @@ describe('Comments API', function () {
etag: anyEtag
})
.matchBodySnapshot({
comments: new Array(1).fill(commentMatcherWithMember)
comments: new Array(1).fill(commentMatcher)
})
.expect(({body}) => {
body.comments[0].liked.should.eql(true);
@ -145,7 +156,7 @@ describe('Comments API', function () {
etag: anyEtag
})
.matchBodySnapshot({
comments: new Array(1).fill(commentMatcherWithMember)
comments: new Array(1).fill(commentMatcher)
})
.expect(({body}) => {
body.comments[0].liked.should.eql(false);