Răsfoiți Sursa

refactor comment model

Yuki Takei 2 ani în urmă
părinte
comite
2fcc22eae8

+ 6 - 0
apps/app/src/features/comment/server/events/consts.ts

@@ -0,0 +1,6 @@
+export const CommentEvent = {
+  CREATE: 'create',
+  UPDATE: 'update',
+  DELETE: 'delete',
+} as const;
+export type CommentEvent = typeof CommentEvent[keyof typeof CommentEvent];

+ 3 - 0
apps/app/src/features/comment/server/events/event-emitter.ts

@@ -0,0 +1,3 @@
+import { EventEmitter } from 'events';
+
+export const commentEvent = new EventEmitter();

+ 2 - 0
apps/app/src/features/comment/server/events/index.ts

@@ -0,0 +1,2 @@
+export * from './consts';
+export * from './event-emitter';

+ 2 - 0
apps/app/src/features/comment/server/index.ts

@@ -0,0 +1,2 @@
+export * from './events';
+export * from './models';

+ 4 - 12
apps/app/src/features/comment/server/models/comment.ts

@@ -22,7 +22,7 @@ export interface CommentModel extends Model<CommentDocument> {
     comment: string,
     comment: string,
     commentPosition: number,
     commentPosition: number,
     replyTo?: Types.ObjectId | null,
     replyTo?: Types.ObjectId | null,
-  ) => Promise<void>
+  ) => Promise<CommentDocument>
 
 
   getCommentsByPageId: (pageId: Types.ObjectId) => Promise<CommentDocument[]>
   getCommentsByPageId: (pageId: Types.ObjectId) => Promise<CommentDocument[]>
 
 
@@ -46,13 +46,6 @@ const commentSchema = new Schema<CommentDocument, CommentModel>({
   timestamps: true,
   timestamps: true,
 });
 });
 
 
-/**
- * post remove hook
- */
-commentSchema.post('remove', async(savedComment) => {
-  await commentEvent.emit('delete', savedComment);
-});
-
 
 
 commentSchema.statics.add = async function(
 commentSchema.statics.add = async function(
     pageId: Types.ObjectId,
     pageId: Types.ObjectId,
@@ -73,6 +66,8 @@ commentSchema.statics.add = async function(
       replyTo,
       replyTo,
     });
     });
     logger.debug('Comment saved.', data);
     logger.debug('Comment saved.', data);
+
+    return data;
   }
   }
   catch (err) {
   catch (err) {
     logger.debug('Error on saving comment.', err);
     logger.debug('Error on saving comment.', err);
@@ -119,9 +114,6 @@ commentSchema.methods.removeWithReplies = async function(comment) {
     $or: (
     $or: (
       [{ replyTo: this._id }, { _id: this._id }]),
       [{ replyTo: this._id }, { _id: this._id }]),
   });
   });
-
-  await commentEvent.emit('delete', comment);
-  return;
 };
 };
 
 
-export default getOrCreateModel<CommentDocument, CommentModel>('Comment', commentSchema);
+export const Comment = getOrCreateModel<CommentDocument, CommentModel>('Comment', commentSchema);

+ 1 - 0
apps/app/src/features/comment/server/models/index.ts

@@ -0,0 +1 @@
+export * from './comment';

+ 0 - 1
apps/app/src/server/crowi/index.js

@@ -100,7 +100,6 @@ function Crowi() {
     page: new (require('../events/page'))(this),
     page: new (require('../events/page'))(this),
     activity: new (require('../events/activity'))(this),
     activity: new (require('../events/activity'))(this),
     bookmark: new (require('../events/bookmark'))(this),
     bookmark: new (require('../events/bookmark'))(this),
-    comment: new (require('../events/comment'))(this),
     tag: new (require('../events/tag'))(this),
     tag: new (require('../events/tag'))(this),
     admin: new (require('../events/admin'))(this),
     admin: new (require('../events/admin'))(this),
   };
   };

+ 0 - 26
apps/app/src/server/events/comment.ts

@@ -1,26 +0,0 @@
-import loggerFactory from '~/utils/logger';
-
-const logger = loggerFactory('growi:events:comment');
-
-const events = require('events');
-const util = require('util');
-
-
-function CommentEvent(crowi) {
-  this.crowi = crowi;
-
-  events.EventEmitter.call(this);
-}
-util.inherits(CommentEvent, events.EventEmitter);
-
-CommentEvent.prototype.onCreate = function(comment) {
-  logger.info('onCreate comment event fired');
-};
-CommentEvent.prototype.onUpdate = function(comment) {
-  logger.info('onUpdate comment event fired');
-};
-CommentEvent.prototype.onDelete = function(comment) {
-  logger.info('onDelete comment event fired');
-};
-
-module.exports = CommentEvent;

+ 0 - 1
apps/app/src/server/models/index.js

@@ -9,7 +9,6 @@ module.exports = {
   Revision: require('./revision'),
   Revision: require('./revision'),
   Tag: require('./tag'),
   Tag: require('./tag'),
   Bookmark: require('./bookmark'),
   Bookmark: require('./bookmark'),
-  Comment: require('./comment'),
   Attachment: require('./attachment'),
   Attachment: require('./attachment'),
   GlobalNotificationSetting: require('./GlobalNotificationSetting'),
   GlobalNotificationSetting: require('./GlobalNotificationSetting'),
   GlobalNotificationMailSetting: require('./GlobalNotificationSetting/GlobalNotificationMailSetting'),
   GlobalNotificationMailSetting: require('./GlobalNotificationSetting/GlobalNotificationMailSetting'),

+ 1 - 1
apps/app/src/server/models/obsolete-page.js

@@ -2,6 +2,7 @@ import { PageGrant } from '@growi/core';
 import { templateChecker, pagePathUtils, pathUtils } from '@growi/core/dist/utils';
 import { templateChecker, pagePathUtils, pathUtils } from '@growi/core/dist/utils';
 import escapeStringRegexp from 'escape-string-regexp';
 import escapeStringRegexp from 'escape-string-regexp';
 
 
+import { Comment } from '~/features/comment/server';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 
 
@@ -274,7 +275,6 @@ export const getPageSchema = (crowi) => {
     validateCrowi();
     validateCrowi();
 
 
     const self = this;
     const self = this;
-    const Comment = crowi.model('Comment');
     return Comment.countCommentByPageId(pageId)
     return Comment.countCommentByPageId(pageId)
       .then((count) => {
       .then((count) => {
         self.update({ _id: pageId }, { commentCount: count }, {}, (err, data) => {
         self.update({ _id: pageId }, { commentCount: count }, {}, (err, data) => {

+ 5 - 10
apps/app/src/server/routes/comment.js

@@ -1,4 +1,5 @@
 
 
+import { Comment, CommentEvent, commentEvent } from '~/features/comment/server';
 import { SupportedAction, SupportedTargetModel, SupportedEventModel } from '~/interfaces/activity';
 import { SupportedAction, SupportedTargetModel, SupportedEventModel } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
@@ -49,7 +50,6 @@ const { serializeUserSecurely } = require('../models/serializers/user-serializer
 
 
 module.exports = function(crowi, app) {
 module.exports = function(crowi, app) {
   const logger = loggerFactory('growi:routes:comment');
   const logger = loggerFactory('growi:routes:comment');
-  const Comment = crowi.model('Comment');
   const User = crowi.model('User');
   const User = crowi.model('User');
   const Page = crowi.model('Page');
   const Page = crowi.model('Page');
   const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
   const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
@@ -234,7 +234,6 @@ module.exports = function(crowi, app) {
     const comment = commentForm.comment;
     const comment = commentForm.comment;
     const position = commentForm.comment_position || -1;
     const position = commentForm.comment_position || -1;
     const replyTo = commentForm.replyTo;
     const replyTo = commentForm.replyTo;
-    const commentEvent = crowi.event('comment');
 
 
     // check whether accessible
     // check whether accessible
     const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
     const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
@@ -244,8 +243,8 @@ module.exports = function(crowi, app) {
 
 
     let createdComment;
     let createdComment;
     try {
     try {
-      createdComment = await Comment.create(pageId, req.user._id, revisionId, comment, position, replyTo);
-      commentEvent.emit('create', createdComment);
+      createdComment = await Comment.add(pageId, req.user._id, revisionId, comment, position, replyTo);
+      commentEvent.emit(CommentEvent.CREATE, createdComment);
     }
     }
     catch (err) {
     catch (err) {
       logger.error(err);
       logger.error(err);
@@ -357,8 +356,6 @@ module.exports = function(crowi, app) {
     const commentId = commentForm.comment_id;
     const commentId = commentForm.comment_id;
     const revision = commentForm.revision_id;
     const revision = commentForm.revision_id;
 
 
-    const commentEvent = crowi.event('comment');
-
     if (commentStr === '') {
     if (commentStr === '') {
       return res.json(ApiResponse.error('Comment text is required'));
       return res.json(ApiResponse.error('Comment text is required'));
     }
     }
@@ -389,7 +386,7 @@ module.exports = function(crowi, app) {
         { _id: commentId },
         { _id: commentId },
         { $set: { comment: commentStr, revision } },
         { $set: { comment: commentStr, revision } },
       );
       );
-      commentEvent.emit('update', updatedComment);
+      commentEvent.emit(CommentEvent.UPDATE, updatedComment);
     }
     }
     catch (err) {
     catch (err) {
       logger.error(err);
       logger.error(err);
@@ -446,8 +443,6 @@ module.exports = function(crowi, app) {
    * @apiParam {String} comment_id Comment Id.
    * @apiParam {String} comment_id Comment Id.
    */
    */
   api.remove = async function(req, res) {
   api.remove = async function(req, res) {
-    const commentEvent = crowi.event('comment');
-
     const commentId = req.body.comment_id;
     const commentId = req.body.comment_id;
     if (!commentId) {
     if (!commentId) {
       return Promise.resolve(res.json(ApiResponse.error('\'comment_id\' is undefined')));
       return Promise.resolve(res.json(ApiResponse.error('\'comment_id\' is undefined')));
@@ -472,7 +467,7 @@ module.exports = function(crowi, app) {
 
 
       await comment.removeWithReplies(comment);
       await comment.removeWithReplies(comment);
       await Page.updateCommentCount(comment.page);
       await Page.updateCommentCount(comment.page);
-      commentEvent.emit('delete', comment);
+      commentEvent.emit(CommentEvent.DELETE, comment);
     }
     }
     catch (err) {
     catch (err) {
       return res.json(ApiResponse.error(err));
       return res.json(ApiResponse.error(err));

+ 12 - 16
apps/app/src/server/service/comment.ts

@@ -1,5 +1,7 @@
 import { Types } from 'mongoose';
 import { Types } from 'mongoose';
 
 
+import { Comment, CommentEvent, commentEvent } from '~/features/comment/server';
+
 import loggerFactory from '../../utils/logger';
 import loggerFactory from '../../utils/logger';
 import Crowi from '../crowi';
 import Crowi from '../crowi';
 import { getModelSafely } from '../util/mongoose-utils';
 import { getModelSafely } from '../util/mongoose-utils';
@@ -17,22 +19,18 @@ class CommentService {
 
 
   inAppNotificationService!: any;
   inAppNotificationService!: any;
 
 
-  commentEvent!: any;
-
   constructor(crowi: Crowi) {
   constructor(crowi: Crowi) {
     this.crowi = crowi;
     this.crowi = crowi;
     this.activityService = crowi.activityService;
     this.activityService = crowi.activityService;
     this.inAppNotificationService = crowi.inAppNotificationService;
     this.inAppNotificationService = crowi.inAppNotificationService;
 
 
-    this.commentEvent = crowi.event('comment');
-
     // init
     // init
     this.initCommentEventListeners();
     this.initCommentEventListeners();
   }
   }
 
 
   initCommentEventListeners(): void {
   initCommentEventListeners(): void {
     // create
     // create
-    this.commentEvent.on('create', async(savedComment) => {
+    commentEvent.on(CommentEvent.CREATE, async(savedComment) => {
 
 
       try {
       try {
         const Page = getModelSafely('Page') || require('../models/page')(this.crowi);
         const Page = getModelSafely('Page') || require('../models/page')(this.crowi);
@@ -45,19 +43,11 @@ class CommentService {
     });
     });
 
 
     // update
     // update
-    this.commentEvent.on('update', async() => {
-      try {
-        this.commentEvent.onUpdate();
-      }
-      catch (err) {
-        logger.error('Error occurred while handling the comment update event:\n', err);
-      }
+    commentEvent.on(CommentEvent.UPDATE, async() => {
     });
     });
 
 
     // remove
     // remove
-    this.commentEvent.on('delete', async(removedComment) => {
-      this.commentEvent.onDelete();
-
+    commentEvent.on(CommentEvent.DELETE, async(removedComment) => {
       try {
       try {
         const Page = getModelSafely('Page') || require('../models/page')(this.crowi);
         const Page = getModelSafely('Page') || require('../models/page')(this.crowi);
         await Page.updateCommentCount(removedComment.page);
         await Page.updateCommentCount(removedComment.page);
@@ -69,11 +59,17 @@ class CommentService {
   }
   }
 
 
   getMentionedUsers = async(commentId: Types.ObjectId): Promise<Types.ObjectId[]> => {
   getMentionedUsers = async(commentId: Types.ObjectId): Promise<Types.ObjectId[]> => {
-    const Comment = getModelSafely('Comment') || require('../models/comment')(this.crowi);
     const User = getModelSafely('User') || require('../models/user')(this.crowi);
     const User = getModelSafely('User') || require('../models/user')(this.crowi);
 
 
     // Get comment by comment ID
     // Get comment by comment ID
     const commentData = await Comment.findOne({ _id: commentId });
     const commentData = await Comment.findOne({ _id: commentId });
+
+    // not found
+    if (commentData == null) {
+      logger.warn(`The comment ('${commentId.toString()}') is not found.`);
+      return [];
+    }
+
     const { comment } = commentData;
     const { comment } = commentData;
 
 
     const usernamesFromComment = comment.match(USERNAME_PATTERN);
     const usernamesFromComment = comment.match(USERNAME_PATTERN);

+ 0 - 3
apps/app/src/server/service/in-app-notification.ts

@@ -35,9 +35,6 @@ export default class InAppNotificationService {
 
 
   activityEvent!: any;
   activityEvent!: any;
 
 
-  commentEvent!: any;
-
-
   constructor(crowi: Crowi) {
   constructor(crowi: Crowi) {
     this.crowi = crowi;
     this.crowi = crowi;
     this.activityEvent = crowi.event('activity');
     this.activityEvent = crowi.event('activity');

+ 1 - 1
apps/app/src/server/service/page.ts

@@ -14,6 +14,7 @@ import escapeStringRegexp from 'escape-string-regexp';
 import mongoose, { ObjectId, Cursor } from 'mongoose';
 import mongoose, { ObjectId, Cursor } from 'mongoose';
 import streamToPromise from 'stream-to-promise';
 import streamToPromise from 'stream-to-promise';
 
 
+import { Comment } from '~/features/comment/server';
 import { SupportedAction } from '~/interfaces/activity';
 import { SupportedAction } from '~/interfaces/activity';
 import { V5ConversionErrCode } from '~/interfaces/errors/v5-conversion-error';
 import { V5ConversionErrCode } from '~/interfaces/errors/v5-conversion-error';
 import {
 import {
@@ -1687,7 +1688,6 @@ class PageService {
   private async deleteCompletelyOperation(pageIds, pagePaths) {
   private async deleteCompletelyOperation(pageIds, pagePaths) {
     // Delete Bookmarks, Attachments, Revisions, Pages and emit delete
     // Delete Bookmarks, Attachments, Revisions, Pages and emit delete
     const Bookmark = this.crowi.model('Bookmark');
     const Bookmark = this.crowi.model('Bookmark');
-    const Comment = this.crowi.model('Comment');
     const Page = this.crowi.model('Page');
     const Page = this.crowi.model('Page');
     const PageTagRelation = this.crowi.model('PageTagRelation');
     const PageTagRelation = this.crowi.model('PageTagRelation');
     const ShareLink = this.crowi.model('ShareLink');
     const ShareLink = this.crowi.model('ShareLink');

+ 4 - 4
apps/app/src/server/service/search.ts

@@ -2,6 +2,7 @@ import type { IPageHasId } from '@growi/core';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 import { FilterXSS } from 'xss';
 import { FilterXSS } from 'xss';
 
 
+import { CommentEvent, commentEvent } from '~/features/comment/server';
 import { SearchDelegatorName } from '~/interfaces/named-query';
 import { SearchDelegatorName } from '~/interfaces/named-query';
 import { IFormattedSearchResult, IPageWithSearchMeta, ISearchResult } from '~/interfaces/search';
 import { IFormattedSearchResult, IPageWithSearchMeta, ISearchResult } from '~/interfaces/search';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -166,10 +167,9 @@ class SearchService implements SearchQueryParser, SearchResolver {
     const tagEvent = this.crowi.event('tag');
     const tagEvent = this.crowi.event('tag');
     tagEvent.on('update', this.fullTextSearchDelegator.syncTagChanged.bind(this.fullTextSearchDelegator));
     tagEvent.on('update', this.fullTextSearchDelegator.syncTagChanged.bind(this.fullTextSearchDelegator));
 
 
-    const commentEvent = this.crowi.event('comment');
-    commentEvent.on('create', this.fullTextSearchDelegator.syncCommentChanged.bind(this.fullTextSearchDelegator));
-    commentEvent.on('update', this.fullTextSearchDelegator.syncCommentChanged.bind(this.fullTextSearchDelegator));
-    commentEvent.on('delete', this.fullTextSearchDelegator.syncCommentChanged.bind(this.fullTextSearchDelegator));
+    commentEvent.on(CommentEvent.CREATE, this.fullTextSearchDelegator.syncCommentChanged.bind(this.fullTextSearchDelegator));
+    commentEvent.on(CommentEvent.UPDATE, this.fullTextSearchDelegator.syncCommentChanged.bind(this.fullTextSearchDelegator));
+    commentEvent.on(CommentEvent.DELETE, this.fullTextSearchDelegator.syncCommentChanged.bind(this.fullTextSearchDelegator));
   }
   }
 
 
   resetErrorStatus() {
   resetErrorStatus() {