Parcourir la source

Refactored updatePage method using suboperation

Taichi Masuyama il y a 3 ans
Parent
commit
35f3a0eb6e
1 fichiers modifiés avec 73 ajouts et 38 suppressions
  1. 73 38
      packages/app/src/server/service/page.ts

+ 73 - 38
packages/app/src/server/service/page.ts

@@ -29,6 +29,7 @@ import { prepareDeleteConfigValuesForCalc } from '~/utils/page-delete-config';
 
 
 import { ObjectIdLike } from '../interfaces/mongoose-utils';
 import { ObjectIdLike } from '../interfaces/mongoose-utils';
 import { PathAlreadyExistsError } from '../models/errors';
 import { PathAlreadyExistsError } from '../models/errors';
+import { IOptionsForUpdate } from '../models/interfaces/page-operation';
 import PageOperation, { PageOperationDocument } from '../models/page-operation';
 import PageOperation, { PageOperationDocument } from '../models/page-operation';
 import { PageRedirectModel } from '../models/page-redirect';
 import { PageRedirectModel } from '../models/page-redirect';
 import { serializePageSecurely } from '../models/serializers/page-serializer';
 import { serializePageSecurely } from '../models/serializers/page-serializer';
@@ -3734,18 +3735,56 @@ class PageService {
     return this.updatePage(page, null, null, user, options);
     return this.updatePage(page, null, null, user, options);
   }
   }
 
 
+  async updatePageSubOperation(page, user, exPage, options: IOptionsForUpdate, pageOpId: ObjectIdLike): Promise<void> {
+    const Page = mongoose.model('Page') as unknown as PageModel;
+
+    const currentPage = page;
+
+    const exParent = exPage.parent;
+    const wasOnTree = exPage.parent != null || isTopPage(exPage.path);
+    const shouldBeOnTree = currentPage.grant !== PageGrant.GRANT_RESTRICTED;
+    const isChildrenExist = await Page.count({ path: new RegExp(`^${escapeStringRegexp(addTrailingSlash(currentPage.path))}`), parent: { $ne: null } });
+
+    // 1. Update descendantCount
+    const shouldPlusDescCount = !wasOnTree && shouldBeOnTree;
+    const shouldMinusDescCount = wasOnTree && !shouldBeOnTree;
+    if (shouldPlusDescCount) {
+      await this.updateDescendantCountOfAncestors(currentPage._id, 1, false);
+      const newDescendantCount = await Page.recountDescendantCount(currentPage._id);
+      await Page.updateOne({ _id: currentPage._id }, { descendantCount: newDescendantCount });
+    }
+    else if (shouldMinusDescCount) {
+      // Update from parent. Parent is null if currentPage.grant is RESTRECTED.
+      if (currentPage.grant === PageGrant.GRANT_RESTRICTED) {
+        await this.updateDescendantCountOfAncestors(exParent, -1, true);
+      }
+    }
+
+    // 2. Delete unnecessary empty pages
+    const shouldRemoveLeafEmpPages = wasOnTree && !isChildrenExist;
+    if (shouldRemoveLeafEmpPages) {
+      await Page.removeLeafEmptyPagesRecursively(exParent);
+    }
+
+    // 3. Update scopes for descendants
+    if (options.overwriteScopesOfDescendants) {
+      await Page.applyScopesToDescendantsAsyncronously(currentPage, user);
+    }
+
+    await PageOperation.findByIdAndDelete(pageOpId);
+  }
+
   async updatePage(
   async updatePage(
       pageData,
       pageData,
       body: string | null,
       body: string | null,
       previousBody: string | null,
       previousBody: string | null,
       user,
       user,
-      options: {grant?: PageGrant, grantUserGroupId?: ObjectIdLike, isSyncRevisionToHackmd?: boolean, overwriteScopesOfDescendants?: boolean} = {},
+      options: IOptionsForUpdate = {},
   ): Promise<PageDocument> {
   ): Promise<PageDocument> {
     const Page = mongoose.model('Page') as unknown as PageModel;
     const Page = mongoose.model('Page') as unknown as PageModel;
     const Revision = mongoose.model('Revision') as any; // TODO: Typescriptize model
     const Revision = mongoose.model('Revision') as any; // TODO: Typescriptize model
 
 
     const wasOnTree = pageData.parent != null || isTopPage(pageData.path);
     const wasOnTree = pageData.parent != null || isTopPage(pageData.path);
-    const exParent = pageData.parent;
     const isV5Compatible = this.crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
     const isV5Compatible = this.crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
 
 
     const shouldUseV4Process = this.shouldUseUpdatePageV4(pageData.grant, isV5Compatible, wasOnTree);
     const shouldUseV4Process = this.shouldUseUpdatePageV4(pageData.grant, isV5Compatible, wasOnTree);
@@ -3754,14 +3793,16 @@ class PageService {
       return this.updatePageV4(pageData, body, previousBody, user, options);
       return this.updatePageV4(pageData, body, previousBody, user, options);
     }
     }
 
 
-    const grant = options.grant ?? pageData.grant; // use the previous data if absence
-    const grantUserGroupId: undefined | ObjectIdLike = options.grantUserGroupId ?? pageData.grantedGroup?._id.toString();
+    // Clone page document
+    const clonedPageData = Page.hydrate(pageData.toObject());
+    const newPageData = pageData;
 
 
-    const grantedUserIds = pageData.grantedUserIds || [user._id];
-    const shouldBeOnTree = grant !== PageGrant.GRANT_RESTRICTED;
-    const isChildrenExist = await Page.count({ path: new RegExp(`^${escapeStringRegexp(addTrailingSlash(pageData.path))}`), parent: { $ne: null } });
+    const grant = options.grant ?? clonedPageData.grant; // use the previous data if absence
+    const grantUserGroupId: undefined | ObjectIdLike = options.grantUserGroupId ?? clonedPageData.grantedGroup?._id.toString();
 
 
-    const newPageData = pageData;
+    const grantedUserIds = clonedPageData.grantedUserIds || [user._id];
+    const shouldBeOnTree = grant !== PageGrant.GRANT_RESTRICTED;
+    const isChildrenExist = await Page.count({ path: new RegExp(`^${escapeStringRegexp(addTrailingSlash(clonedPageData.path))}`), parent: { $ne: null } });
 
 
     const { pageService, pageGrantService } = this.crowi;
     const { pageService, pageGrantService } = this.crowi;
 
 
@@ -3769,10 +3810,11 @@ class PageService {
       let isGrantNormalized = false;
       let isGrantNormalized = false;
       try {
       try {
         const shouldCheckDescendants = !options.overwriteScopesOfDescendants;
         const shouldCheckDescendants = !options.overwriteScopesOfDescendants;
-        isGrantNormalized = await pageGrantService.isGrantNormalized(user, pageData.path, grant, grantedUserIds, grantUserGroupId, shouldCheckDescendants);
+        // eslint-disable-next-line max-len
+        isGrantNormalized = await pageGrantService.isGrantNormalized(user, clonedPageData.path, grant, grantedUserIds, grantUserGroupId, shouldCheckDescendants);
       }
       }
       catch (err) {
       catch (err) {
-        logger.error(`Failed to validate grant of page at "${pageData.path}" of grant ${grant}:`, err);
+        logger.error(`Failed to validate grant of page at "${clonedPageData.path}" of grant ${grant}:`, err);
         throw err;
         throw err;
       }
       }
       if (!isGrantNormalized) {
       if (!isGrantNormalized) {
@@ -3781,7 +3823,7 @@ class PageService {
 
 
       if (options.overwriteScopesOfDescendants) {
       if (options.overwriteScopesOfDescendants) {
         const updateGrantInfo = await pageGrantService.generateUpdateGrantInfoToOverwriteDescendants(user, grant, options.grantUserGroupId);
         const updateGrantInfo = await pageGrantService.generateUpdateGrantInfoToOverwriteDescendants(user, grant, options.grantUserGroupId);
-        const canOverwriteDescendants = await pageGrantService.canOverwriteDescendants(pageData.path, user, updateGrantInfo);
+        const canOverwriteDescendants = await pageGrantService.canOverwriteDescendants(clonedPageData.path, user, updateGrantInfo);
 
 
         if (!canOverwriteDescendants) {
         if (!canOverwriteDescendants) {
           throw Error('Cannot overwrite scopes of descendants.');
           throw Error('Cannot overwrite scopes of descendants.');
@@ -3796,9 +3838,9 @@ class PageService {
     else {
     else {
       if (wasOnTree && isChildrenExist) {
       if (wasOnTree && isChildrenExist) {
         // Update children's parent with new parent
         // Update children's parent with new parent
-        const newParentForChildren = await Page.createEmptyPage(pageData.path, pageData.parent, pageData.descendantCount);
+        const newParentForChildren = await Page.createEmptyPage(clonedPageData.path, clonedPageData.parent, clonedPageData.descendantCount);
         await Page.updateMany(
         await Page.updateMany(
-          { parent: pageData._id },
+          { parent: clonedPageData._id },
           { parent: newParentForChildren._id },
           { parent: newParentForChildren._id },
         );
         );
       }
       }
@@ -3831,7 +3873,7 @@ class PageService {
 
 
     // Update ex children's parent
     // Update ex children's parent
     if (!wasOnTree && shouldBeOnTree) {
     if (!wasOnTree && shouldBeOnTree) {
-      const emptyPageAtSamePath = await Page.findOne({ path: pageData.path, isEmpty: true }); // this page is necessary to find children
+      const emptyPageAtSamePath = await Page.findOne({ path: clonedPageData.path, isEmpty: true }); // this page is necessary to find children
 
 
       if (isChildrenExist) {
       if (isChildrenExist) {
         if (emptyPageAtSamePath != null) {
         if (emptyPageAtSamePath != null) {
@@ -3843,35 +3885,28 @@ class PageService {
         }
         }
       }
       }
 
 
-      await Page.findOneAndDelete({ path: pageData.path, isEmpty: true }); // delete here
-    }
-
-    // Sub operation
-    // update scopes for descendants
-    if (options.overwriteScopesOfDescendants) {
-      Page.applyScopesToDescendantsAsyncronously(savedPage, user);
+      await Page.findOneAndDelete({ path: clonedPageData.path, isEmpty: true }); // delete here
     }
     }
 
 
-    // 1. Update descendantCount
-    const shouldPlusDescCount = !wasOnTree && shouldBeOnTree;
-    const shouldMinusDescCount = wasOnTree && !shouldBeOnTree;
-    if (shouldPlusDescCount) {
-      await pageService.updateDescendantCountOfAncestors(newPageData._id, 1, false);
-      const newDescendantCount = await Page.recountDescendantCount(newPageData._id);
-      await Page.updateOne({ _id: newPageData._id }, { descendantCount: newDescendantCount });
+    // Directly run sub operation for now since it might be complex to handle main operation for updating pages -- Taichi Masuyama 2022.11.08
+    let pageOp;
+    try {
+      pageOp = await PageOperation.create({
+        actionType: PageActionType.Update,
+        actionStage: PageActionStage.Sub,
+        page: savedPage,
+        exPage: clonedPageData,
+        user,
+        fromPath: clonedPageData.path,
+        options,
+      });
     }
     }
-    else if (shouldMinusDescCount) {
-      // Update from parent. Parent is null if newPageData.grant is RESTRECTED.
-      if (newPageData.grant === PageGrant.GRANT_RESTRICTED) {
-        await pageService.updateDescendantCountOfAncestors(exParent, -1, true);
-      }
+    catch (err) {
+      logger.error('Failed to create PageOperation document.', err);
+      throw err;
     }
     }
 
 
-    // 2. Delete unnecessary empty pages
-    const shouldRemoveLeafEmpPages = wasOnTree && !isChildrenExist;
-    if (shouldRemoveLeafEmpPages) {
-      await Page.removeLeafEmptyPagesRecursively(exParent);
-    }
+    this.updatePageSubOperation(savedPage, user, clonedPageData, options, pageOp._id);
 
 
     return savedPage;
     return savedPage;
   }
   }