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:
parent
cf529111f6
commit
14020f46d2
5 changed files with 60 additions and 16 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue