/// declare module '@ioc:Adonis/Lucid/Factory' { import faker from 'faker'; import { OneOrMany, QueryClientContract, TransactionClientContract } from '@ioc:Adonis/Lucid/Database'; import { LucidRow, LucidModel, ModelAttributes, ModelAdapterOptions, RelationshipsContract, ExtractModelRelations } from '@ioc:Adonis/Lucid/Orm'; /** * ------------------------------------------------------ * Helpers * ------------------------------------------------------ */ /** * Extracts the attributes accepted by the lucid model set on a * factory */ export type ExtractFactoryAttributes> = Partial>>; /** * ------------------------------------------------------ * Callbacks * ------------------------------------------------------ */ /** * Function to return the model attributes. */ export type DefineCallback = (ctx: FactoryContextContract) => Promise>>> | Partial>>; /** * 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 */ export type NewUpCallback> = (attributes: ExtractFactoryAttributes, ctx: FactoryContextContract) => InstanceType; /** * Function to merge attributes defined during runtime */ export type MergeCallback> = (model: InstanceType, attributes: ExtractFactoryAttributes, ctx: FactoryContextContract) => void; /** * Callback to define a new model state */ export type StateCallback = (model: Model, ctx: FactoryContextContract) => any | Promise; /** * ------------------------------------------------------ * Hooks * ------------------------------------------------------ */ /** * List of events for which a factory will trigger hooks */ export type EventsList = 'makeStubbed' | 'create' | 'make'; /** * Shape of hooks handler */ export type HooksHandler> = (factory: FactoryBuilderContract, model: InstanceType, ctx: FactoryContextContract) => void | Promise; /** * ------------------------------------------------------ * Runtime context * ------------------------------------------------------ */ /** * The runtime context of the factory builder. A new state is constructed * for each `create/make` operation and passed down to relationships * as well. */ export interface FactoryContextContract { faker: typeof faker; isStubbed: boolean; $trx: TransactionClientContract | undefined; } /** * ------------------------------------------------------ * Relationships * ------------------------------------------------------ */ /** * Callback accepted by the `with` method and relationships * `create` and `make` methods */ export type RelationCallback = (factory: FactoryBuilderContract>) => void; /** * Shape of the factory relationships. To keep relationships slim, we will have * a common interface for relationships vs fine tuning API for each type of * relationship */ export interface FactoryRelationContract { /** * Reference to the Lucid model relationship */ relation: RelationshipsContract; /** * Pass context to the relationship. Must be done everytime, so that * relationships uses the same transaction as the parent model */ useCtx(ctx: FactoryContextContract): this; /** * Create and persist */ create(parent: LucidRow, callback?: RelationCallback, count?: number): Promise; /** * Create and stub */ make(parent: LucidRow, callback?: RelationCallback, count?: number): Promise; } /** * ------------------------------------------------------ * Runtime builder * ------------------------------------------------------ */ /** * Factory builder uses the factory model to create/make * instances of lucid models */ export interface FactoryBuilderContract> { /** * Define custom database connection */ connection(connection: string): this; /** * Define custom query client */ client(client: QueryClientContract): this; /** * Apply pre-defined state */ apply(...states: K[]): this; /** * Create/make relationships for explicitly defined related factories */ with(relation: K, count?: number, callback?: ( /** * Receives the explicitly defined factory */ factory: FactoryModel['relations'][K] extends () => FactoryBuilderContract ? ReturnType : never) => void): this; /** * Merge custom set of attributes. They are passed to the merge method of * the model factory * * For `createMany` and `makeMany`, you can pass an array of attributes mapped * according to the array index. */ merge(attributes: OneOrMany>): this; /** * Define custom runtime context. This method is usually called by * the relationships to ensure a single context is used by the * parent and relationship factories. * * Do not define a custom context, unless you know what you are really * doing. */ useCtx(ctx: FactoryContextContract): this; /** * Make model instance without persitance. The make method * doesn't process relationships */ make(callback?: (model: InstanceType, ctx: FactoryContextContract) => void): Promise>; /** * Create model instance and stub out the persistance * mechanism */ makeStubbed(callback?: (model: InstanceType, ctx: FactoryContextContract) => void): Promise>; /** * Create and persist model instance */ create(callback?: (model: InstanceType, ctx: FactoryContextContract) => void): Promise>; /** * Make model instance without persitance. The makeMany method * doesn't process relationships */ makeMany(count: number, callback?: (model: InstanceType, ctx: FactoryContextContract) => void): Promise[]>; /** * Create one or more model instances and stub * out the persistance mechanism. */ makeStubbedMany(count: number, callback?: (model: InstanceType, ctx: FactoryContextContract) => void): Promise[]>; /** * Create and persist more than one model instance */ createMany(count: number, callback?: (model: InstanceType, ctx: FactoryContextContract) => void): Promise[]>; } /** * 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> extends FactoryBuilderContract { query(options?: ModelAdapterOptions): FactoryBuilderContract; } /** * ------------------------------------------------------ * Factory model * ------------------------------------------------------ */ /** * Factory model exposes the API to defined a model factory with states * and relationships */ export interface FactoryModelContract { /** * Reference to the underlying lucid model used by the factory * model */ model: Model; /** * Mainly for types support. Not used at runtime to derive any * logic. Sorry, at times have to hack into typescript to * get the desired output. :) */ states: unknown; relations: unknown; /** * Optionally define a custom method to instantiate the model * instance */ newUp(callback: NewUpCallback): this; merge(callback: MergeCallback): this; /** * Define custom state for the factory. When executing the factory, * you can apply the pre-defined states */ state(state: K, callback: StateCallback>): this & { states: { [P in K]: StateCallback>; }; }; /** * Define a relationship on another factory */ relation>, Relation>(relation: K, callback: Relation): this & { relations: { [P in K]: Relation; }; }; /** * Define before hooks. Only `create` event is invoked * during the before lifecycle */ before(event: Exclude, handler: HooksHandler): this; /** * Define after hooks. */ after(event: EventsList, handler: HooksHandler): this; /** * Build model factory. This method returns the factory builder, which can be used to * execute model queries */ build(): FactoryBuilderQueryContract; } /** * ------------------------------------------------------ * Manager to register new factories * ------------------------------------------------------ */ /** * Factory manager to define new factories */ export interface FactoryManagerContract { /** * Define a custom factory */ define(model: Model, callback: DefineCallback): FactoryModelContract; /** * Define a custom callback to generate stub ids */ stubId(callback: StubIdCallback): void; } const Factory: FactoryManagerContract; export default Factory; }