w3tecch / typeorm-seeding

🌱 A delightful way to seed test data into your database.
https://www.npmjs.com/package/typeorm-seeding
MIT License
887 stars 132 forks source link

Override params while seeding database #111

Open jorgebodega opened 3 years ago

jorgebodega commented 3 years ago

While I'm trying to seed a database with so many entities related, I found a problem and want to known if it is designed to work like this or if it is a bug.

Imagine that I have something like this:

define(EntityOne, (faker: typeof Faker) => {
  const entity = new EntityOne()
  const anotherField = factory(EntityTwo)() as any

  entity.title = faker.random.word()
  entity.anotherField = entityTwo

  return entity
})
define(EntityTwo, (faker: typeof Faker) => {
  const entity = new EntityTwo()

  entity.uniqueNumber = faker.random.number(10)

  return entity
})

and imagine that I have a UNIQUE flag on uniqueNumber on typeorm entity and generate only 10 possible values because it is something an iso-code.

If I try to run something like this:

const entityTwo = await factory(EntityTwo)().create()

const entityOne = await factory(EntityOne)().createMany(200, {
  anotherField: entityTwo,
})

It fails. As I can see in src/entity-factory.ts (L91):

private async makeEnity(overrideParams: EntityProperty<Entity> = {}, isSeeding = false): Promise<Entity> {
  if (this.factory) {
    let entity = await this.resolveEntity(this.factory(Faker, this.context), isSeeding)
    if (this.mapFunction) {
      entity = await this.mapFunction(entity)
    }

    for (const key in overrideParams) {
      if (overrideParams.hasOwnProperty(key)) {
        entity[key] = overrideParams[key]
      }
    }

    return entity
  }
  throw new Error('Could not found entity')
}

overrideParams is being checked AFTER persisting/creating entity. Is this the expected behaviour?

We can extrapolate this as if I override a param that is a factory with another previously created entity, the original value is still on DB. Imagine seeding the database with thousands of rows.

jorgebodega commented 3 years ago

I will try to update the message to the last commit

  private async makeEntity(overrideParams: EntityProperty<Entity> = {}, isSeeding = false): Promise<Entity> {
    if (!this.factory) {
      throw new Error('Could not found entity')
    }

    let entity = await this.resolveEntity(this.factory(Faker, this.context), isSeeding)
    if (this.mapFunction) {
      entity = await this.mapFunction(entity)
    }

    for (const key in overrideParams) {
      if (overrideParams.hasOwnProperty(key)) {
        entity[key] = overrideParams[key]
      }
    }

    return entity
  }

resolveEntity is creating/making some other entities that is related to the main entity, like this example:

define(EntityOne, (faker: typeof Faker) => {
  const entity = new EntityOne()
  const field2 = factory(EntityTwo)()
  const field3 = factory(EntityThree)().create()

  entity.title = faker.random.word()
  entity.field2 = field2
  entity.field3 = field3

  return entity
})

field2 depends on seeding, but could be possible to be persisted. field3 is being persisted EVERY single time. This could cause some problems because if I try to create an entity this way:

await factory(EntityOne)().create({
  field3: await factory(EntityThree)().create()
})

Two EntityThree are being created but only the second is related. This can cause problems with UNIQUE restrictions or GET requests that lists resources

jorgebodega commented 3 years ago

@RaphaelWoude please take a look on this, i will update my PR