Skip to content

Commit

Permalink
feat: add graphql operations for comments (#176)
Browse files Browse the repository at this point in the history
* feat: add graphql operations for comment

* feat: add roles for comments

* chore: prettier and eslint check

* fix: update roles for comment

* feat: add operations for comment content

* feat: add comment parent model enum

* fix: remove redundant transactions,
 * fix: add null check for author
 * chore: prettier check

* fix: refactor create comment
 * remove redundant transactions
 * add destructuring
 * add null check for author

* fix: remove redundant authorID

* fix: make required arguments non-null

* fix: add null check in getting list of comments

* fix: remove update author mutation

* fix: remove update author import

* feat: update permissions

* chore: remove previous roles and permissions

* chore: run eslint and prettier
  • Loading branch information
Shurtu-gal authored Oct 15, 2023
1 parent 477d759 commit 8fd2962
Show file tree
Hide file tree
Showing 14 changed files with 545 additions and 16 deletions.
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,8 +348,7 @@ module.exports = {
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 } }
) => {
try {
if (!UserPermission.exists(session, authToken, decodedToken, 'article.write.new')) {
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');

/**
* @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: {
description: 'Retrieves comments for given list of ids (default all) in descending order of creation time',
type: new GraphQLList(new GraphQLNonNull(CommentType)),
args: {
ids: {
description: 'List of Ids of comments to be retrieved',
type: new GraphQLList(new GraphQLNonNull(GraphQLID)),
},
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

0 comments on commit 8fd2962

Please sign in to comment.