Skip to content

Commit

Permalink
♻️ Refacor import strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
hirsch committed Jun 4, 2019
1 parent f1bbdbf commit 84df811
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 168 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@types/jest": "^24.0.13",
"@types/yargs": "^13.0.0",
"jest": "^24.8.0",
"prettier": "^1.17.1",
"rimraf": "^2.6.2",
"rollup": "^0.66.2",
"rollup-plugin-cli": "^0.1.5",
Expand All @@ -48,6 +49,7 @@
"typescript": "^3.0.3"
},
"dependencies": {
"@types/glob": "^7.1.1",
"@types/node": "^12.0.4",
"chalk": "^2.4.2",
"faker": "^4.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import 'reflect-metadata'
import * as yargs from 'yargs'
import { SeedCommand } from './commands/seed.command'
import { ConfigCommand } from './commands/config.command';
import { ConfigCommand } from './commands/config.command'

yargs
.usage('Usage: $0 <command> [options]')
Expand Down
11 changes: 5 additions & 6 deletions src/commands/config.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ export class ConfigCommand implements yargs.CommandModule {
describe = 'Show the TypeORM config'

builder(args: yargs.Argv) {
return args
.option('c', {
alias: 'config',
default: 'ormconfig.js',
describe: 'Path to the typeorm config file (json or js).',
})
return args.option('c', {
alias: 'config',
default: 'ormconfig.js',
describe: 'Path to the typeorm config file (json or js).',
})
}

async handler(args: yargs.Arguments) {
Expand Down
38 changes: 19 additions & 19 deletions src/commands/seed.command.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
import * as yargs from 'yargs'
import chalk from 'chalk'
import { createConnection } from 'typeorm'
import { setConnection, loadEntityFactories, loadSeeds, runSeed, getConnectionOptions } from '../typeorm-seeding'
import { setConnection, runSeed, getConnectionOptions } from '../typeorm-seeding'
import * as pkg from '../../package.json'
import { printError } from '../utils/log.util'
import { importSeed } from '../importer'
import { loadFiles, importFiles } from '../utils/file.util'

export class SeedCommand implements yargs.CommandModule {
command = 'seed'
describe = 'Runs the seeds'

builder(args: yargs.Argv) {
return args
.option('c', {
alias: 'config',
.option('config', {
default: 'ormconfig.js',
describe: 'Path to the typeorm config file (json or js).',
})
.option('s', {
alias: 'seeds',
default: 'database/seeds',
describe: 'Directory where seeds are.',
})
.option('f', {
alias: 'factories',
default: 'database/factories',
describe: 'Directory where enity factories are.',
.option('class', {
describe: 'Specific seed class to run.',
})
}

async handler(args: yargs.Arguments) {
const log = console.log
log(chalk.bold(`typeorm-seeding v${(pkg as any).version}`))

// Get TypeORM config file
let options
try {
options = await getConnectionOptions(args.config as string)
} catch (error) {
printError('Could not load the config file!', error)
process.exit(1)
}

// Find all factories and seed with help of the config
let factoryFiles
let seedFiles
const factoryFiles = loadFiles(options.factories || ['src/database/factories/**/*.ts'])
const seedFiles = loadFiles(options.seeds || ['src/database/seeds/**/*.ts'])
try {
factoryFiles = await loadEntityFactories(args.factories as string)
seedFiles = await loadSeeds(args.seeds as string)
importFiles(factoryFiles)
} catch (error) {
printError('Could not load factories and seeds!', error)
printError('Could not load factories!', error)
process.exit(1)
}

Expand All @@ -57,7 +58,6 @@ export class SeedCommand implements yargs.CommandModule {

// Get database connection and pass it to the seeder
try {
const options = await getConnectionOptions(args.config as string)
const connection = await createConnection(options)
setConnection(connection)
} catch (error) {
Expand All @@ -68,7 +68,7 @@ export class SeedCommand implements yargs.CommandModule {
// Show seeds in the console
for (const seedFile of seedFiles) {
try {
const seedFileObject: any = importSeed(seedFile)
const seedFileObject = importSeed(seedFile)
log(chalk.gray.underline(`executing seed:`), chalk.green.bold(`${seedFileObject.name}`))
await runSeed(seedFileObject)
} catch (error) {
Expand Down
8 changes: 2 additions & 6 deletions src/connection.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import * as path from 'path'
import {
Connection,
createConnection as createTypeORMConnection,
ConnectionOptions,
} from 'typeorm'
import { Connection, createConnection as createTypeORMConnection, ConnectionOptions } from 'typeorm'

export const getConnectionOptions = async (configPath: string): Promise<ConnectionOptions> => {
return require(path.join(process.cwd(), configPath))
return require(path.join(process.cwd(), configPath))
}

export const createConnection = async (configPath: string): Promise<Connection> => {
Expand Down
29 changes: 7 additions & 22 deletions src/entity-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as Faker from 'faker'
import { Connection, ObjectType } from 'typeorm'
import { FactoryFunction, EntityProperty } from './types'
import { isPromiseLike } from './utils/factory.util'
import { printError } from './utils/log.util';
import { printError } from './utils/log.util'

export class EntityFactory<Entity, Settings> {
private mapFunction: (entity: Entity) => Promise<Entity>
Expand All @@ -22,19 +22,15 @@ export class EntityFactory<Entity, Settings> {
* This function is used to alter the generated values of entity, before it
* is persist into the database
*/
public map(
mapFunction: (entity: Entity) => Promise<Entity>,
): EntityFactory<Entity, Settings> {
public map(mapFunction: (entity: Entity) => Promise<Entity>): EntityFactory<Entity, Settings> {
this.mapFunction = mapFunction
return this
}

/**
* Make a new entity, but does not persist it
*/
public async make(
overrideParams: EntityProperty<Entity> = {},
): Promise<Entity> {
public async make(overrideParams: EntityProperty<Entity> = {}): Promise<Entity> {
if (this.factory) {
let entity = await this.resolveEntity(this.factory(Faker, this.settings))
if (this.mapFunction) {
Expand All @@ -53,9 +49,7 @@ export class EntityFactory<Entity, Settings> {
/**
* Seed makes a new entity and does persist it
*/
public async seed(
overrideParams: EntityProperty<Entity> = {},
): Promise<Entity> {
public async seed(overrideParams: EntityProperty<Entity> = {}): Promise<Entity> {
const connection: Connection = (global as any).seeder.connection
if (connection) {
const em = connection.createEntityManager()
Expand All @@ -74,21 +68,15 @@ export class EntityFactory<Entity, Settings> {
}
}

public async makeMany(
amount: number,
overrideParams: EntityProperty<Entity> = {},
): Promise<Entity[]> {
public async makeMany(amount: number, overrideParams: EntityProperty<Entity> = {}): Promise<Entity[]> {
const list = []
for (let index = 0; index < amount; index++) {
list[index] = await this.make(overrideParams)
}
return list
}

public async seedMany(
amount: number,
overrideParams: EntityProperty<Entity> = {},
): Promise<Entity[]> {
public async seedMany(amount: number, overrideParams: EntityProperty<Entity> = {}): Promise<Entity[]> {
const list = []
for (let index = 0; index < amount; index++) {
list[index] = await this.seed(overrideParams)
Expand All @@ -107,10 +95,7 @@ export class EntityFactory<Entity, Settings> {
entity[attribute] = await entity[attribute]
}

if (
typeof entity[attribute] === 'object' &&
!(entity[attribute] instanceof Date)
) {
if (typeof entity[attribute] === 'object' && !(entity[attribute] instanceof Date)) {
const subEntityFactory = entity[attribute]
try {
if (typeof (subEntityFactory as any).make === 'function') {
Expand Down
5 changes: 1 addition & 4 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/**
* Times repeats a function n times
*/
export const times = async <TResult>(
n: number,
iteratee: (index: number) => Promise<TResult>,
): Promise<TResult[]> => {
export const times = async <TResult>(n: number, iteratee: (index: number) => Promise<TResult>): Promise<TResult[]> => {
const rs = [] as TResult[]
for (let i = 0; i < n; i++) {
const r = await iteratee(i)
Expand Down
32 changes: 3 additions & 29 deletions src/importer.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,10 @@
import { loadFiles, importFiles } from './utils/file.util'
import { SeederConstructor } from './types'

// -------------------------------------------------------------------------
// Util functions
// -------------------------------------------------------------------------

const loadFactoryFiles = loadFiles('**/*actory{.js,.ts}')
const loadSeedFiles = loadFiles('**/*{.js,.ts}')

// -------------------------------------------------------------------------
// Facade functions
// -------------------------------------------------------------------------

export const loadEntityFactories = (pathToFolder: string): Promise<string[]> => {
return new Promise((resolve, reject) => {
loadFactoryFiles(pathToFolder)(files => {
importFiles(files)
resolve(files)
})(reject)
})
}

export const loadSeeds = (pathToFolder: string): Promise<string[]> => {
return new Promise((resolve, reject) => {
loadSeedFiles(pathToFolder + '/')(resolve)(reject)
})
}

export const importSeed = (filePath: string): any => {
export const importSeed = (filePath: string): SeederConstructor => {
let className = filePath.split('/')[filePath.split('/').length - 1]
className = className.replace('.ts', '').replace('.js', '')
className = className.split('-')[className.split('-').length - 1]

// TODO: without the default
const seedFileObject: any = require(filePath)
return seedFileObject.default
}
36 changes: 9 additions & 27 deletions src/typeorm-seeding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import 'reflect-metadata'
import { Connection, ObjectType } from 'typeorm'

import { EntityFactory } from './entity-factory'
import {
EntityFactoryDefinition,
Factory,
FactoryFunction,
SeedConstructor,
} from './types'
import { EntityFactoryDefinition, Factory, FactoryFunction, SeederConstructor, Seeder } from './types'
import { getNameOfEntity } from './utils/factory.util'

// -------------------------------------------------------------------------
Expand All @@ -16,7 +11,7 @@ import { getNameOfEntity } from './utils/factory.util'

export * from './importer'
export * from './connection'
export { Factory, Seed } from './types'
export { Factory, Seeder } from './types'
export { times } from './helpers'

// -------------------------------------------------------------------------
Expand All @@ -34,8 +29,7 @@ export { times } from './helpers'
/**
* Adds the typorm connection to the seed options
*/
export const setConnection = (connection: Connection) =>
((global as any).seeder.connection = connection)
export const setConnection = (connection: Connection) => ((global as any).seeder.connection = connection)

/**
* Returns the typorm connection from our seed options
Expand All @@ -45,10 +39,7 @@ export const getConnection = () => (global as any).seeder.connection
/**
* Defines a new entity factory
*/
export const define = <Entity, Settings>(
entity: ObjectType<Entity>,
factoryFn: FactoryFunction<Entity, Settings>,
) => {
export const define = <Entity, Settings>(entity: ObjectType<Entity>, factoryFn: FactoryFunction<Entity, Settings>) => {
;(global as any).seeder.entityFactories.set(getNameOfEntity(entity), {
entity,
factory: factoryFn,
Expand All @@ -58,25 +49,16 @@ export const define = <Entity, Settings>(
/**
* Gets a defined entity factory and pass the settigns along to the entity factory function
*/
export const factory: Factory = <Entity, Settings>(
entity: ObjectType<Entity>,
) => (settings?: Settings) => {
export const factory: Factory = <Entity, Settings>(entity: ObjectType<Entity>) => (settings?: Settings) => {
const name = getNameOfEntity(entity)
const entityFactoryObject = (global as any).seeder.entityFactories.get(name)
return new EntityFactory<Entity, Settings>(
name,
entity,
entityFactoryObject.factory,
settings,
)
return new EntityFactory<Entity, Settings>(name, entity, entityFactoryObject.factory, settings)
}

/**
* Runs a seed class
*/
export const runSeed = async <T>(
seederConstructor: SeedConstructor,
): Promise<T> => {
const seeder = new seederConstructor()
return seeder.seed(factory, getConnection())
export const runSeed = async (clazz: SeederConstructor): Promise<void> => {
const seeder: Seeder = new clazz()
return seeder.run(factory, getConnection())
}
15 changes: 6 additions & 9 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ import { EntityFactory } from './entity-factory'
/**
* FactoryFunction is the fucntion, which generate a new filled entity
*/
export type FactoryFunction<Entity, Settings> = (
faker: typeof Faker,
settings?: Settings,
) => Entity
export type FactoryFunction<Entity, Settings> = (faker: typeof Faker, settings?: Settings) => Entity

/**
* EntityProperty defines an object whose keys and values must be properties of the given Entity.
*/
export type EntityProperty<Entity> = { [Property in keyof Entity]?: Entity[Property] };
export type EntityProperty<Entity> = { [Property in keyof Entity]?: Entity[Property] }

/**
* Factory gets the EntityFactory to the given Entity and pass the settings along
Expand All @@ -26,15 +23,15 @@ export type Factory = <Entity, Settings>(
/**
* Seed are the class to create some data. Those seed are run by the cli.
*/
export interface Seed {
seed(factory: Factory, connection: Connection): Promise<any>
export interface Seeder {
run(factory: Factory, connection: Connection): Promise<void>
}

/**
* Constructor of the seed class
*/
export interface SeedConstructor {
new (): Seed
export interface SeederConstructor {
new (): Seeder
}

/**
Expand Down
Loading

0 comments on commit 84df811

Please sign in to comment.