edwardanthony / nestjs-seeder

An extension library for NestJS to perform seeding.
MIT License
102 stars 21 forks source link

@Factory() is not working in typeorm entity #2

Closed osamahafez closed 4 years ago

osamahafez commented 4 years ago

I am using typeorm(mysql configs) and my user.entity.ts looks like this:

@Entity('users')
export class User {
  @Field(type => ID)
  @PrimaryGeneratedColumn()
  readonly id: number;

  @Field()
  @Column()
  @Factory(faker => faker.name.findName())
  firstName: string;

  @Field()
  @Column()
  @Factory(faker => faker.name.findName())
  lastName: string;

  @Field()
  @Index({ unique: true })
  @Column()
  @Factory(faker => faker.internet.email())
  email: string;

  @Exclude({ toPlainOnly: true })
  @Column()
  @Factory(faker => faker.internet.password())
  password: string;

but @Factory() doesn't seem to work because I get this error when i run yarn seed

UnhandledPromiseRejectionWarning: QueryFailedError: ER_NO_DEFAULT_FOR_FIELD: Field 'first_name' doesn't have a default value

maybe the solution is to export the factory schema like you specified in your example here

export const userSchema = SchemaFactory.createForClass(User);

and that's for mongodb only and I can't find anything like it in typeorm

edwardanthony commented 4 years ago

Thanks for submitting an issue. Can you show me your seeder file?

osamahafez commented 4 years ago

users.seeder.ts

import { Injectable } from '@nestjs/common';
import { Seeder, DataFactory } from 'nestjs-seeder';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from 'src/users/users.repository';

@Injectable()
export class UsersSeeder implements Seeder {
  constructor(@InjectRepository(UserRepository) private readonly userRepository: UserRepository) {}

  async seed(): Promise<any> {
    // Generate 1 user.
    const users = DataFactory.createForClass(UserRepository).generate(1);

    // Insert into the database.
    return this.userRepository.insert(users);
  }

  async drop(): Promise<any> {
    return this.userRepository.delete({});
  }
}

seeder.ts

import { seeder } from 'nestjs-seeder';
import { UsersSeeder } from './seeders/users.seeder';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserRepository } from './users/users.repository';
import { ConfigModule } from 'nestjs-config';
import { TypeOrmOptionsService } from './bootstrap/typeorm/typeorm-options.service';
import * as path from 'path';

seeder({
  imports: [
    ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')),
    TypeOrmModule.forRootAsync({
        imports: [ConfigModule],
        useClass: TypeOrmOptionsService,
      }),
      TypeOrmModule.forFeature([UserRepository])
  ],
}).run([UsersSeeder]);

typeorm-options.service.ts

import { Injectable, Logger } from '@nestjs/common';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import optionFactory from './typeorm.mysql';
import { ConfigService } from 'nestjs-config';

@Injectable()
export class TypeOrmOptionsService implements TypeOrmOptionsFactory {
    constructor(private readonly config: ConfigService) { }

    createTypeOrmOptions(): TypeOrmModuleOptions {
        Logger.debug('Database Init', this.constructor.name);
        return optionFactory(this.config);
        throw new Error();
    }
}

mysql configs

import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { TypeOrmNamingStrategy } from './typeorm-naming-strategy';
import { ConfigService } from 'nestjs-config';

let optionFactory = (config: ConfigService) => {
  let options: TypeOrmModuleOptions = {
    type: 'mysql',
    host: config.get('database.host'),
    port: config.get('database.port'),
    username: config.get('database.user'),
    password: config.get('database.pass'),
    database: config.get('database.name'),
    entities: [`${__dirname}/../../**/*.entity.{ts,js}`],
    migrations: [`${__dirname}/../../migrations/*.{ts,js}`],
    namingStrategy: new TypeOrmNamingStrategy(),
    logging: config.get('app.debug'),
    synchronize: true,
    cli: {
      migrationsDir: 'src/migrations',
    },
  };
  return options;
};

export default optionFactory;
edwardanthony commented 4 years ago

Thanks. I'll test it locally on my computer.

In the meantime, you could try to do console.log on the generated data to make sure DataFactory.createForClass works properly.

// Generate 1 user.
const users = DataFactory.createForClass(UserRepository).generate(1);

console.log(users);
osamahafez commented 4 years ago

Thanks. I'll test it locally on my computer.

In the meantime, you could try to do console.log on the generated data to make sure DataFactory.createForClass works properly.

// Generate 1 user.
const users = DataFactory.createForClass(UserRepository).generate(1);

console.log(users);

console.log(users) returns empty array [{ }]

edwardanthony commented 4 years ago

Okay, so you passed invalid parameter to DataFactory.createClass() method. What you should pass is the user model class that contains this code below:

@Entity('users')
export class User {
  @Field(type => ID)
  @PrimaryGeneratedColumn()
  readonly id: number;

  @Field()
  @Column()
  @Factory(faker => faker.name.findName())
  firstName: string;

  @Field()
  @Column()
  @Factory(faker => faker.name.findName())
  lastName: string;
}

Let's say the name of this file is user.model.ts, then you can do:

import { Injectable } from '@nestjs/common';
import { Seeder, DataFactory } from 'nestjs-seeder';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from 'src/users/users.repository';
import { User } from 'src/users/user.model';

@Injectable()
export class UsersSeeder implements Seeder {
  constructor(@InjectRepository(UserRepository) private readonly userRepository: UserRepository) {}

  async seed(): Promise<any> {
    // Generate 1 user.
    // Notice that I pass "User" here, not "UserRepository".
    const users = DataFactory.createForClass(User).generate(1);

    // View the generated users.
    console.log(users);

    // Insert into the database.
    return this.userRepository.insert(users);
  }

  async drop(): Promise<any> {
    return this.userRepository.delete({});
  }
}

Please let me know if it works, and please also do the same console.log on the generated users so you can be sure.

osamahafez commented 4 years ago

Yes it works now. Many thanks.

edwardanthony commented 4 years ago

I'll close this issue since this is not a bug. If you have any other questions, feel free to post comments.

albjeremias commented 1 year ago

Yay! :) I was able to make a seeder running using this issue.. https://github.com/helpbuttons/helpbuttons/commit/df124ee3de9aeb18f4cabbb3338330a36c94137b

but I get this error when running the app in server mode..

src/data/seed/seeder.tsx:4:31 - error TS6142: Module './buttons.seed' was resolved to 'api/src/data/seed/buttons.seed.tsx', but '--jsx' is not set.

4 import { ButtonsSeeder } from './buttons.seed';
                                ~~~~~~~~~~~~~~~~

any ideas?