Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add graphql operations for comments #176

Merged
merged 16 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion planning/v1/ROLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ _TODO: Consider combining reaction permissions in comment roles_
| comment.superadmin | true | true | true | true | true | true | true |

---

## Issue Section

| Role Name | issue.read.unpublished | issue.read.admin | issue.write.new | issue.write.all | issue.write.delete |
Expand Down
2 changes: 2 additions & 0 deletions server/config/apolloServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const MediaDataSources = require('../schema/media/media.datasources');
const IssueDataSources = require('../schema/issue/issue.datasources');
const CategoryMapDataSources = require('../schema/categoryMap/categoryMap.datasources');
const ArticleDataSources = require('../schema/article/article.datasources');
const CommentDataSources = require('../schema/comment/comment.datasources');
const CompanyDataSources = require('../schema/company/company.datasources');
const LiveDataSources = require('../schema/live/live.datasources');

Expand Down Expand Up @@ -59,6 +60,7 @@ const apolloServer = (httpServer) =>
Article: ArticleDataSources(),
Company: CompanyDataSources(),
Live: LiveDataSources(),
Comment: CommentDataSources(),
},
}),
debug: !process.env.NODE_ENV || process.env.NODE_ENV === 'development',
Expand Down
3 changes: 1 addition & 2 deletions server/schema/article/article.resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,8 @@
createArticle: async (
_parent,
{ articleType, title, authors, photographers, designers, tech, categoryNumbers },
{ mid, session, authToken, decodedToken, API: { Article, CategoryMap } },
{ fieldNodes }
{ mid, session, authToken, decodedToken, API: { Article, CategoryMap } }
) => {

Check failure on line 352 in server/schema/article/article.resolver.js

View workflow job for this annotation

GitHub Actions / eslint / ESLint Check

'fieldNodes' is defined but never used. Allowed unused args must match /^_$/u
try {
if (!UserPermission.exists(session, authToken, decodedToken, 'article.write.new')) {
throw APIError('FORBIDDEN', null, {
Expand Down
101 changes: 101 additions & 0 deletions server/schema/comment/comment.datasources.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const DataLoader = require('dataloader');
const { APIError } = require('../../utils/exception');
const UserSession = require('../../utils/userAuth/session');
const userModel = require('../user/user.model');
const CommentModel = require('./comment.model');

const findByID = () =>
new DataLoader(
async (data) => {
try {
const _comments = await CommentModel.find({ _id: { $in: data.map(({ id }) => id) } });

const _returnComments = data.map(({ id, permission, mid }) => {
const _comment = _comments.find((comment) => comment._id.toString() === id.toString());
return _comment && _comment.approved ? _comment : permission || mid === _comment.createdBy ? _comment : null;
});
return _returnComments;
} catch (error) {
throw APIError(null, error);
}
},
{
batchScheduleFn: (cb) => setTimeout(cb, 100),
}
);

const findAll = (offset, limit, permission, mid) => {
// Get approved comments if the user does not have permission to read unapproved comments and the user is not the author
// Get all comments if the user has permission to read unapproved comments or the user is the author
const query = permission ? {} : { $or: [{ approved: true }, { approved: false, createdBy: mid }] };
return CommentModel.find(query).sort({ createdAt: 'desc' }).skip(offset).limit(limit);
};

const countNumberOfComments = (parentID, parentModel) =>
CommentModel.countDocuments({
'parent.reference': parentID,
'parent.model': parentModel,
approved: true,
});

const create = async (authorID, content, parentID, parentType, session, authToken, mid, approved) => {
try {
const _author = await userModel.findById(authorID);
if (!_author) {
throw APIError('NOT FOUND', null, 'Invalid Author ID');
}

const [_comment] = await CommentModel.create([
{
content,
author: {
name: _author.fullName,
reference: authorID,
},
parent: {
reference: parentID,
model: parentType,
},
approved: approved || false,
createdBy: UserSession.valid(session, authToken) ? mid : null,
},
]);

return _comment;
} catch (error) {
throw APIError(null, error);
}
};

const updateContent = async (id, content, session, authToken, mid) => {
try {
const _comment = await CommentModel.findByIdAndUpdate(
id,
{
content,
updatedBy: UserSession.valid(session, authToken) ? mid : null,
},
{ new: true }
);

return _comment;
} catch (error) {
throw APIError(null, error);
}
};

const approve = (id) => CommentModel.findByIdAndUpdate(id, { approved: true }, { new: true });

const remove = (id) => CommentModel.findByIdAndDelete(id);

const CommentDataSources = () => ({
findAll,
findByID: findByID(),
countNumberOfComments,
create,
approve,
updateContent,
remove,
});

module.exports = CommentDataSources;
34 changes: 34 additions & 0 deletions server/schema/comment/comment.enum.types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const {
// GraphQLObjectType,
// GraphQLScalarType,
// GraphQLUnionType,
// GraphQLInputObjectType,
GraphQLEnumType,
// GraphQLInterfaceType,
// GraphQLSchema,
// GraphQLNonNull,
// GraphQLError,
// GraphQLList,
// GraphQLString,
// GraphQLID,
// GraphQLBoolean,
// GraphQLInt,
// GraphQLFloat,
// GraphQLDate,
// GraphQLTime,
// GraphQLDateTime,
// GraphQLJSON,
// GraphQLJSONObject,
} = require('../scalars');

const CommentParentModelEmum = new GraphQLEnumType({
name: 'CommentParentModelEnum',
values: {
ARTICLE: { value: 'Article' },
COMMENT: { value: 'Comment' },
},
});

module.exports = {
CommentParentModelEmum,
};
20 changes: 11 additions & 9 deletions server/schema/comment/comment.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @since 0.1.0
*/

const { Schema, model } = require('mongoose');
const { Schema, model, Model: _Model } = require('mongoose');
Shurtu-gal marked this conversation as resolved.
Show resolved Hide resolved

/**
* @description The schema definition for Comment Model
Expand All @@ -19,13 +19,10 @@ const { Schema, model } = require('mongoose');
*/
const CommentSchema = new Schema(
{
// TODO: update content with final structure
content: [
{
type: Object,
required: true,
},
],
content: {
type: String,
required: true,
},
author: {
name: {
type: String,
Expand All @@ -38,6 +35,11 @@ const CommentSchema = new Schema(
trim: true,
},
},
approved: {
type: Boolean,
required: false,
default: false,
},
parent: {
model: {
type: String,
Expand Down Expand Up @@ -81,6 +83,6 @@ const CommentSchema = new Schema(
* @description Generated Comment Model
* @constant CommentModel
*
* @type {model}
* @type {_Model}
*/
module.exports = model('Comment', CommentSchema);
68 changes: 68 additions & 0 deletions server/schema/comment/comment.mutation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const {
GraphQLObjectType,
// GraphQLScalarType,
// GraphQLUnionType,
// GraphQLInputObjectType,
// GraphQLEnumType,
// GraphQLInterfaceType,
// GraphQLSchema,
GraphQLNonNull,
// GraphQLError,
// GraphQLList,
GraphQLString,
GraphQLID,
// GraphQLBoolean,
// GraphQLInt,
// GraphQLFloat,
// GraphQLDate,
// GraphQLTime,
// GraphQLDateTime,
// GraphQLJSON,
// GraphQLJSONObject,
} = require('../scalars');
const { CommentParentModelEmum } = require('./comment.enum.types');
const { createComment, deleteComment, updateCommentContent, approveComment } = require('./comment.resolver');

const CommentType = require('./comment.type');

module.exports = new GraphQLObjectType({
name: 'CommentMutation',
fields: {
createComment: {
description: 'Create a comment',
type: CommentType,
args: {
content: { type: new GraphQLNonNull(GraphQLString) },
authorID: { type: new GraphQLNonNull(GraphQLID) },
parentID: { type: new GraphQLNonNull(GraphQLID) },
parentType: { type: new GraphQLNonNull(CommentParentModelEmum) },
},
resolve: createComment,
},
approveComment: {
description: 'Approve a comment',
type: CommentType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
},
resolve: approveComment,
},
updateCommentContent: {
description: 'Update Comment by Id',
type: CommentType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
content: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: updateCommentContent,
},
deleteComment: {
description: 'Delete comment by Id',
type: CommentType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
},
resolve: deleteComment,
},
},
});
77 changes: 77 additions & 0 deletions server/schema/comment/comment.query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const {
GraphQLObjectType,
// GraphQLScalarType,
// GraphQLUnionType,
// GraphQLInputObjectType,
// GraphQLEnumType,
// GraphQLInterfaceType,
// GraphQLSchema,
GraphQLNonNull,
// GraphQLError,
GraphQLList,
// GraphQLString,
GraphQLID,
// GraphQLBoolean,
GraphQLInt,
// GraphQLFloat,
// GraphQLDate,
// GraphQLTime,
// GraphQLDateTime,
// GraphQLJSON,
// GraphQLJSONObject,
} = require('../scalars');
const { CommentParentModelEmum } = require('./comment.enum.types');
const { getListOfComments, getCommentById, countOfComments } = require('./comment.resolver');

const CommentType = require('./comment.type');

module.exports = new GraphQLObjectType({
name: 'CommentQuery',
fields: {
getListOfComments: {
Shurtu-gal marked this conversation as resolved.
Show resolved Hide resolved
description: 'Retrieves comments for given list of ids (default all) in descending order of creation time',
type: new GraphQLList(new GraphQLNonNull(CommentType)),
Shurtu-gal marked this conversation as resolved.
Show resolved Hide resolved
args: {
ids: {
description: 'List of Ids of comments to be retrieved',
type: new GraphQLList(new GraphQLNonNull(GraphQLID)),
Shurtu-gal marked this conversation as resolved.
Show resolved Hide resolved
},
limit: {
description: 'No. of Comments to be retrieved',
type: GraphQLInt,
},
offset: {
description: 'No. of Comments to be skipped | pagination',
type: GraphQLInt,
},
},
resolve: getListOfComments,
},
getCommentById: {
description: 'Retrieves single comment based on id',
type: CommentType,
args: {
id: {
description: 'The id of comment to be retrieved',
type: new GraphQLNonNull(GraphQLID),
},
},
resolve: getCommentById,
},
countOfCommentsByParent: {
description: 'The number of comments on a article/comment',
type: GraphQLInt,
args: {
id: {
description: 'Id of article or comment',
type: new GraphQLNonNull(GraphQLID),
},
parentType: {
description: 'Type of parent',
type: new GraphQLNonNull(CommentParentModelEmum),
},
},
resolve: countOfComments,
},
},
});
Loading
Loading