Просмотр исходного кода

fix: Modified a migration file for mongodb compatibility (#4413)

* Modified for compatibility

* Fixed empty bulkWrite error

* make setup-crowi.js ES6 style export

* clean code

* add test

* improve expect lines

* fix empty bulkWrite error

* fix test

Co-authored-by: Yuki Takei <yuki@weseek.co.jp>
Haku Mizuki 4 лет назад
Родитель
Сommit
a2bf84f4f1

+ 56 - 41
packages/app/src/migrations/20210913153942-migrate-slack-app-integration-schema.js

@@ -7,44 +7,60 @@ import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:migrate:update-configs-for-slackbot');
 const logger = loggerFactory('growi:migrate:update-configs-for-slackbot');
 
 
+// create default data
+const defaultDataForBroadcastUse = {};
+defaultSupportedCommandsNameForBroadcastUse.forEach((commandName) => {
+  defaultDataForBroadcastUse[commandName] = false;
+});
+const defaultDataForSingleUse = {};
+defaultSupportedCommandsNameForSingleUse.forEach((commandName) => {
+  defaultDataForSingleUse[commandName] = false;
+});
+
 module.exports = {
 module.exports = {
   async up(db) {
   async up(db) {
     logger.info('Apply migration');
     logger.info('Apply migration');
-    mongoose.connect(getMongoUri(), mongoOptions);
+    await mongoose.connect(getMongoUri(), mongoOptions);
 
 
     const SlackAppIntegration = getModelSafely('SlackAppIntegration') || require('~/server/models/slack-app-integration')();
     const SlackAppIntegration = getModelSafely('SlackAppIntegration') || require('~/server/models/slack-app-integration')();
 
 
     const slackAppIntegrations = await SlackAppIntegration.find();
     const slackAppIntegrations = await SlackAppIntegration.find();
 
 
-    // create default data
-    const defaultDataForBroadcastUse = {};
-    defaultSupportedCommandsNameForBroadcastUse.forEach((commandName) => {
-      defaultDataForBroadcastUse[commandName] = false;
-    });
-    const defaultDataForSingleUse = {};
-    defaultSupportedCommandsNameForSingleUse.forEach((commandName) => {
-      defaultDataForSingleUse[commandName] = false;
-    });
+    if (slackAppIntegrations.length === 0) return;
 
 
     // create operations
     // create operations
     const operations = slackAppIntegrations.map((doc) => {
     const operations = slackAppIntegrations.map((doc) => {
-      const copyForBroadcastUse = { ...defaultDataForBroadcastUse };
-      const copyForSingleUse = { ...defaultDataForSingleUse };
-      // when the document does NOT have supportedCommandsFor... columns
-      if (doc._doc.supportedCommandsForBroadcastUse == null) {
-        defaultSupportedCommandsNameForBroadcastUse.forEach((commandName) => {
+      let copyForBroadcastUse = { ...defaultDataForBroadcastUse };
+      let copyForSingleUse = { ...defaultDataForSingleUse };
+      // when the document already has permissionsFor... colums
+      if (doc._doc.permissionsForBroadcastUseCommands != null) {
+        // merge
+        copyForBroadcastUse = {
+          ...defaultDataForBroadcastUse,
+          ...Object.fromEntries(doc._doc.permissionsForBroadcastUseCommands),
+        };
+        copyForSingleUse = {
+          ...defaultDataForSingleUse,
+          ...Object.fromEntries(doc._doc.permissionsForSingleUseCommands),
+        };
+      }
+      // when the document has supportedCommandsFor... columns
+      else if (doc._doc.supportedCommandsForBroadcastUse != null) {
+        // merge
+        doc._doc.supportedCommandsForBroadcastUse.forEach((commandName) => {
           copyForBroadcastUse[commandName] = true;
           copyForBroadcastUse[commandName] = true;
         });
         });
-        defaultSupportedCommandsNameForSingleUse.forEach((commandName) => {
+        doc._doc.supportedCommandsForSingleUse.forEach((commandName) => {
           copyForSingleUse[commandName] = true;
           copyForSingleUse[commandName] = true;
         });
         });
       }
       }
-      // // when the document has supportedCommandsFor... columns
+      // when the document does NOT have supportedCommandsFor... columns
       else {
       else {
-        doc._doc.supportedCommandsForBroadcastUse.forEach((commandName) => {
+        // turn on all
+        defaultSupportedCommandsNameForBroadcastUse.forEach((commandName) => {
           copyForBroadcastUse[commandName] = true;
           copyForBroadcastUse[commandName] = true;
         });
         });
-        doc._doc.supportedCommandsForSingleUse.forEach((commandName) => {
+        defaultSupportedCommandsNameForSingleUse.forEach((commandName) => {
           copyForSingleUse[commandName] = true;
           copyForSingleUse[commandName] = true;
         });
         });
       }
       }
@@ -52,35 +68,35 @@ module.exports = {
       return {
       return {
         updateOne: {
         updateOne: {
           filter: { _id: doc._id },
           filter: { _id: doc._id },
-          update: [
-            {
-              $set: {
-                permissionsForBroadcastUseCommands: copyForBroadcastUse,
-                permissionsForSingleUseCommands: copyForSingleUse,
-              },
+          update: {
+            $set: {
+              permissionsForBroadcastUseCommands: copyForBroadcastUse,
+              permissionsForSingleUseCommands: copyForSingleUse,
             },
             },
-            {
-              $unset: ['supportedCommandsForBroadcastUse', 'supportedCommandsForSingleUse'],
+            $unset: {
+              supportedCommandsForBroadcastUse: '',
+              supportedCommandsForSingleUse: '',
             },
             },
-          ],
+          },
         },
         },
       };
       };
     });
     });
 
 
-    await SlackAppIntegration.bulkWrite(operations);
+    await db.collection('slackappintegrations').bulkWrite(operations);
 
 
     logger.info('Migration has successfully applied');
     logger.info('Migration has successfully applied');
   },
   },
 
 
   async down(db, next) {
   async down(db, next) {
     logger.info('Rollback migration');
     logger.info('Rollback migration');
-    // return next();
-    mongoose.connect(getMongoUri(), mongoOptions);
+    await mongoose.connect(getMongoUri(), mongoOptions);
 
 
     const SlackAppIntegration = getModelSafely('SlackAppIntegration') || require('~/server/models/slack-app-integration')();
     const SlackAppIntegration = getModelSafely('SlackAppIntegration') || require('~/server/models/slack-app-integration')();
 
 
     const slackAppIntegrations = await SlackAppIntegration.find();
     const slackAppIntegrations = await SlackAppIntegration.find();
 
 
+    if (slackAppIntegrations.length === 0) return next();
+
     // create operations
     // create operations
     const operations = slackAppIntegrations.map((doc) => {
     const operations = slackAppIntegrations.map((doc) => {
       const dataForBroadcastUse = [];
       const dataForBroadcastUse = [];
@@ -99,22 +115,21 @@ module.exports = {
       return {
       return {
         updateOne: {
         updateOne: {
           filter: { _id: doc._id },
           filter: { _id: doc._id },
-          update: [
-            {
-              $set: {
-                supportedCommandsForBroadcastUse: dataForBroadcastUse,
-                supportedCommandsForSingleUse: dataForSingleUse,
-              },
+          update: {
+            $set: {
+              supportedCommandsForBroadcastUse: dataForBroadcastUse,
+              supportedCommandsForSingleUse: dataForSingleUse,
             },
             },
-            {
-              $unset: ['permissionsForBroadcastUseCommands', 'permissionsForSingleUseCommands'],
+            $unset: {
+              permissionsForBroadcastUseCommands: '',
+              permissionsForSingleUseCommands: '',
             },
             },
-          ],
+          },
         },
         },
       };
       };
     });
     });
 
 
-    await SlackAppIntegration.bulkWrite(operations);
+    await db.collection('slackappintegrations').bulkWrite(operations);
 
 
     next();
     next();
     logger.info('Migration has successfully applied');
     logger.info('Migration has successfully applied');

+ 122 - 0
packages/app/src/test/integration/migrations/20210913153942-migrate-slack-app-integration-schema.test.ts

@@ -0,0 +1,122 @@
+import mongoose from 'mongoose';
+import { Collection } from 'mongodb';
+import { getMongoUri, mongoOptions } from '@growi/core';
+
+const migrate = require('../../../migrations/20210913153942-migrate-slack-app-integration-schema');
+
+describe('migrate-slack-app-integration-schema', () => {
+
+  let collection: Collection;
+
+  beforeAll(async() => {
+    await mongoose.connect(getMongoUri(), mongoOptions);
+    collection = mongoose.connection.db.collection('slackappintegrations');
+
+    await collection.insertMany([
+      {
+        tokenGtoP: 'tokenGtoP1', tokenPtoG: 'tokenPtoG1', permissionsForBroadcastUseCommands: { foo: true }, permissionsForSingleUseCommands: { bar: true },
+      },
+      {
+        tokenGtoP: 'tokenGtoP2', tokenPtoG: 'tokenPtoG2', supportedCommandsForBroadcastUse: ['foo'], supportedCommandsForSingleUse: ['bar'],
+      },
+      {
+        tokenGtoP: 'tokenGtoP3', tokenPtoG: 'tokenPtoG3',
+      },
+    ]);
+  });
+
+  test('up is applied successfully', async() => {
+    // setup
+    const doc1 = await collection.findOne({ tokenGtoP: 'tokenGtoP1' });
+    const doc2 = await collection.findOne({ tokenGtoP: 'tokenGtoP2' });
+    const doc3 = await collection.findOne({ tokenGtoP: 'tokenGtoP3' });
+    expect(doc1 != null).toBeTruthy();
+    expect(doc2 != null).toBeTruthy();
+    expect(doc3 != null).toBeTruthy();
+    expect(doc1).toStrictEqual({
+      _id: doc1._id,
+      tokenGtoP: 'tokenGtoP1',
+      tokenPtoG: 'tokenPtoG1',
+      permissionsForBroadcastUseCommands: {
+        foo: true,
+      },
+      permissionsForSingleUseCommands: {
+        bar: true,
+      },
+    });
+    expect(doc2).toStrictEqual({
+      _id: doc2._id,
+      tokenGtoP: 'tokenGtoP2',
+      tokenPtoG: 'tokenPtoG2',
+      supportedCommandsForBroadcastUse: [
+        'foo',
+      ],
+      supportedCommandsForSingleUse: [
+        'bar',
+      ],
+    });
+    expect(doc3).toStrictEqual({
+      _id: doc3._id,
+      tokenGtoP: 'tokenGtoP3',
+      tokenPtoG: 'tokenPtoG3',
+    });
+
+    // when
+    await migrate.up(mongoose.connection.db);
+
+    // then
+    const fixedDoc1 = await collection.findOne({ tokenGtoP: 'tokenGtoP1' });
+    const fixedDoc2 = await collection.findOne({ tokenGtoP: 'tokenGtoP2' });
+    const fixedDoc3 = await collection.findOne({ tokenGtoP: 'tokenGtoP3' });
+    expect(fixedDoc1 != null).toBeTruthy();
+    expect(fixedDoc2 != null).toBeTruthy();
+    expect(fixedDoc3 != null).toBeTruthy();
+    expect(fixedDoc1.supportedCommandsForBroadcastUse).toBeUndefined();
+    expect(fixedDoc1.supportedCommandsForSingleUse).toBeUndefined();
+    expect(fixedDoc2.supportedCommandsForBroadcastUse).toBeUndefined();
+    expect(fixedDoc2.supportedCommandsForSingleUse).toBeUndefined();
+    expect(fixedDoc3.supportedCommandsForBroadcastUse).toBeUndefined();
+    expect(fixedDoc3.supportedCommandsForSingleUse).toBeUndefined();
+    expect(fixedDoc1).toStrictEqual({
+      _id: doc1._id,
+      tokenGtoP: 'tokenGtoP1',
+      tokenPtoG: 'tokenPtoG1',
+      permissionsForBroadcastUseCommands: {
+        foo: true,
+        search: false,
+      },
+      permissionsForSingleUseCommands: {
+        bar: true,
+        create: false,
+        togetter: false,
+      },
+    });
+    expect(fixedDoc2).toStrictEqual({
+      _id: doc2._id,
+      tokenGtoP: 'tokenGtoP2',
+      tokenPtoG: 'tokenPtoG2',
+      permissionsForBroadcastUseCommands: {
+        foo: true,
+        search: false,
+      },
+      permissionsForSingleUseCommands: {
+        bar: true,
+        create: false,
+        togetter: false,
+      },
+    });
+    expect(fixedDoc3).toStrictEqual({
+      _id: doc3._id,
+      tokenGtoP: 'tokenGtoP3',
+      tokenPtoG: 'tokenPtoG3',
+      permissionsForBroadcastUseCommands: {
+        search: true,
+      },
+      permissionsForSingleUseCommands: {
+        create: true,
+        togetter: true,
+      },
+    });
+  });
+
+});

+ 1 - 5
packages/app/src/test/integration/setup-crowi.js

@@ -2,7 +2,7 @@ import Crowi from '~/server/crowi';
 
 
 let _instance = null;
 let _instance = null;
 
 
-async function getInstance(isNewInstance) {
+export async function getInstance(isNewInstance) {
   if (isNewInstance) {
   if (isNewInstance) {
     const crowi = new Crowi();
     const crowi = new Crowi();
     await crowi.initForTest();
     await crowi.initForTest();
@@ -16,7 +16,3 @@ async function getInstance(isNewInstance) {
   }
   }
   return _instance;
   return _instance;
 }
 }
-
-module.exports = {
-  getInstance,
-};