Skip to content

Commit

Permalink
feat: add nested seeding
Browse files Browse the repository at this point in the history
  • Loading branch information
hirsch committed Apr 10, 2020
1 parent 69c9956 commit 9290214
Show file tree
Hide file tree
Showing 12 changed files with 72 additions and 62 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ define(Pet, (faker: typeof Faker, settings: undefined) => {
const pet = new Pet()
pet.name = name
pet.age = faker.random.number()
pet.user = factory(User)({ roles: ['admin'] }) // not yet implemented
pet.user = factory(User)({ roles: ['admin'] }) as any
return pet
})
```
Expand Down Expand Up @@ -230,8 +230,8 @@ Now you are able to execute your seeds with this command `npm run seed`.
| Option | Default | Description |
| ---------------------- | --------------- | --------------------------------------------------------------------------- |
| `--seed` or `-s` | null | Option to specify a specific seeder class to run individually. |
| `--name` or `-n` | null | Name of the typeorm connection. Required if there are multiple connections. |
| `--configName` or `-c` | `ormconfig.js` | Name to the typeorm config file. |
| `--connection` or `-c` | null | Name of the typeorm connection. Required if there are multiple connections. |
| `--configName` or `-n` | `ormconfig.js` | Name to the typeorm config file. |
| `--root` or `-r` | `process.cwd()` | Path to the typeorm config file. |

## ❯ License
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"semantic-release": "semantic-release",
"schema:drop": "ts-node ./node_modules/typeorm/cli.js schema:drop",
"schema:sync": "ts-node ./node_modules/typeorm/cli.js schema:sync",
"schema:log": "ts-node ./node_modules/typeorm/cli.js schema:log",
"seed:run": "ts-node ./src/cli.ts seed",
"seed:config": "ts-node ./src/cli.ts config"
"schema:drop": "ts-node ./node_modules/typeorm/cli.js schema:drop -c sample",
"schema:sync": "ts-node ./node_modules/typeorm/cli.js schema:sync -c sample",
"schema:log": "ts-node ./node_modules/typeorm/cli.js schema:log -c sample",
"seed:run": "ts-node ./src/cli.ts seed -c sample",
"seed:config": "ts-node ./src/cli.ts config -c sample"
},
"license": "MIT",
"author": "w3tec.ch <[email protected]>",
Expand Down
2 changes: 1 addition & 1 deletion sample/factories/pet.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ define(Pet, (faker: typeof Faker) => {
const pet = new Pet()
pet.name = name
pet.age = faker.random.number()
// pet.user = factory(User)({ roles: ['admin'] })
pet.user = factory(User)({ roles: ['admin'] }) as any
return pet
})
19 changes: 10 additions & 9 deletions sample/seeds/create-pets.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { User } from '../entities/User.entity'

export default class CreatePets implements Seeder {
public async run(factory: Factory, connection: Connection): Promise<any> {
const em = connection.createEntityManager()
await factory(Pet)().seed()

await times(10, async (n) => {
// This creates a pet in the database
const pet = await factory(Pet)().seed()
// This only returns a entity with fake data
const user = await factory(User)().make()
user.pets = [pet]
await em.save(user)
})
// const em = connection.createEntityManager()
// await times(1, async (n) => {
// This creates a pet in the database
// const pet = await factory(Pet)().seed()
// This only returns a entity with fake data
// const user = await factory(User)().make()
// user.pets = [pet]
// await em.save(user)
// })
}
}
1 change: 0 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as yargs from 'yargs'
import { SeedCommand } from './commands/seed.command'
import { ConfigCommand } from './commands/config.command'

// tslint:disable-next-line
yargs
.usage('Usage: $0 <command> [options]')
.command(new ConfigCommand())
Expand Down
8 changes: 4 additions & 4 deletions src/commands/config.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export class ConfigCommand implements yargs.CommandModule {

builder(args: yargs.Argv) {
return args
.option('c', {
.option('n', {
alias: 'configName',
default: '',
describe: 'Name of the typeorm config file (json or js).',
})
.option('n', {
alias: 'name',
.option('c', {
alias: 'connection',
default: '',
describe: 'Name of the typeorm connection',
})
Expand All @@ -36,7 +36,7 @@ export class ConfigCommand implements yargs.CommandModule {
root: args.root as string,
configName: args.configName as string,
},
args.name as string,
args.connection as string,
)
log(option)
} catch (error) {
Expand Down
12 changes: 4 additions & 8 deletions src/commands/seed.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ export class SeedCommand implements yargs.CommandModule {

builder(args: yargs.Argv) {
return args
.option('c', {
.option('n', {
alias: 'configName',
default: '',
describe: 'Name of the typeorm config file (json or js).',
})
.option('n', {
alias: 'name',
.option('c', {
alias: 'connection',
default: '',
describe: 'Name of the typeorm connection',
})
Expand All @@ -36,10 +36,7 @@ export class SeedCommand implements yargs.CommandModule {

async handler(args: yargs.Arguments) {
// Disable logging for the seeders, but keep it alive for our cli
// tslint:disable-next-line
const log = console.log
// // tslint:disable-next-line
// console.log = () => void 0

const pkg = require('../../package.json')
log(chalk.bold(`typeorm-seeding v${(pkg as any).version}`))
Expand All @@ -53,7 +50,7 @@ export class SeedCommand implements yargs.CommandModule {
root: args.root as string,
configName: args.configName as string,
},
args.name as string,
args.connection as string,
)
spinner.succeed('ORM Config loaded')
} catch (error) {
Expand Down Expand Up @@ -111,7 +108,6 @@ export class SeedCommand implements yargs.CommandModule {

function panic(spinner: ora.Ora, error: Error, message: string) {
spinner.fail(message)
// tslint:disable-next-line
console.error(error)
process.exit(1)
}
22 changes: 16 additions & 6 deletions src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,39 @@ export declare type ConnectionOptions = TypeORMConnectionOptions & SeedingOption

const attachSeedingOptions = (option: ConnectionOptions): ConnectionOptions => {
if (!option.factories) {
option.factories = [process.env.TYPEORM_SEEDING_FACTORIES as string]
const envFactoriesPath = process.env.TYPEORM_SEEDING_FACTORIES
if (envFactoriesPath) {
option.factories = [envFactoriesPath]
} else {
option.factories = ['src/database/factories/**/*{.ts,.js}']
}
}
if (!option.seeds) {
option.seeds = [process.env.TYPEORM_SEEDING_SEEDS as string]
const envSeedsPath = process.env.TYPEORM_SEEDING_SEEDS
if (envSeedsPath) {
option.seeds = [envSeedsPath]
} else {
option.seeds = ['src/database/seeds/**/*{.ts,.js}']
}
}
return option
}

export const getConnectionOption = async (
option: ConnectionOptionArguments,
name: string,
connection: string,
): Promise<ConnectionOptions> => {
const reader = new ConnectionOptionsReader(option)
const options = (await reader.all()) as any[]
if (options.length === 1) {
return attachSeedingOptions(options[0])
}
if (name !== undefined && name !== '') {
const filteredOptions = options.filter((o) => o.name === name)
if (connection !== undefined && connection !== '') {
const filteredOptions = options.filter((o) => o.name === connection)
if (filteredOptions.length === 1) {
return attachSeedingOptions(options[0])
} else {
printError('Could not find any connection with the name=', name)
printError('Could not find any connection with the name=', connection)
}
}
printError('There are multiple connections please provide a connection name')
Expand Down
2 changes: 0 additions & 2 deletions src/entity-factory.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { EntityFactory } from './entity-factory'

describe('make', () => {
// tslint:disable-next-line
class User {
constructor(public name: string) {}
}
// tslint:disable-next-line
class Pet {
constructor(public name: string, public user: User) {}
}
Expand Down
49 changes: 29 additions & 20 deletions src/entity-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,7 @@ export class EntityFactory<Entity, Settings> {
* Make a new entity, but does not persist it
*/
public async make(overrideParams: EntityProperty<Entity> = {}): Promise<Entity> {
if (this.factory) {
let entity = await this.resolveEntity(this.factory(Faker, this.settings))
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')
return this.makeEnity(overrideParams, false)
}

/**
Expand All @@ -56,7 +42,7 @@ export class EntityFactory<Entity, Settings> {
if (connection) {
const em = connection.createEntityManager()
try {
const entity = await this.make(overrideParams)
const entity = await this.makeEnity(overrideParams, true)
return await em.save<Entity>(entity)
} catch (error) {
const message = 'Could not save entity'
Expand Down Expand Up @@ -90,18 +76,41 @@ export class EntityFactory<Entity, Settings> {
// Prrivat Helpers
// -------------------------------------------------------------------------

private async resolveEntity(entity: Entity): Promise<Entity> {
private async makeEnity(overrideParams: EntityProperty<Entity> = {}, isSeeding = false): Promise<Entity> {
if (this.factory) {
let entity = await this.resolveEntity(this.factory(Faker, this.settings), 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')
}

private async resolveEntity(entity: Entity, isSeeding = false): Promise<Entity> {
for (const attribute in entity) {
if (entity.hasOwnProperty(attribute)) {
if (isPromiseLike(entity[attribute])) {
entity[attribute] = entity[attribute]
}

if (entity[attribute] && typeof entity[attribute] === 'object' && !(entity[attribute] instanceof Date)) {
const subEntityFactory = entity[attribute]
try {
if (typeof (subEntityFactory as any).make === 'function') {
entity[attribute] = await (subEntityFactory as any).make()
if (isSeeding) {
if (typeof (subEntityFactory as any).seed === 'function') {
entity[attribute] = await (subEntityFactory as any).seed()
}
} else {
if (typeof (subEntityFactory as any).make === 'function') {
entity[attribute] = await (subEntityFactory as any).make()
}
}
} catch (error) {
const message = `Could not make ${(subEntityFactory as any).name}`
Expand Down
1 change: 0 additions & 1 deletion src/utils/factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ describe('isPromiseLike', () => {
expect(isPromiseLike([])).toBeFalsy()
expect(isPromiseLike({})).toBeFalsy()
expect(isPromiseLike((): any => void 0)).toBeFalsy()
// tslint:disable-next-line
class UserEntity {}
expect(isPromiseLike(new UserEntity())).toBeFalsy()
expect(isPromiseLike(new Date())).toBeFalsy()
Expand Down
2 changes: 0 additions & 2 deletions src/utils/log.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import * as chalk from 'chalk'
* Prints the error to the console
*/
export const printError = (message: string, error?: any) => {
// tslint:disable-next-line
console.log('\n❌ ', chalk.red(message))
if (error) {
// tslint:disable-next-line
console.error(error)
}
}

0 comments on commit 9290214

Please sign in to comment.