续集错误-“ TypeError:hooks.concat不是函数”

时间:2018-08-27 08:57:38

标签: javascript node.js sequelize.js

后续版本:4.38.0

在我的node.js Web应用程序中。 我编写模型规格并运行npm测试。 在Create a model with associated data using set()单元测试中。 但出现以下错误:

TypeError: hooks.concat is not a function
      at Function.runHooks (node_modules/sequelize/lib/hooks.js:102:23)
      at Promise.try.then (node_modules/sequelize/lib/model.js:3624:33)
      at tryCatcher (node_modules/bluebird/js/release/util.js:16:23)
      at Promise._settlePromiseFromHandler (node_modules/bluebird/js/release/promise.js:512:31)
      at Promise._settlePromise (node_modules/bluebird/js/release/promise.js:569:18)
      at Promise._settlePromise0 (node_modules/bluebird/js/release/promise.js:614:10)
      at Promise._settlePromises (node_modules/bluebird/js/release/promise.js:693:18)
      at Async._drainQueue (node_modules/bluebird/js/release/async.js:133:16)
      at Async._drainQueues (node_modules/bluebird/js/release/async.js:143:10)
      at Immediate.Async.drainQueues (node_modules/bluebird/js/release/async.js:17:14)

以下是我的单元测试代码:

it('Create a model with associated data using set()', async () => {
      // TODO:
      // 1. create a User and use set() method to set 1 associated model data.
      // 2. use model.findOne() with include to get model data with associated data.
      // 3. use data as Passport data, use `fakeData.create3` as user data.
      const data = {
        token: '1',
        workspaceName: 'ws1',
        passwordHash: 'ws1ws1ws1',
        provider: 'local',
      };

      let user = await models[SPEC_MODEL_NAME].create({
        ...fakeData.create3,
        Passports: data
      }, {
        include: [models.Passport]
      });

      const passport = await models.Passport.create(data);
      await user.setPassports(passport);

      const userWithPassport = await models[SPEC_MODEL_NAME].findOne(
        {
          where: {
            nickName: fakeData.create3.nickName
          },
          include: [models.Passport],

        }
      );

      expect(userWithPassport.nickName).to.be.equal(fakeData.create3.nickName);
      expect(userWithPassport.email).to.be.equal(fakeData.create3.email);
      expect(userWithPassport.Passports.length).to.equal(1);
      expect(userWithPassport.Passports[0].token).to.equal(data.token);
      expect(userWithPassport.Passports[0].workspaceName).to.equal(data.workspaceName);
      expect(userWithPassport.Passports[0].passwordHash).to.equal(data.passwordHash);
    });
  });

以下是我的用户模型:

module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    nickName: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
    },
  });

  User.associate = function (models) {
    User.hasMany(models.UserOrder);
    User.hasMany(models.Passport);
  };
  return User;
};

以下是我的护照型号:

const bcrypt = require('bcrypt');
const crypto = require('crypto');

module.exports = (sequelize, DataTypes) => {
  const Passport = sequelize.define('Passport', {
    provider: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    token: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    password: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    workspaceName: {
      type: DataTypes.STRING,
      allowNull: true,
    },
  });

  Passport.associate = function (models) {
    Passport.belongsTo(models.User);
  };

  Passport.options.classMethod = {
    hashPassword: async (passport) => {
      // eslint-disable-next-line
      await new Promise((defer, reject) => {
        if (passport.passwordHash) {
          bcrypt.hash(passport.passwordHash, 10, (err, hash) => {
            if (err) reject(err);
            // eslint-disable-next-line
            passport.passwordHash = hash;
            defer();
          });
        }
        defer();
      });
    },
    async createDefaultLocalProviderIfNotExist (user) {
      try {
        const localPassport = await Passport.findOne({
          where: {
            provider: 'local',
            userId: user.id,
          },
        });
        console.log('localPassport ==', localPassport);
        if (localPassport == null) {
          const newLocalPassport = {
            provider: 'local',
            password: 'password',
            userId: user.id,
          };
          console.log('=== newLocalPassport ===', newLocalPassport);
          await Passport.create(newLocalPassport);
        }
      } catch (e) {
        throw e;
      }
    },
  };

  Passport.options.instanceMethod = {
    async validatePassword (password) {
      try {
        const that = this;
        // eslint-disable-next-line
        let result = await new Promise((defer, reject) => {
          if (password === that.password) {
            defer(true);
          }
          // eslint-disable-next-line
          bcrypt.compare(password, that.password, (err, result) => {
            if (err) defer(false);
            else defer(result);
          });
        });
        if (result) return result;
        console.log('=== this.salt ===', that.salt);
        console.log('=== this.salt ===', result);
        if (!this.salt) return result;
        console.log('=== check two ===');
        const comparePassword = crypto.pbkdf2Sync(password, Buffer.from(this.salt, 'base64'), 10000, 64).toString('base64');
        if (comparePassword === that.password) {
          result = true;
        }
        return result;
      } catch (e) {
        throw e;
      }
    },
  };

  Passport.options.hooks = {
    async beforeCreate (passport) {
      return new Promise(async (resolve, reject) => {
        try {
          await Passport.hashPassword(passport);
          return resolve(passport);
        } catch (e) {
          return reject(e);
        }
      });
    },
    async beforeUpdate (passport) {
      return new Promise(async (resolve, reject) => {
        try {
          await Passport.hashPassword(passport);
          return resolve(passport);
        } catch (e) {
          return reject(e);
        }
      });
    },
  };
  return Passport;
};

以下是我的userOrder模型:

module.exports = (sequelize, DataTypes) => {
  const UserOrder = sequelize.define('UserOrder', {
    price: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    count: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    subTotal: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    remark: DataTypes.STRING,
  });

  UserOrder.associate = function (models) {
    UserOrder.belongsTo(models.User);
    UserOrder.belongsTo(models.GroupOrder);
    UserOrder.belongsTo(models.Food);
  };
  return UserOrder;
};

谢谢!

1 个答案:

答案 0 :(得分:1)

查看文档中的示例并稍微看一下源代码,我认为Sequelize希望您在调用define时传递完整的选项对象。为Passport模型创建一个选项对象,并在将其作为第二个参数传递给define调用之前,将钩子分配给它(而不是在定义后修改选项)。这将允许它在初始化模型时正确检查该选项对象。