Преглед изворни кода

Merge pull request #5252 from weseek/feat/update-descendant-count-on-duplication

update descendant count on duplication
Yohei Shiina пре 4 година
родитељ
комит
82767ed24c

+ 3 - 3
packages/app/src/server/models/page.ts

@@ -145,7 +145,7 @@ schema.statics.createEmptyPagesByPaths = async function(paths: string[], publicO
 };
 };
 
 
 schema.statics.createEmptyPage = async function(
 schema.statics.createEmptyPage = async function(
-    path: string, parent: any, descendantCount: number, // TODO: improve type including IPage at https://redmine.weseek.co.jp/issues/86506
+    path: string, parent: any, descendantCount = 0, // TODO: improve type including IPage at https://redmine.weseek.co.jp/issues/86506
 ): Promise<PageDocument & { _id: any }> {
 ): Promise<PageDocument & { _id: any }> {
   if (parent == null) {
   if (parent == null) {
     throw Error('parent must not be null');
     throw Error('parent must not be null');
@@ -534,7 +534,7 @@ export default (crowi: Crowi): any => {
   }
   }
 
 
   schema.statics.create = async function(path: string, body: string, user, options: PageCreateOptions = {}) {
   schema.statics.create = async function(path: string, body: string, user, options: PageCreateOptions = {}) {
-    if (crowi.pageGrantService == null || crowi.configManager == null) {
+    if (crowi.pageGrantService == null || crowi.configManager == null || crowi.pageService == null) {
       throw Error('Crowi is not setup');
       throw Error('Crowi is not setup');
     }
     }
 
 
@@ -622,7 +622,7 @@ export default (crowi: Crowi): any => {
 
 
     let savedPage = await page.save();
     let savedPage = await page.save();
 
 
-    await crowi.pageService?.updateDescendantCountOfAncestors(page._id, 1, false);
+    await crowi.pageService.updateDescendantCountOfAncestors(page._id, 1, false);
 
 
     /*
     /*
      * After save
      * After save

+ 4 - 2
packages/app/src/server/routes/apiv3/pages.js

@@ -638,9 +638,11 @@ module.exports = (crowi) => {
     const newParentPage = await crowi.pageService.duplicate(page, newPagePath, req.user, isRecursively);
     const newParentPage = await crowi.pageService.duplicate(page, newPagePath, req.user, isRecursively);
     const result = { page: serializePageSecurely(newParentPage) };
     const result = { page: serializePageSecurely(newParentPage) };
 
 
-    page.path = newPagePath;
+    // copy the page since it's used and updated in crowi.pageService.duplicate
+    const copyPage = { ...page };
+    copyPage.path = newPagePath;
     try {
     try {
-      await globalNotificationService.fire(GlobalNotificationSetting.EVENT.PAGE_CREATE, page, req.user);
+      await globalNotificationService.fire(GlobalNotificationSetting.EVENT.PAGE_CREATE, copyPage, req.user);
     }
     }
     catch (err) {
     catch (err) {
       logger.error('Create grobal notification failed', err);
       logger.error('Create grobal notification failed', err);

+ 29 - 19
packages/app/src/server/service/page.ts

@@ -700,9 +700,17 @@ class PageService {
 
 
     newPagePath = this.crowi.xss.process(newPagePath); // eslint-disable-line no-param-reassign
     newPagePath = this.crowi.xss.process(newPagePath); // eslint-disable-line no-param-reassign
 
 
-    const createdPage = await (Page.create as CreateMethod)(
-      newPagePath, page.revision.body, user, options,
-    );
+    let createdPage;
+
+    if (page.isEmpty) {
+      const parent = await Page.getParentAndFillAncestors(newPagePath);
+      createdPage = await Page.createEmptyPage(newPagePath, parent);
+    }
+    else {
+      createdPage = await (Page.create as CreateMethod)(
+        newPagePath, page.revision.body, user, options,
+      );
+    }
 
 
     // take over tags
     // take over tags
     const originTags = await page.findRelatedTagsById();
     const originTags = await page.findRelatedTagsById();
@@ -718,12 +726,16 @@ class PageService {
 
 
     // TODO: resume
     // TODO: resume
     if (isRecursively) {
     if (isRecursively) {
-      this.duplicateDescendantsWithStream(page, newPagePath, user, shouldUseV4Process);
+      this.resumableDuplicateDescendants(page, newPagePath, user, shouldUseV4Process, createdPage._id);
     }
     }
-
     return result;
     return result;
   }
   }
 
 
+  async resumableDuplicateDescendants(page, newPagePath, user, shouldUseV4Process, createdPageId) {
+    const descendantCountAppliedToAncestors = await this.duplicateDescendantsWithStream(page, newPagePath, user, shouldUseV4Process);
+    await this.updateDescendantCountOfAncestors(createdPageId, descendantCountAppliedToAncestors, false);
+  }
+
   async duplicateV4(page, newPagePath, user, isRecursively) {
   async duplicateV4(page, newPagePath, user, isRecursively) {
     const Page = this.crowi.model('Page');
     const Page = this.crowi.model('Page');
     const PageTagRelation = mongoose.model('PageTagRelation') as any; // TODO: Typescriptize model
     const PageTagRelation = mongoose.model('PageTagRelation') as any; // TODO: Typescriptize model
@@ -827,14 +839,7 @@ class PageService {
       pageIdMapping[page._id] = newPageId;
       pageIdMapping[page._id] = newPageId;
 
 
       let newPage;
       let newPage;
-      if (page.isEmpty) {
-        newPage = {
-          _id: newPageId,
-          path: newPagePath,
-          isEmpty: true,
-        };
-      }
-      else {
+      if (!page.isEmpty) {
         newPage = {
         newPage = {
           _id: newPageId,
           _id: newPageId,
           path: newPagePath,
           path: newPagePath,
@@ -845,14 +850,11 @@ class PageService {
           lastUpdateUser: user._id,
           lastUpdateUser: user._id,
           revision: revisionId,
           revision: revisionId,
         };
         };
+        newRevisions.push({
+          _id: revisionId, pageId: newPageId, body: pageIdRevisionMapping[page._id].body, author: user._id, format: 'markdown',
+        });
       }
       }
-
       newPages.push(newPage);
       newPages.push(newPage);
-
-      newRevisions.push({
-        _id: revisionId, pageId: newPageId, body: pageIdRevisionMapping[page._id].body, author: user._id, format: 'markdown',
-      });
-
     });
     });
 
 
     await Page.insertMany(newPages, { ordered: false });
     await Page.insertMany(newPages, { ordered: false });
@@ -922,11 +924,13 @@ class PageService {
     const normalizeParentAndDescendantCountOfDescendants = this.normalizeParentAndDescendantCountOfDescendants.bind(this);
     const normalizeParentAndDescendantCountOfDescendants = this.normalizeParentAndDescendantCountOfDescendants.bind(this);
     const pageEvent = this.pageEvent;
     const pageEvent = this.pageEvent;
     let count = 0;
     let count = 0;
+    let nNonEmptyDuplicatedPages = 0;
     const writeStream = new Writable({
     const writeStream = new Writable({
       objectMode: true,
       objectMode: true,
       async write(batch, encoding, callback) {
       async write(batch, encoding, callback) {
         try {
         try {
           count += batch.length;
           count += batch.length;
+          nNonEmptyDuplicatedPages += batch.filter(page => !page.isEmpty).length;
           await duplicateDescendants(batch, user, pathRegExp, newPagePathPrefix, shouldUseV4Process);
           await duplicateDescendants(batch, user, pathRegExp, newPagePathPrefix, shouldUseV4Process);
           logger.debug(`Adding pages progressing: (count=${count})`);
           logger.debug(`Adding pages progressing: (count=${count})`);
         }
         }
@@ -962,6 +966,9 @@ class PageService {
       .pipe(createBatchStream(BULK_REINDEX_SIZE))
       .pipe(createBatchStream(BULK_REINDEX_SIZE))
       .pipe(writeStream);
       .pipe(writeStream);
 
 
+    await streamToPromise(writeStream);
+
+    return nNonEmptyDuplicatedPages;
   }
   }
 
 
   private async duplicateDescendantsWithStreamV4(page, newPagePath, user) {
   private async duplicateDescendantsWithStreamV4(page, newPagePath, user) {
@@ -1000,6 +1007,9 @@ class PageService {
       .pipe(createBatchStream(BULK_REINDEX_SIZE))
       .pipe(createBatchStream(BULK_REINDEX_SIZE))
       .pipe(writeStream);
       .pipe(writeStream);
 
 
+    await streamToPromise(writeStream);
+
+    return count;
   }
   }
 
 
   /*
   /*