Răsfoiți Sursa

send progress data

Yuki Takei 6 ani în urmă
părinte
comite
14f9062166
2 a modificat fișierele cu 53 adăugiri și 27 ștergeri
  1. 10 3
      src/server/routes/apiv3/export.js
  2. 43 24
      src/server/service/export.js

+ 10 - 3
src/server/routes/apiv3/export.js

@@ -51,12 +51,19 @@ module.exports = (crowi) => {
    *                    items:
    *                      type: object
    *                      description: the property of each file
+   *                  isExporting:
+   *                    type: boolean
+   *                    description: whether the current exporting job exists or not
    */
   router.get('/status', accessTokenParser, loginRequired, adminRequired, async(req, res) => {
-    const zipFileStats = await exportService.getStatus();
+    const { zipFileStats, isExporting } = await exportService.getStatus();
 
     // TODO: use res.apiv3
-    return res.json({ ok: true, zipFileStats });
+    return res.json({
+      ok: true,
+      zipFileStats,
+      isExporting,
+    });
   });
 
   /**
@@ -81,7 +88,7 @@ module.exports = (crowi) => {
       // get model for collection
       const models = collections.map(collectionName => growiBridgeService.getModelFromCollectionName(collectionName));
 
-      exportService.exportCollectionsToZippedJson(models);
+      exportService.export(models);
 
       // TODO: use res.apiv3
       return res.status(200).json({

+ 43 - 24
src/server/service/export.js

@@ -63,13 +63,15 @@ class ExportService {
     this.zlibLevel = 9; // 0(min) - 9(max)
 
     this.adminEvent = crowi.event('admin');
+
+    this.currentExportStatus = null;
   }
 
   /**
    * parse all zip files in downloads dir
    *
    * @memberOf ExportService
-   * @return {Array.<object>} info for zip files
+   * @return {object} info for zip files and whether currentExportStatus exists
    */
   async getStatus() {
     const zipFiles = fs.readdirSync(this.baseDir).filter((file) => { return path.extname(file) === '.zip' });
@@ -81,7 +83,12 @@ class ExportService {
     // filter null object (broken zip)
     const filtered = zipFileStats.filter(element => element != null);
 
-    return filtered;
+    const isExporting = this.currentExportStatus != null;
+
+    return {
+      zipFileStats: filtered,
+      isExporting,
+    };
   }
 
   /**
@@ -111,11 +118,10 @@ class ExportService {
 
   /**
    *
-   * @param {ExportStatus} exportStatus
    * @param {ExportProguress} exportProgress
    * @return {Transform}
    */
-  generateLogStream(exportStatus, exportProgress) {
+  generateLogStream(exportProgress) {
     const logProgress = this.logProgress.bind(this);
 
     let count = 0;
@@ -123,7 +129,7 @@ class ExportService {
     return new Transform({
       transform(chunk, encoding, callback) {
         count++;
-        logProgress(exportStatus, exportProgress, count);
+        logProgress(exportProgress, count);
 
         this.push(chunk);
 
@@ -170,11 +176,10 @@ class ExportService {
    * dump a mongodb collection into json
    *
    * @memberOf ExportService
-   * @param {ExportStatus} exportStatus
    * @param {object} Model instance of mongoose model
    * @return {string} path to zip file
    */
-  async exportCollectionToJson(exportStatus, Model) {
+  async exportCollectionToJson(Model) {
     const collectionName = Model.collection.name;
 
     // get native Cursor instance
@@ -188,8 +193,8 @@ class ExportService {
     const transformStream = this.generateTransformStream();
 
     // log configuration
-    const exportProgress = exportStatus.progressMap[collectionName];
-    const logStream = this.generateLogStream(exportStatus, exportProgress);
+    const exportProgress = this.currentExportStatus.progressMap[collectionName];
+    const logStream = this.generateLogStream(exportProgress);
 
     // create WritableStream
     const jsonFileToWrite = path.join(this.baseDir, `${collectionName}.json`);
@@ -215,10 +220,7 @@ class ExportService {
   async exportCollectionsToZippedJson(models) {
     const metaJson = await this.createMetaJson();
 
-    const exportStatus = new ExportStatus();
-    await exportStatus.init(models);
-
-    const promisesForModels = models.map(Model => this.exportCollectionToJson(exportStatus, Model));
+    const promisesForModels = models.map(Model => this.exportCollectionToJson(Model));
     const jsonFiles = await Promise.all(promisesForModels);
 
     // zip json
@@ -234,16 +236,32 @@ class ExportService {
     // TODO: remove broken zip file
   }
 
+  async export(models) {
+    if (this.currentExportStatus != null) {
+      throw new Error('There is an exporting process running.');
+    }
+
+    this.currentExportStatus = new ExportStatus();
+    await this.currentExportStatus.init(models);
+
+    try {
+      await this.exportCollectionsToZippedJson(models);
+    }
+    finally {
+      this.currentExportStatus = null;
+    }
+
+  }
+
   /**
    * log export progress
    *
    * @memberOf ExportService
    *
-   * @param {ExportStatus} exportStatus
    * @param {ExportProgress} exportProgress
    * @param {number} currentCount number of items exported
    */
-  logProgress(exportStatus, exportProgress, currentCount) {
+  logProgress(exportProgress, currentCount) {
     const output = `${exportProgress.collectionName}: ${currentCount}/${exportProgress.totalCount} written`;
 
     // update exportProgress.currentCount
@@ -252,28 +270,29 @@ class ExportService {
     // output every this.per items
     if (currentCount % this.per === 0) {
       logger.debug(output);
-      this.emitProgressEvent(exportStatus, exportProgress);
+      this.emitProgressEvent();
     }
     // output last item
     else if (currentCount === exportProgress.totalCount) {
       logger.info(output);
-      this.emitProgressEvent(exportStatus, exportProgress);
+      this.emitProgressEvent();
     }
   }
 
   /**
    * emit progress event
-   * @param {ExportStatus} exportStatus
    * @param {ExportProgress} exportProgress
    */
-  emitProgressEvent(exportStatus, exportProgress) {
-    const globalCurrentCount = exportStatus.currentCount;
-
-    const globalTotalCount = exportStatus.totalCount;
+  emitProgressEvent(exportProgress) {
+    const { currentCount, totalCount, progressList } = this.currentExportStatus;
 
     // send event (in progress in global)
-    if (globalCurrentCount !== globalTotalCount) {
-      this.adminEvent.emit('onProgressForExport', globalTotalCount, globalCurrentCount);
+    if (currentCount !== totalCount) {
+      this.adminEvent.emit('onProgressForExport', {
+        currentCount,
+        totalCount,
+        progressList,
+      });
     }
     else {
       this.adminEvent.emit('onTerminateForExport');