Skip to content

Commit

Permalink
Supports "createMany" model method
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Dec 23, 2020
1 parent 0721795 commit 8241e4d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 1 deletion.
43 changes: 42 additions & 1 deletion src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import {
EntityInstance,
ModelDictionary,
PrimaryKeyType,
RelationKind,
} from './glossary'
import { first } from './utils/first'
import { executeQuery } from './query/executeQuery'
import { parseModelDeclaration } from './model/parseModelDeclaration'
import { createModel } from './model/createModel'
import { invariant } from './utils/invariant'
import { updateEntity } from './model/updateEntity'
import { getRandomNumber } from './utils/getRandomNumber'

/**
* Create a database with the given models.
Expand All @@ -30,6 +32,7 @@ export function factory<Dictionary extends ModelDictionary>(
modelName,
props,
db,
acc,
)
return acc
}, {})
Expand All @@ -38,7 +41,12 @@ export function factory<Dictionary extends ModelDictionary>(
function createModelApi<
Dictionary extends ModelDictionary,
ModelName extends string
>(modelName: ModelName, declaration: ModelDeclaration, db: Database) {
>(
modelName: ModelName,
declaration: ModelDeclaration,
db: Database,
factory: FactoryAPI<Dictionary>,
) {
let modelPrimaryKey: PrimaryKeyType

const api: ModelAPI<Dictionary, ModelName> = {
Expand Down Expand Up @@ -68,6 +76,39 @@ function createModelApi<

return entity
},
createMany(count, options) {
const resolvedCount = count || getRandomNumber(5, 10)
const getRelations = options?.relations
? () =>
Object.entries(options.relations).reduce((acc, [key, count]) => {
const propDeclaration = declaration[key]

if ('__type' in propDeclaration) {
const isManyOfRelationType =
propDeclaration.__type === RelationKind.ManyOf
const resolvedCount = isManyOfRelationType
? (count as number)
: 1
const relationalModel = factory[propDeclaration.modelName]
const records = isManyOfRelationType
? relationalModel.createMany(resolvedCount)
: relationalModel.create()

acc[key] = records
}

return acc
}, {})
: null

const createdEntities = new Array(resolvedCount)
.fill(null)
.map<EntityInstance<any, any>>(() => {
return api.create(getRelations?.())
})

return createdEntities
},
count() {
return db[modelName].size
},
Expand Down
23 changes: 23 additions & 0 deletions src/glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ export interface ModelAPI<
create(
initialValues?: Partial<Value<Dictionary[ModelName], Dictionary>>,
): EntityInstance<Dictionary, ModelName>
/**
* Create multiple entities for the model.
* @param count Amount of models to create.
*/
createMany(
count?: number,
options?: CreateManyOptions<Dictionary[ModelName]>,
): EntityInstance<Dictionary, ModelName>[]
/**
* Return the total number of entities.
*/
Expand Down Expand Up @@ -155,4 +163,19 @@ export type Value<
: ReturnType<T[K]>
}

type SubType<P, Condition> = Pick<
P,
{
[K in keyof P]: P[K] extends Condition ? K : never
}[keyof P]
>

interface CreateManyOptions<T extends Record<string, any>> {
relations: {
[K in keyof SubType<T, OneOf<any> | ManyOf<any>>]: T[K] extends ManyOf<any>
? number
: boolean
}
}

export type Database = Record<KeyType, Map<string, EntityInstance<any, any>>>
8 changes: 8 additions & 0 deletions src/utils/getRandomNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Return a random number in range.
* @param min The lowest number to return.
* @param max The highest number to return.
*/
export function getRandomNumber(min: number = 5, max: number = 50) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
64 changes: 64 additions & 0 deletions test/model/createMany.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { name, random } from 'faker'
import { factory, manyOf, oneOf, primaryKey } from '../../src'

test('creates multiple entities with a fixed count', () => {
const db = factory({
user: {
id: primaryKey(random.uuid),
firstName: name.firstName,
},
})

db.user.createMany(5)

const allUsers = db.user.getAll()
expect(allUsers).toHaveLength(5)
})

test('allows to specify count of relational entities to create', () => {
const db = factory({
user: {
id: primaryKey(random.uuid),
country: oneOf('country'),
posts: manyOf('post'),
},
post: {
id: primaryKey(random.uuid),
title: random.words,
},
country: {
id: primaryKey(random.uuid),
name: random.word,
},
})

db.user.createMany(3, {
relations: {
/**
* @todo How to specify that multiple entities should reuse the same relational model?
* I.e. multiple users from the same country.
*
* @todo `Boolean` value of `oneOf` relation has no sense:
* an entity cannot be created without all relational models specified.
*/
country: false,
posts: 2,
},
})

const allUsers = db.user.getAll()
const allPosts = db.post.getAll()
const allCountries = db.country.getAll()

expect(allUsers).toHaveLength(3)
// Each of 3 random "user" should have "2" posts created.
expect(allPosts).toHaveLength(6)
// Each of 3 random "user" should have its own "country".
expect(allCountries).toHaveLength(3)

allUsers.forEach((user, index) => {
expect(user.posts).toHaveLength(2)
expect(user.country).toHaveProperty('id', allCountries[index].id)
expect(user.country).toHaveProperty('name', allCountries[index].name)
})
})

0 comments on commit 8241e4d

Please sign in to comment.