Skip to content

Commit

Permalink
feat: try to assume FK and PK
Browse files Browse the repository at this point in the history
If they are not provided. We assume that PK is "id" and FK is "{tableName}_id"
  • Loading branch information
Julien-R44 committed Sep 20, 2022
1 parent 2e0383f commit ff174c7
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 28 deletions.
16 changes: 8 additions & 8 deletions packages/core/src/builder/relationship_builder.ts
@@ -1,6 +1,6 @@
/* eslint-disable sonarjs/no-duplicate-string */
import type { FactoryModel } from '../model.js'
import type { WithCallback } from '../contracts.js'
import { RelationType } from '../contracts'
import type { WithCallback } from '../contracts'
import type { FactoryModel } from '../model'

export class RelationshipBuilder {
constructor(private factory: FactoryModel<any>) {}
Expand All @@ -27,11 +27,11 @@ export class RelationshipBuilder {
relations: any[]
) {
for (const model of models) {
if (type === 'has-one') {
if (type === RelationType.HasOne) {
model[relationship.name] = relations.shift()
} else if (type === 'has-many') {
} else if (type === RelationType.HasMany) {
model[relationship.name] = relations.splice(0, relationship.count || 1)
} else if (type === 'belongs-to') {
} else if (type === RelationType.BelongsTo) {
model[relationship.name] = relations.shift()
}
}
Expand All @@ -44,10 +44,10 @@ export class RelationshipBuilder {
return this.appliedRelationships.filter((relationship) => {
const meta = this.factory.relations[relationship.name]!
if (type === 'pre') {
return meta.type === 'belongs-to'
return meta.type === RelationType.BelongsTo
}

return meta.type !== 'belongs-to'
return meta.type !== RelationType.BelongsTo
})
}

Expand Down
40 changes: 39 additions & 1 deletion packages/core/src/contracts.ts
@@ -1,8 +1,10 @@
import type { Builder } from './builder/builder.js'
import type { Builder } from './builder/builder'
import type { FactoryModel } from './model'
import type { faker } from '@faker-js/faker'
import type { Knex } from 'knex'

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>

/**
* Callback that must be passed to the `defineFactory` function.
*/
Expand Down Expand Up @@ -41,3 +43,39 @@ export type FactoryExtractGeneric<
* Callback that must be passed to the `with` function.
*/
export type WithCallback = (builder: Builder<any>) => void

/**
* Possible relations type
*/
export enum RelationType {
HasOne = 'has-one',
HasMany = 'has-many',
BelongsTo = 'belongs-to',
}

/**
* Metadata for a relationship.
*/
export interface RelationshipMeta {
type: RelationType

/**
* If no localKey is defined, we gonna assume that it's "id"
*/
localKey: string

/**
* If no foreignKey is defined, we gonna assume that it's "{tableName}_id"
*/
foreignKey: string

/**
* Reference to the relation factory
*/
factory: Builder<any, any, any>
}

export type RelationshipMetaOptions = Optional<
Omit<RelationshipMeta, 'type'>,
'foreignKey' | 'localKey'
>
46 changes: 27 additions & 19 deletions packages/core/src/model.ts
@@ -1,14 +1,12 @@
import { faker } from '@faker-js/faker'
import { Builder } from './builder/builder'
import type { DefineFactoryCallback, DefineStateCallback } from './contracts'

interface HasOneMeta {
type: 'has-one' | 'has-many' | 'belongs-to'
localKey: string
foreignKey: string
factory: Builder<any, any, any>
}

type RelationshipMeta = HasOneMeta
import { RelationType } from './contracts'
import type {
DefineFactoryCallback,
DefineStateCallback,
RelationshipMeta,
RelationshipMetaOptions,
} from './contracts'

export class FactoryModel<Model extends Record<string, any>, States extends string | null = null> {
/**
Expand All @@ -30,6 +28,19 @@ export class FactoryModel<Model extends Record<string, any>, States extends stri
this.callback = callback
}

private addRelation(name: string, type: RelationType, meta: RelationshipMetaOptions) {
const { tableName } = this.callback({ faker })

this.relations[name] = {
foreignKey: `${tableName}_id`,
localKey: 'id',
...meta,
type,
}

return this
}

/**
* Allows you to define a new state for the factory.
*/
Expand All @@ -44,25 +55,22 @@ export class FactoryModel<Model extends Record<string, any>, States extends stri
/**
* Add hasOne relationship
*/
public hasOne(name: string, meta: Omit<HasOneMeta, 'type'>) {
this.relations[name] = { ...meta, type: 'has-one' }
return this
public hasOne(name: string, meta: RelationshipMetaOptions) {
return this.addRelation(name, RelationType.HasOne, meta)
}

/**
* Add hasMany relationship
*/
public hasMany(name: string, meta: Omit<HasOneMeta, 'type'>) {
this.relations[name] = { ...meta, type: 'has-many' }
return this
public hasMany(name: string, meta: RelationshipMetaOptions) {
return this.addRelation(name, RelationType.HasMany, meta)
}

/**
* Add belongsTo relationship
*/
public belongsTo(name: string, meta: Omit<HasOneMeta, 'type'>) {
this.relations[name] = { ...meta, type: 'belongs-to' }
return this
public belongsTo(name: string, meta: RelationshipMetaOptions) {
return this.addRelation(name, RelationType.BelongsTo, meta)
}

/**
Expand Down
26 changes: 26 additions & 0 deletions tests/has_many.spec.ts
@@ -1,5 +1,6 @@
import { test } from '@japa/runner'
import { DatabaseUtils } from '@julr/japa-database-plugin'
import { defineFactory } from '@julr/factorio'
import { UserFactory } from '../tests-helpers/setup.js'
import { setupDb } from '../tests-helpers/db.js'

Expand Down Expand Up @@ -78,4 +79,29 @@ test.group('HasMany', (group) => {
await database.assertHas('post', { user_id: user.id, title: 'Rust' }, 1)
await database.assertHas('post', { user_id: user.id, title: 'AdonisJS' }, 1)
})

test('auto detect foreign and primary keys', async ({ database }) => {
const postFactory = defineFactory<any>(({ faker }) => ({
tableName: 'post',
fields: {
title: faker.company.bs(),
},
})).build()

const userFactory = defineFactory<any>(({ faker }) => ({
tableName: 'user',
fields: {
email: faker.internet.email(),
password: faker.random.alphaNumeric(6),
},
}))
.hasMany('post', { factory: postFactory })
.build()

const user = await userFactory.with('post', 5).create()

await database.assertCount('user', 1)
await database.assertCount('post', 5)
await database.assertHas('post', { user_id: user.id }, 5)
})
})
1 change: 1 addition & 0 deletions tests/has_one.spec.ts
@@ -1,5 +1,6 @@
import { test } from '@japa/runner'
import { DatabaseUtils } from '@julr/japa-database-plugin'
import { defineFactory } from '@julr/factorio'
import { UserFactory } from '../tests-helpers/setup.js'
import { setupDb } from '../tests-helpers/db.js'

Expand Down

0 comments on commit ff174c7

Please sign in to comment.