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

refs 124934: convert ExternalAccount model to ts

Futa Arai 2 лет назад
Родитель
Сommit
b2fda35d3f

+ 2 - 2
apps/app/src/components/Me/DisassociateModal.tsx

@@ -9,13 +9,13 @@ import {
 } from 'reactstrap';
 
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import { IExternalAccount } from '~/interfaces/external-account';
+import { IExternalAccountHasId } from '~/interfaces/external-account';
 import { usePersonalSettings, useSWRxPersonalExternalAccounts } from '~/stores/personal-settings';
 
 type Props = {
   isOpen: boolean,
   onClose: () => void,
-  accountForDisassociate: IExternalAccount,
+  accountForDisassociate: IExternalAccountHasId,
 }
 
 

+ 3 - 4
apps/app/src/features/external-user-group/server/service/external-user-group-sync-service.ts

@@ -1,6 +1,5 @@
-import mongoose from 'mongoose';
-
 import { IUserHasId } from '~/interfaces/user';
+import ExternalAccount from '~/server/models/external-account';
 import { excludeTestIdsFromTargetIds } from '~/server/util/compare-objectId';
 
 import { configManager } from '../../../../server/service/config-manager';
@@ -92,7 +91,6 @@ abstract class ExternalUserGroupSyncService {
    */
   async getMemberUser(userInfo: ExternalUserInfo): Promise<IUserHasId | null> {
     const autoGenerateUserOnGroupSync = configManager?.getConfig('crowi', `external-user-group:${this.groupProviderType}:autoGenerateUserOnGroupSync`);
-    const ExternalAccount = mongoose.model('ExternalAccount');
 
     const getExternalAccount = async() => {
       if (autoGenerateUserOnGroupSync && externalAccountService != null) {
@@ -106,7 +104,8 @@ abstract class ExternalUserGroupSyncService {
     const externalAccount = await getExternalAccount();
 
     if (externalAccount != null) {
-      return externalAccount.getPopulatedUser();
+      await externalAccount.populate('user');
+      return externalAccount.user;
     }
     return null;
   }

+ 4 - 3
apps/app/src/interfaces/external-account.ts

@@ -1,11 +1,12 @@
-import { Ref } from '@growi/core';
+import { HasObjectId, Ref } from '@growi/core';
 
 import { IUser } from '~/interfaces/user';
 
 
-export type IExternalAccount<ID = string> = {
-  _id: ID,
+export type IExternalAccount = {
   providerType: string,
   accountId: string,
   user: Ref<IUser>,
 }
+
+export type IExternalAccountHasId = IExternalAccount & HasObjectId

+ 0 - 187
apps/app/src/server/models/external-account.js

@@ -1,187 +0,0 @@
-// disable no-return-await for model functions
-/* eslint-disable no-return-await */
-import { NullUsernameToBeRegisteredError } from '~/server/models/errors';
-
-const debug = require('debug')('growi:models:external-account');
-const mongoose = require('mongoose');
-const mongoosePaginate = require('mongoose-paginate-v2');
-const uniqueValidator = require('mongoose-unique-validator');
-
-const ObjectId = mongoose.Schema.Types.ObjectId;
-
-/*
- * define schema
- */
-const schema = new mongoose.Schema({
-  providerType: { type: String, required: true },
-  accountId: { type: String, required: true },
-  user: { type: ObjectId, ref: 'User', required: true },
-}, {
-  timestamps: { createdAt: true, updatedAt: false },
-});
-// compound index
-schema.index({ providerType: 1, accountId: 1 }, { unique: true });
-// apply plugins
-schema.plugin(mongoosePaginate);
-schema.plugin(uniqueValidator);
-
-/**
- * The Exception class thrown when User.username is duplicated when creating user
- *
- * @class DuplicatedUsernameException
- */
-class DuplicatedUsernameException {
-
-  constructor(message, user) {
-    this.name = this.constructor.name;
-    this.message = message;
-    this.user = user;
-  }
-
-}
-
-/**
- * ExternalAccount Class
- *
- * @class ExternalAccount
- */
-class ExternalAccount {
-
-  /**
-   * limit items num for pagination
-   *
-   * @readonly
-   * @static
-   * @memberof ExternalAccount
-   */
-  static get DEFAULT_LIMIT() {
-    return 50;
-  }
-
-  static set crowi(crowi) {
-    this._crowi = crowi;
-  }
-
-  static get crowi() {
-    return this._crowi;
-  }
-
-  /**
-   * get the populated user entity
-   *
-   * @returns Promise<User>
-   * @memberof ExternalAccount
-   */
-  getPopulatedUser() {
-    return this.populate('user')
-      .then((account) => {
-        return account.user;
-      });
-  }
-
-  /**
-   * find an account or register if not found
-   *
-   * @static
-   * @param {string} providerType
-   * @param {string} accountId
-   * @param {object} usernameToBeRegistered the username of User entity that will be created when accountId is not found
-   * @param {object} nameToBeRegistered the name of User entity that will be created when accountId is not found
-   * @param {object} mailToBeRegistered the mail of User entity that will be created when accountId is not found
-   * @param {boolean} isSameUsernameTreatedAsIdenticalUser
-   * @param {boolean} isSameEmailTreatedAsIdenticalUser
-   * @returns {Promise<ExternalAccount>}
-   * @memberof ExternalAccount
-   */
-  static findOrRegister(providerType, accountId,
-      usernameToBeRegistered, nameToBeRegistered, mailToBeRegistered,
-      isSameUsernameTreatedAsIdenticalUser, isSameEmailTreatedAsIdenticalUser) {
-    //
-    return this.findOne({ providerType, accountId })
-      .then((account) => {
-        // ExternalAccount is found
-        if (account != null) {
-          debug(`ExternalAccount '${accountId}' is found `, account);
-          return account;
-        }
-
-        if (usernameToBeRegistered == null) {
-          throw new NullUsernameToBeRegisteredError('username_should_not_be_null');
-        }
-
-        const User = ExternalAccount.crowi.model('User');
-
-        let promise = User.findOne({ username: usernameToBeRegistered });
-        if (isSameUsernameTreatedAsIdenticalUser && isSameEmailTreatedAsIdenticalUser) {
-          promise = promise
-            .then((user) => {
-              if (user == null) { return User.findOne({ email: mailToBeRegistered }) }
-              return user;
-            });
-        }
-        else if (isSameEmailTreatedAsIdenticalUser) {
-          promise = User.findOne({ email: mailToBeRegistered });
-        }
-
-        return promise
-          .then((user) => {
-            // when the User that have the same `username` exists
-            if (user != null) {
-              throw new DuplicatedUsernameException(`User '${usernameToBeRegistered}' already exists`, user);
-            }
-            if (nameToBeRegistered == null) {
-              // eslint-disable-next-line no-param-reassign
-              nameToBeRegistered = '';
-            }
-
-            // create a new User with STATUS_ACTIVE
-            debug(`ExternalAccount '${accountId}' is not found, it is going to be registered.`);
-            return User.createUser(nameToBeRegistered, usernameToBeRegistered, mailToBeRegistered, undefined, undefined, User.STATUS_ACTIVE);
-          })
-          .then((newUser) => {
-            return this.associate(providerType, accountId, newUser);
-          });
-      });
-  }
-
-  /**
-   * Create ExternalAccount document and associate to existing User
-   *
-   * @param {string} providerType
-   * @param {string} accountId
-   * @param {object} user
-   */
-  static associate(providerType, accountId, user) {
-    return this.create({ providerType, accountId, user: user._id });
-  }
-
-  /**
-   * find all entities with pagination
-   *
-   * @see https://github.com/edwardhotchkiss/mongoose-paginate
-   *
-   * @static
-   * @param {any} opts mongoose-paginate options object
-   * @returns {Promise<any>} mongoose-paginate result object
-   * @memberof ExternalAccount
-   */
-  static findAllWithPagination(opts) {
-    const query = {};
-    const options = Object.assign({ populate: 'user' }, opts);
-    if (options.sort == null) {
-      options.sort = { accountId: 1, createdAt: 1 };
-    }
-    if (options.limit == null) {
-      options.limit = ExternalAccount.DEFAULT_LIMIT;
-    }
-
-    return this.paginate(query, options);
-  }
-
-}
-
-module.exports = function(crowi) {
-  ExternalAccount.crowi = crowi;
-  schema.loadClass(ExternalAccount);
-  return mongoose.model('ExternalAccount', schema);
-};

+ 153 - 0
apps/app/src/server/models/external-account.ts

@@ -0,0 +1,153 @@
+// disable no-return-await for model functions
+/* eslint-disable no-return-await */
+import { IUserHasId } from '@growi/core';
+import { Schema, Model, Document } from 'mongoose';
+
+import { IExternalAccount, IExternalAccountHasId } from '~/interfaces/external-account';
+import { NullUsernameToBeRegisteredError } from '~/server/models/errors';
+
+import { getOrCreateModel } from '../util/mongoose-utils';
+
+const debug = require('debug')('growi:models:external-account');
+const mongoose = require('mongoose');
+const mongoosePaginate = require('mongoose-paginate-v2');
+const uniqueValidator = require('mongoose-unique-validator');
+
+const ObjectId = mongoose.Schema.Types.ObjectId;
+
+interface ExternalAccountDocument extends IExternalAccount, Document {}
+
+export interface ExternalAccountModel extends Model<ExternalAccountDocument> {
+  [x:string]: any, // for old methods
+}
+
+const schema = new Schema<ExternalAccountDocument, ExternalAccountModel>({
+  providerType: { type: String, required: true },
+  accountId: { type: String, required: true },
+  user: { type: ObjectId, ref: 'User', required: true },
+}, {
+  timestamps: { createdAt: true, updatedAt: false },
+});
+// compound index
+schema.index({ providerType: 1, accountId: 1 }, { unique: true });
+// apply plugins
+schema.plugin(mongoosePaginate);
+schema.plugin(uniqueValidator);
+
+/**
+ * limit items num for pagination
+ */
+const DEFAULT_LIMIT = 50;
+
+/**
+ * The Exception class thrown when User.username is duplicated when creating user
+ *
+ * @class DuplicatedUsernameException
+ */
+class DuplicatedUsernameException {
+
+  name: string;
+
+  message: string;
+
+  user: IUserHasId;
+
+  constructor(message, user) {
+    this.name = this.constructor.name;
+    this.message = message;
+    this.user = user;
+  }
+
+}
+
+/**
+ * find an account or register if not found
+ */
+schema.statics.findOrRegister = function(
+    isSameUsernameTreatedAsIdenticalUser: boolean,
+    isSameEmailTreatedAsIdenticalUser: boolean,
+    providerType: string,
+    accountId: string,
+    usernameToBeRegistered?: string,
+    nameToBeRegistered?: string,
+    mailToBeRegistered?: string,
+): Promise<IExternalAccountHasId> {
+//
+  return this.findOne({ providerType, accountId })
+    .then((account) => {
+    // ExternalAccount is found
+      if (account != null) {
+        debug(`ExternalAccount '${accountId}' is found `, account);
+        return account;
+      }
+
+      if (usernameToBeRegistered == null) {
+        throw new NullUsernameToBeRegisteredError('username_should_not_be_null');
+      }
+
+      const User = mongoose.model('User');
+
+      let promise = User.findOne({ username: usernameToBeRegistered });
+      if (isSameUsernameTreatedAsIdenticalUser && isSameEmailTreatedAsIdenticalUser) {
+        promise = promise
+          .then((user) => {
+            if (user == null) { return User.findOne({ email: mailToBeRegistered }) }
+            return user;
+          });
+      }
+      else if (isSameEmailTreatedAsIdenticalUser) {
+        promise = User.findOne({ email: mailToBeRegistered });
+      }
+
+      return promise
+        .then((user) => {
+        // when the User that have the same `username` exists
+          if (user != null) {
+            throw new DuplicatedUsernameException(`User '${usernameToBeRegistered}' already exists`, user);
+          }
+          if (nameToBeRegistered == null) {
+          // eslint-disable-next-line no-param-reassign
+            nameToBeRegistered = '';
+          }
+
+          // create a new User with STATUS_ACTIVE
+          debug(`ExternalAccount '${accountId}' is not found, it is going to be registered.`);
+          return User.createUser(nameToBeRegistered, usernameToBeRegistered, mailToBeRegistered, undefined, undefined, User.STATUS_ACTIVE);
+        })
+        .then((newUser) => {
+          return this.associate(providerType, accountId, newUser);
+        });
+    });
+};
+
+/**
+ * Create ExternalAccount document and associate to existing User
+ */
+schema.statics.associate = function(providerType: string, accountId: string, user: IUserHasId) {
+  return this.create({ providerType, accountId, user: user._id });
+};
+
+/**
+ * find all entities with pagination
+ *
+ * @see https://github.com/edwardhotchkiss/mongoose-paginate
+ *
+ * @static
+ * @param {any} opts mongoose-paginate options object
+ * @returns {Promise<any>} mongoose-paginate result object
+ * @memberof ExternalAccount
+ */
+schema.statics.findAllWithPagination = function(opts) {
+  const query = {};
+  const options = Object.assign({ populate: 'user' }, opts);
+  if (options.sort == null) {
+    options.sort = { accountId: 1, createdAt: 1 };
+  }
+  if (options.limit == null) {
+    options.limit = DEFAULT_LIMIT;
+  }
+
+  return this.paginate(query, options);
+};
+
+export default getOrCreateModel<ExternalAccountDocument, ExternalAccountModel>('ExternalAccount', schema);

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

@@ -6,7 +6,6 @@ module.exports = {
   // PageArchive: require('./page-archive'),
   PageTagRelation: require('./page-tag-relation'),
   User: require('./user'),
-  ExternalAccount: require('./external-account'),
   UserGroupRelation: require('./user-group-relation'),
   Revision: require('./revision'),
   Tag: require('./tag'),

+ 2 - 1
apps/app/src/server/routes/apiv3/personal-setting.js

@@ -10,6 +10,7 @@ import loggerFactory from '~/utils/logger';
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 import EditorSettings from '../../models/editor-settings';
+import ExternalAccount from '../../models/external-account';
 import InAppNotificationSettings from '../../models/in-app-notification-settings';
 
 
@@ -75,7 +76,7 @@ module.exports = (crowi) => {
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const addActivity = generateAddActivityMiddleware(crowi);
 
-  const { User, ExternalAccount } = crowi.models;
+  const { User } = crowi.models;
 
   const activityEvent = crowi.event('activity');
 

+ 1 - 1
apps/app/src/server/routes/apiv3/users.js

@@ -2,6 +2,7 @@ import { ErrorV3 } from '@growi/core';
 
 import { SupportedAction } from '~/interfaces/activity';
 import Activity from '~/server/models/activity';
+import ExternalAccount from '~/server/models/external-account';
 import { configManager } from '~/server/service/config-manager';
 import loggerFactory from '~/utils/logger';
 
@@ -87,7 +88,6 @@ module.exports = (crowi) => {
   const {
     User,
     Page,
-    ExternalAccount,
     UserGroupRelation,
   } = crowi.models;
 

+ 5 - 5
apps/app/src/server/routes/login-passport.js

@@ -224,7 +224,7 @@ module.exports = function(crowi, app) {
       return next(new ErrorV3('message.external_account_not_exist'));
     }
 
-    const user = await externalAccount.getPopulatedUser();
+    const user = (await externalAccount.populate('user')).user;
 
     // login
     await req.logIn(user, (err) => {
@@ -392,7 +392,7 @@ module.exports = function(crowi, app) {
       return next(new ExternalAccountLoginError('message.sign_in_failure'));
     }
 
-    const user = await externalAccount.getPopulatedUser();
+    const user = (await externalAccount.populate('user')).user;
 
     // login
     req.logIn(user, async(err) => {
@@ -435,7 +435,7 @@ module.exports = function(crowi, app) {
       return next(new ExternalAccountLoginError('message.sign_in_failure'));
     }
 
-    const user = await externalAccount.getPopulatedUser();
+    const user = (await externalAccount.populate('user')).user;
 
     // login
     req.logIn(user, async(err) => {
@@ -486,7 +486,7 @@ module.exports = function(crowi, app) {
     }
 
     // login
-    const user = await externalAccount.getPopulatedUser();
+    const user = (await externalAccount.populate('user')).user;
     req.logIn(user, async(err) => {
       if (err) { debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
 
@@ -544,7 +544,7 @@ module.exports = function(crowi, app) {
       return next(new ExternalAccountLoginError('message.sign_in_failure'));
     }
 
-    const user = await externalAccount.getPopulatedUser();
+    const user = (await externalAccount.populate('user')).user;
 
     // login
     req.logIn(user, (err) => {

+ 0 - 1
apps/app/src/server/routes/me.js

@@ -51,7 +51,6 @@
 module.exports = function(crowi, app) {
   const models = crowi.models;
   const UserGroupRelation = models.UserGroupRelation;
-  const ExternalAccount = models.ExternalAccount;
   const ApiResponse = require('../util/apiResponse');
 
   // , pluginService = require('../service/plugin')

+ 8 - 7
apps/app/src/server/service/external-account.ts

@@ -1,11 +1,11 @@
 import { ErrorV3 } from '^/../../packages/core/dist';
-import mongoose from 'mongoose';
 
 import { LoginErrorCode } from '~/interfaces/errors/login-error';
-import { IExternalAccount } from '~/interfaces/external-account';
+import { IExternalAccountHasId } from '~/interfaces/external-account';
 import loggerFactory from '~/utils/logger';
 
 import { NullUsernameToBeRegisteredError } from '../models/errors';
+import ExternalAccount from '../models/external-account';
 
 import PassportService from './passport';
 
@@ -20,23 +20,24 @@ class ExternalAccountService {
     this.passportService = passportService;
   }
 
-  async getOrCreateUser(userInfo: {id: string, username: string, name?: string, email?: string}, providerId: string): Promise<IExternalAccount | undefined> {
+  async getOrCreateUser(
+      userInfo: {id: string, username: string, name?: string, email?: string},
+      providerId: string,
+  ): Promise<IExternalAccountHasId | undefined> {
     // get option
     const isSameUsernameTreatedAsIdenticalUser = this.passportService.isSameUsernameTreatedAsIdenticalUser(providerId);
     const isSameEmailTreatedAsIdenticalUser = this.passportService.isSameEmailTreatedAsIdenticalUser(providerId);
 
-    const ExternalAccount = mongoose.model('ExternalAccount') as any;
-
     try {
       // find or register(create) user
       const externalAccount = await ExternalAccount.findOrRegister(
+        isSameUsernameTreatedAsIdenticalUser,
+        isSameEmailTreatedAsIdenticalUser,
         providerId,
         userInfo.id,
         userInfo.username,
         userInfo.name,
         userInfo.email,
-        isSameUsernameTreatedAsIdenticalUser,
-        isSameEmailTreatedAsIdenticalUser,
       );
       return externalAccount;
     }

+ 2 - 2
apps/app/src/stores/personal-settings.tsx

@@ -2,7 +2,7 @@ import { ErrorV3 } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import useSWR, { SWRConfiguration, SWRResponse } from 'swr';
 
-import { IExternalAccount } from '~/interfaces/external-account';
+import { IExternalAccountHasId } from '~/interfaces/external-account';
 import { IUser } from '~/interfaces/user';
 import { useIsGuestUser } from '~/stores/context';
 import loggerFactory from '~/utils/logger';
@@ -104,7 +104,7 @@ export const usePersonalSettings = (config?: SWRConfiguration): SWRResponse<IUse
   };
 };
 
-export const useSWRxPersonalExternalAccounts = (): SWRResponse<IExternalAccount[], Error> => {
+export const useSWRxPersonalExternalAccounts = (): SWRResponse<IExternalAccountHasId[], Error> => {
   return useSWR(
     '/personal-setting/external-accounts',
     endpoint => apiv3Get(endpoint).then(response => response.data.externalAccounts),