Skip to content

Commit

Permalink
feat: add custom casing strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed Sep 20, 2022
1 parent 58e1f69 commit cd02b06
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 6 deletions.
21 changes: 20 additions & 1 deletion README.md
Expand Up @@ -43,13 +43,15 @@ Integrations for some test runners are available below :
- [Japa](./packages/japa-plugin/)
- [Vitest](./packages/vitest-plugin/) ( 馃毀 Coming soon )

## Defining database connection
## Defining configuration and database connection

Before running your tests, you must initialize Factorio with your database configuration.

This must be done **BEFORE** creating models via Factorio. In general, you can use the setup files system provided by the test runners.

```ts
import { defineFactorioConfig } from '@julr/factorio'

const disconnect = defineFactorioConfig({
// Can also specify a locale for faker
locale: 'fr',
Expand All @@ -76,6 +78,23 @@ This is useful when you want to cleanly disconnect from the database after all t

> **Note**: You don't need to do this manually if you are using a test runner integration.
### Casing Strategy
You can also define a specific casing strategy. By default, Factorio convert all keys to `snake_case` before inserting the models into the database. And before returning the model, it converts all keys to `camelCase`.

```ts
import { defineFactorioConfig } from '@julr/factorio'

defineFactorioConfig({
casing: {
// Convert all keys to snake_case before inserting into the database
insert: 'snake',

// Convert all keys to camelCase before returning the models
return: 'camel',
}
})
```

## Creating factories

```ts
Expand Down
8 changes: 3 additions & 5 deletions packages/core/src/builder/builder.ts
@@ -1,15 +1,13 @@
import { faker } from '@faker-js/faker'
import defu from 'defu'
import humps from 'humps'
import { factorioConfig } from '../config'
import { convertCase } from '../utils'
import { RelationshipBuilder } from './relationship_builder'
import { StatesManager } from './states_manager'
import type { FactoryModel } from '../model'
import type { FactoryExtractGeneric, WithCallback } from '../contracts'
import type { Knex } from 'knex'

const { camelizeKeys, decamelizeKeys } = humps

export class Builder<
Factory extends FactoryModel<any, any>,
Model extends Record<string, any> = FactoryExtractGeneric<Factory, 'model'>,
Expand Down Expand Up @@ -162,7 +160,7 @@ export class Builder<
* Insert rows
*/
const res = await factorioConfig
.knex!.insert(decamelizeKeys(models))
.knex!.insert(convertCase(models, factorioConfig.casing.insert))
.into(this.factory.tableName)
.returning('*')

Expand All @@ -178,6 +176,6 @@ export class Builder<

this.resetBuilder()

return finalModels.map((model) => camelizeKeys(model)) as Model[]
return finalModels.map((model) => convertCase(model, factorioConfig.casing.return)) as Model[]
}
}
5 changes: 5 additions & 0 deletions packages/core/src/config.ts
Expand Up @@ -9,6 +9,10 @@ import type { FactorioConfig } from './contracts'
*/
export const factorioConfig = {
knex: null as Knex | null,
casing: {
insert: 'snake',
return: 'camel',
} as NonNullable<FactorioConfig['casing']>,
}

/**
Expand All @@ -20,6 +24,7 @@ export const defineFactorioConfig = (options: FactorioConfig & { locale?: Usable
faker.locale = options.locale || faker.locale

factorioConfig.knex = knex(options.database)
factorioConfig.casing = options.casing || factorioConfig.casing

return () => {
if (factorioConfig.knex) {
Expand Down
21 changes: 21 additions & 0 deletions packages/core/src/contracts.ts
Expand Up @@ -5,6 +5,8 @@ import type { Knex } from 'knex'

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

export type CasingStrategy = 'camel' | 'snake' | 'none'

/**
* Callback that must be passed to the `defineFactory` function.
*/
Expand All @@ -22,6 +24,25 @@ export type DefineStateCallback<T> = (attributes: T) => Partial<T>
*/
export interface FactorioConfig {
database: Knex.Config

/**
* Configure the casing conversion for the database operations
*/
casing?: {
/**
* Casing to which the keys will be converted before inserting into the database
*
* Default: `snake`
*/
insert: CasingStrategy

/**
* Casing to which the keys will be converted before returning
*
* Default: `camel`
*/
return: CasingStrategy
}
}

/**
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/utils.ts
@@ -0,0 +1,14 @@
import humps from 'humps'
import type { CasingStrategy } from './contracts.js'

export function convertCase(obj: Record<string, any>, casing: CasingStrategy) {
if (casing === 'camel') {
return humps.camelizeKeys(obj)
}

if (casing === 'snake') {
return humps.decamelizeKeys(obj)
}

return obj
}

0 comments on commit cd02b06

Please sign in to comment.