SoftwareBrothers / adminjs

AdminJS is an admin panel for apps written in node.js
https://adminjs.co
MIT License
8.21k stars 661 forks source link

Trying to add a sequelize model as a resource, UnhandledPromiseRejectionWarning: TypeError: Cannot convert undefined or null to object. #77

Closed stackedq closed 5 years ago

stackedq commented 5 years ago

First of thanks for this well done project, I got an issue trying to add a Sequelize model as a resource,

The error is:

UnhandledPromiseRejectionWarning: TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
    at Resource.properties (webpack:///./node_modules/admin-bro-sequelizejs/src/resource.js?:42:19)
    at ResourceDecorator.decorateProperties (/Users/kiarashws/dev/qukip/backend/node_modules/admin-bro/src/backend/decorators/resource-decorator.js:105:47)
    at new ResourceDecorator (/dev/node_modules/admin-bro/src/backend/decorators/resource-decorator.js:61:28)
    at Resource.assignDecorator (/dev/node_modules/admin-bro/src/backend/adapters/base-resource.js:234:23)
    at resources.map (/dev/node_modules/admin-bro/src/backend/utils/resources-factory.js:102:16)
    at Array.map (<anonymous>)
    at ResourcesFactory._decorateResources (/dev/node_modules/admin-bro/src/backend/utils/resources-factory.js:100:22)
    at ResourcesFactory.buildResources (/dev/node_modules/admin-bro/src/backend/utils/resources-factory.js:35:17)
    at new AdminBro (/dev/node_modules/admin-bro/src/admin-bro.js:111:39)
    at configAdmin (webpack:///./admin/config.js?:52:20)
    at runServer (webpack:///./server.js?:115:69)
    at process._tickCallback (internal/process/next_tick.js:178:7)

I followed example express app

import AdminBro from 'admin-bro'
import AdminBroExpress from 'admin-bro-expressjs'
import AdminBroSequelize from 'admin-bro-sequelizejs'
import sqModels from '../sq-models'

AdminBro.registerAdapter(AdminBroSequelize)

  const adminBro = new AdminBro({
    resources: [
      {
        resource: sqModels.User, options: {}
      }
    ],
    rootPath: '/admin',
    branding: {
      logo: '/logo.png',
      companyName: 'Admin'
    },
    dashboard: DashboardPage
  })

Any help would be appreciated.

wojtek-krysiak commented 5 years ago

Hi - thanks for issue :).

Can you write what import sqModels (from '../sq-models') are in your case and what is the schema of your model (sqModels.User from the example)

stackedq commented 5 years ago

sqModels index.js:

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/seq.json')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs.readdirSync(__dirname).filter(file => {
  return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
}).forEach(file => {
  const model = sequelize.import (path.join(__dirname, file));
  db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

And User model:


module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    name: {
      type: DataTypes.STRING,
      validate: {
        len: [0, 30]
      }
    },
    username: {
      type: DataTypes.STRING,
      allowNull: {
        args: false,
        msg: 'Please enter your username'
      },
      unique: {
        args: true,
        msg: 'Sorry, this username is taken'
      }
    },
    email: {
      type: DataTypes.STRING,
      unique: {
        args: true,
        msg: 'Sorry, this username is taken'
      },
      validate: {
        isEmail: {
          args: true,
          msg: 'Please enter a valid email address'
        }
      }
    },
    password: {
      type: DataTypes.STRING,
      allowNull: {
        args: false,
        msg: 'Please enter a password'
      },
      validate: {
        isNotShort: (value) => {
          if (value.length < 8) {
            throw new Error('Password should be at least 8 characters');
          }
        }
      }
    },
    bio: {
      type: DataTypes.STRING,
      validate: {
        len: [0, 150]
      }
    },
    cuid: {
      type: DataTypes.STRING,
      allowNull: true
    },
    phoneNumber: {
      type: DataTypes.STRING
    },
    isPrivate: {
      type: DataTypes.BOOLEAN
    },
    isSuperUser: {
      type: DataTypes.BOOLEAN,
      allowNull: true
    },
    needToVerifyEmail: {
      type: DataTypes.BOOLEAN
    },
    needToVerifyPhoneNumber: {
      type: DataTypes.BOOLEAN
    },
    emailVerificationNumber: {
      type: DataTypes.INTEGER
    },
    cellphoneVerificationNumber: {
      type: DataTypes.INTEGER
    },
    followerCount: {
      type: DataTypes.INTEGER,
      defaultValue: 0
    },
    followingCount: {
      type: DataTypes.INTEGER,
      defaultValue: 0
    },
    qoins: {
      type: DataTypes.INTEGER,
      defaultValue: 0
    },
    image: {
      type: DataTypes.STRING
    }
  }, {});
  User.associate = models => {
    User.hasMany(models.UserImage, {
      as: 'imagesPath',
      constraints: false,
      allowNull: true
    });
    User.hasMany(models.Relation, {
      as: 'selfRelations',
      foreignKey: 'fromUser',
      constraints: false,
      defaultValue: null
    });
    User.hasMany(models.Relation, {
      as: 'othersRelations',
      foreignKey: 'toUser',
      constraints: false,
      defaultValue: null
    });
  };
  User.findByUsername = async (username, passId) => {
    let filter = {
      where: {
        username: username
      },
      attributes: [
        'followerCount',
        'followingCount',
        'image',
        'qoins',
        'isPrivate',
        'name',
        'bio',
        'id',
        'username'
      ],
      include: ['imagesPath']
    }
    if (passId)
      filter.attributes.push('id')
    let user = await User.findOne(filter);
    return user
  }
  User.getFullUser = async username => {
    let user = await User.findOne({
      where: {
        username: username
      },
      attributes: {
        exclude: ['emailVerificationNumber', 'cellphoneVerificationNumber', 'createdAt', 'updatedAt', 'password']
      },
      include: ['imagesPath']
    });
    return user
  }
  return User;
};
wojtek-krysiak commented 5 years ago

most probably it is due to the fact that sequelize changed interface in the latest version. i will update it during the weekend and let you know

MattRay0295 commented 5 years ago

@wojtek-krysiak Awesome, I was getting the same thing with MySQLv5.7.23 and I was only grabbing the name from the users table, I couldn't figure out why I was getting that TypeError: Cannot convert undefined or null to object. 😅

wojtek-krysiak commented 5 years ago

Gents - fixed that in admin-bro-sequelizejs v0.2.2 - please update your package.json files and let me know if it still occurs.

stackedq commented 5 years ago

It works, thanks.