Skip to content

Commit

Permalink
feat: allow defining custom handler for generating stub ids
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Jun 20, 2020
1 parent fe0febe commit e755e2e
Show file tree
Hide file tree
Showing 17 changed files with 291 additions and 116 deletions.
51 changes: 47 additions & 4 deletions adonis-typings/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ declare module '@ioc:Adonis/Lucid/Factory' {
ctx: FactoryContextContract,
) => Promise<Partial<ModelAttributes<InstanceType<Model>>>> | Partial<ModelAttributes<InstanceType<Model>>>

/**
* Function to generate custom stub ids
*/
export type StubIdCallback = (counter: number, model: LucidRow) => any

/**
* Function to initiate a model instance. It will receive the
* attributes returned by the `define` method
Expand Down Expand Up @@ -197,6 +202,17 @@ declare module '@ioc:Adonis/Lucid/Factory' {
*/
useCtx (ctx: FactoryContextContract): this

/**
* Make model instance without persitance. The make method
* doesn't process relationships
*/
make (
callback?: (
model: InstanceType<FactoryModel['model']>,
ctx: FactoryContextContract,
) => void
): Promise<InstanceType<FactoryModel['model']>>

/**
* Create model instance and stub out the persistance
* mechanism
Expand All @@ -218,6 +234,18 @@ declare module '@ioc:Adonis/Lucid/Factory' {
) => void
): Promise<InstanceType<FactoryModel['model']>>

/**
* Make model instance without persitance. The makeMany method
* doesn't process relationships
*/
makeMany (
count: number,
callback?: (
model: InstanceType<FactoryModel['model']>,
ctx: FactoryContextContract,
) => void
): Promise<InstanceType<FactoryModel['model']>[]>

/**
* Create one or more model instances and stub
* out the persistance mechanism.
Expand All @@ -242,6 +270,16 @@ declare module '@ioc:Adonis/Lucid/Factory' {
): Promise<InstanceType<FactoryModel['model']>[]>
}

/**
* Query contract that initiates the factory builder. Since the factory builder
* API surface is small, we also proxy all of it's methods for a nicer DX
*/
export interface FactoryBuilderQueryContract<
FactoryModel extends FactoryModelContract<LucidModel>
> extends FactoryBuilderContract<FactoryModel> {
query (): FactoryBuilderContract<FactoryModel>
}

/**
* ------------------------------------------------------
* Factory model
Expand Down Expand Up @@ -295,7 +333,7 @@ declare module '@ioc:Adonis/Lucid/Factory' {
* Define before hooks. Only `create` event is invoked
* during the before lifecycle
*/
before (event: 'create', handler: HooksHandler<this>): this
before (event: Exclude<EventsList, 'make'>, handler: HooksHandler<this>): this

/**
* Define after hooks.
Expand All @@ -306,7 +344,7 @@ declare module '@ioc:Adonis/Lucid/Factory' {
* Build model factory. This method returns the factory builder, which can be used to
* execute model queries
*/
build (): FactoryBuilderContract<this>
build (): FactoryBuilderQueryContract<this>
}

/**
Expand All @@ -318,16 +356,21 @@ declare module '@ioc:Adonis/Lucid/Factory' {
/**
* Factory manager to define new factories
*/
export interface FactoryManager {
export interface FactoryManagerContract {
/**
* Define a custom factory
*/
define<Model extends LucidModel> (
model: Model,
callback: DefineCallback<Model>
): FactoryModelContract<Model>

/**
* Define a custom callback to generate stub ids
*/
stubId (callback: StubIdCallback): void
}

const Factory: FactoryManager
const Factory: FactoryManagerContract
export default Factory
}
36 changes: 33 additions & 3 deletions src/Factory/FactoryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
import { FactoryModel } from './FactoryModel'
import { FactoryContext } from './FactoryContext'

let Counter = 1

/**
* Factory builder exposes the API to create/persist factory model instances.
*/
Expand Down Expand Up @@ -195,6 +193,18 @@ export class FactoryBuilder implements FactoryBuilderContract<FactoryModelContra
return this
}

/**
* Make model instance. Relationships are not processed with the make function.
*/
public async make (callback?: (
model: LucidRow,
ctx: FactoryContextContract,
) => void) {
const { modelInstance, ctx } = await this.compile(true, callback)
await this.model.hooks.exec('after', 'make', this, modelInstance, ctx)
return modelInstance
}

/**
* Returns a model instance without persisting it to the database.
* Relationships are still loaded and states are also applied.
Expand All @@ -205,8 +215,10 @@ export class FactoryBuilder implements FactoryBuilderContract<FactoryModelContra
) => void) {
const { modelInstance, ctx } = await this.compile(true, callback)
await this.model.hooks.exec('after', 'make', this, modelInstance, ctx)
await this.model.hooks.exec('before', 'makeStubbed', this, modelInstance, ctx)

modelInstance[this.model.model.primaryKey] = modelInstance.$primaryKeyValue || Counter++
const id = modelInstance.$primaryKeyValue || this.model.manager.getNextId(modelInstance)
modelInstance[this.model.model.primaryKey] = id

/**
* Make relationships. The relationships will be not persisted
Expand Down Expand Up @@ -303,4 +315,22 @@ export class FactoryBuilder implements FactoryBuilderContract<FactoryModelContra

return modelInstances
}

/**
* Create many of the factory model instances
*/
public async makeMany (
count: number,
callback?: (model: LucidRow, state: FactoryContextContract) => void,
) {
let modelInstances: LucidRow[] = []

const counter = new Array(count).fill(0).map((_, i) => i)
for (let index of counter) {
this.currentIndex = index
modelInstances.push(await this.make(callback))
}

return modelInstances
}
}
3 changes: 2 additions & 1 deletion src/Factory/FactoryContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
* file that was distributed with this source code.
*/

import faker from 'faker'
import { FactoryContextContract } from '@ioc:Adonis/Lucid/Factory'
import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database'

export class FactoryContext implements FactoryContextContract {
public faker = {}
public faker = faker

constructor (
public isStubbed: boolean,
Expand Down
54 changes: 50 additions & 4 deletions src/Factory/FactoryModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import {
DefineCallback,
FactoryModelContract,
FactoryRelationContract,
FactoryBuilderQueryContract,
} from '@ioc:Adonis/Lucid/Factory'

import { FactoryManager } from './index'
import { HasOne } from './Relations/HasOne'
import { HasMany } from './Relations/HasMany'
import { FactoryBuilder } from './FactoryBuilder'
import { BelongsTo } from './Relations/BelongsTo'
import { ManyToMany } from './Relations/ManyToMany'

import { FactoryBuilder } from './FactoryBuilder'

/**
* Factory model exposes the API to define a model factory with custom
* states and relationships
Expand Down Expand Up @@ -72,7 +73,11 @@ export class FactoryModel<Model extends LucidModel> implements FactoryModelContr
*/
public hooks = new Hooks()

constructor (public model: Model, public define: DefineCallback<LucidModel>) {
constructor (
public model: Model,
public define: DefineCallback<LucidModel>,
public manager: FactoryManager,
) {
}

/**
Expand Down Expand Up @@ -195,6 +200,47 @@ export class FactoryModel<Model extends LucidModel> implements FactoryModelContr
* used to make/create model instances
*/
public build () {
return new FactoryBuilder(this)
/**
* Return a build object, which proxies all of the factory builder
* method and invokes them with a fresh instance.
*/
const builder = {
model: this,
query () {
return new FactoryBuilder(this.model)
},
apply (...args: any[]) {
return this.query().apply(...args)
},
with (relation, ...args: any[]) {
return this.query().with(relation, ...args)
},
merge (attributes) {
return this.query().merge(attributes)
},
useCtx (ctx) {
return this.query().useCtx(ctx)
},
make (callback) {
return this.query().make(callback)
},
makeStubbed (callback) {
return this.query().makeStubbed(callback)
},
create (callback) {
return this.query().create(callback)
},
makeMany (count, callback) {
return this.query().makeMany(count, callback)
},
makeStubbedMany (count, callback) {
return this.query().makeStubbedMany(count, callback)
},
createMany (count, callback) {
return this.query().createMany(count, callback)
},
}

return builder as unknown as FactoryBuilderQueryContract<FactoryModelContract<Model>>
}
}
6 changes: 3 additions & 3 deletions src/Factory/Relations/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
RelationCallback,
FactoryModelContract,
FactoryContextContract,
FactoryBuilderContract,
FactoryBuilderQueryContract,
} from '@ioc:Adonis/Lucid/Factory'

/**
Expand All @@ -22,15 +22,15 @@ export abstract class BaseRelation {
protected ctx: FactoryContextContract

constructor (
private factory: () => FactoryBuilderContract<FactoryModelContract<LucidModel>>
private factory: () => FactoryBuilderQueryContract<FactoryModelContract<LucidModel>>
) {
}

/**
* Instantiates the relationship factory
*/
protected compile (callback?: RelationCallback) {
const factory = this.factory()
const factory = this.factory().query()
if (typeof (callback) === 'function') {
callback(factory)
}
Expand Down
4 changes: 2 additions & 2 deletions src/Factory/Relations/BelongsTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { BelongsToRelationContract } from '@ioc:Adonis/Lucid/Relations'
import {
RelationCallback,
FactoryModelContract,
FactoryBuilderContract,
FactoryRelationContract,
FactoryBuilderQueryContract,
} from '@ioc:Adonis/Lucid/Factory'

import { BaseRelation } from './Base'
Expand All @@ -24,7 +24,7 @@ import { BaseRelation } from './Base'
export class BelongsTo extends BaseRelation implements FactoryRelationContract {
constructor (
public relation: BelongsToRelationContract<LucidModel, LucidModel>,
factory: () => FactoryBuilderContract<FactoryModelContract<LucidModel>>
factory: () => FactoryBuilderQueryContract<FactoryModelContract<LucidModel>>
) {
super(factory)
this.relation.boot()
Expand Down
4 changes: 2 additions & 2 deletions src/Factory/Relations/HasMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { HasManyRelationContract } from '@ioc:Adonis/Lucid/Relations'
import {
RelationCallback,
FactoryModelContract,
FactoryBuilderContract,
FactoryRelationContract,
FactoryBuilderQueryContract,
} from '@ioc:Adonis/Lucid/Factory'

import { BaseRelation } from './Base'
Expand All @@ -24,7 +24,7 @@ import { BaseRelation } from './Base'
export class HasMany extends BaseRelation implements FactoryRelationContract {
constructor (
public relation: HasManyRelationContract<LucidModel, LucidModel>,
factory: () => FactoryBuilderContract<FactoryModelContract<LucidModel>>
factory: () => FactoryBuilderQueryContract<FactoryModelContract<LucidModel>>
) {
super(factory)
this.relation.boot()
Expand Down
4 changes: 2 additions & 2 deletions src/Factory/Relations/HasOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { HasOneRelationContract } from '@ioc:Adonis/Lucid/Relations'
import {
RelationCallback,
FactoryModelContract,
FactoryBuilderContract,
FactoryRelationContract,
FactoryBuilderQueryContract,
} from '@ioc:Adonis/Lucid/Factory'

import { BaseRelation } from './Base'
Expand All @@ -24,7 +24,7 @@ import { BaseRelation } from './Base'
export class HasOne extends BaseRelation implements FactoryRelationContract {
constructor (
public relation: HasOneRelationContract<LucidModel, LucidModel>,
factory: () => FactoryBuilderContract<FactoryModelContract<LucidModel>>
factory: () => FactoryBuilderQueryContract<FactoryModelContract<LucidModel>>
) {
super(factory)
this.relation.boot()
Expand Down
4 changes: 2 additions & 2 deletions src/Factory/Relations/ManyToMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { LucidModel, LucidRow, ModelObject } from '@ioc:Adonis/Lucid/Model'
import {
RelationCallback,
FactoryModelContract,
FactoryBuilderContract,
FactoryRelationContract,
FactoryBuilderQueryContract,
} from '@ioc:Adonis/Lucid/Factory'

import { BaseRelation } from './Base'
Expand All @@ -24,7 +24,7 @@ import { BaseRelation } from './Base'
export class ManyToMany extends BaseRelation implements FactoryRelationContract {
constructor (
public relation: ManyToManyRelationContract<LucidModel, LucidModel>,
factory: () => FactoryBuilderContract<FactoryModelContract<LucidModel>>
factory: () => FactoryBuilderQueryContract<FactoryModelContract<LucidModel>>
) {
super(factory)
this.relation.boot()
Expand Down
Loading

0 comments on commit e755e2e

Please sign in to comment.