Skip to content

Commit 8241e4d

Browse files
committed
Supports "createMany" model method
1 parent 0721795 commit 8241e4d

File tree

4 files changed

+137
-1
lines changed

4 files changed

+137
-1
lines changed

src/factory.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import {
66
EntityInstance,
77
ModelDictionary,
88
PrimaryKeyType,
9+
RelationKind,
910
} from './glossary'
1011
import { first } from './utils/first'
1112
import { executeQuery } from './query/executeQuery'
1213
import { parseModelDeclaration } from './model/parseModelDeclaration'
1314
import { createModel } from './model/createModel'
1415
import { invariant } from './utils/invariant'
1516
import { updateEntity } from './model/updateEntity'
17+
import { getRandomNumber } from './utils/getRandomNumber'
1618

1719
/**
1820
* Create a database with the given models.
@@ -30,6 +32,7 @@ export function factory<Dictionary extends ModelDictionary>(
3032
modelName,
3133
props,
3234
db,
35+
acc,
3336
)
3437
return acc
3538
}, {})
@@ -38,7 +41,12 @@ export function factory<Dictionary extends ModelDictionary>(
3841
function createModelApi<
3942
Dictionary extends ModelDictionary,
4043
ModelName extends string
41-
>(modelName: ModelName, declaration: ModelDeclaration, db: Database) {
44+
>(
45+
modelName: ModelName,
46+
declaration: ModelDeclaration,
47+
db: Database,
48+
factory: FactoryAPI<Dictionary>,
49+
) {
4250
let modelPrimaryKey: PrimaryKeyType
4351

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

6977
return entity
7078
},
79+
createMany(count, options) {
80+
const resolvedCount = count || getRandomNumber(5, 10)
81+
const getRelations = options?.relations
82+
? () =>
83+
Object.entries(options.relations).reduce((acc, [key, count]) => {
84+
const propDeclaration = declaration[key]
85+
86+
if ('__type' in propDeclaration) {
87+
const isManyOfRelationType =
88+
propDeclaration.__type === RelationKind.ManyOf
89+
const resolvedCount = isManyOfRelationType
90+
? (count as number)
91+
: 1
92+
const relationalModel = factory[propDeclaration.modelName]
93+
const records = isManyOfRelationType
94+
? relationalModel.createMany(resolvedCount)
95+
: relationalModel.create()
96+
97+
acc[key] = records
98+
}
99+
100+
return acc
101+
}, {})
102+
: null
103+
104+
const createdEntities = new Array(resolvedCount)
105+
.fill(null)
106+
.map<EntityInstance<any, any>>(() => {
107+
return api.create(getRelations?.())
108+
})
109+
110+
return createdEntities
111+
},
71112
count() {
72113
return db[modelName].size
73114
},

src/glossary.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ export interface ModelAPI<
7878
create(
7979
initialValues?: Partial<Value<Dictionary[ModelName], Dictionary>>,
8080
): EntityInstance<Dictionary, ModelName>
81+
/**
82+
* Create multiple entities for the model.
83+
* @param count Amount of models to create.
84+
*/
85+
createMany(
86+
count?: number,
87+
options?: CreateManyOptions<Dictionary[ModelName]>,
88+
): EntityInstance<Dictionary, ModelName>[]
8189
/**
8290
* Return the total number of entities.
8391
*/
@@ -155,4 +163,19 @@ export type Value<
155163
: ReturnType<T[K]>
156164
}
157165

166+
type SubType<P, Condition> = Pick<
167+
P,
168+
{
169+
[K in keyof P]: P[K] extends Condition ? K : never
170+
}[keyof P]
171+
>
172+
173+
interface CreateManyOptions<T extends Record<string, any>> {
174+
relations: {
175+
[K in keyof SubType<T, OneOf<any> | ManyOf<any>>]: T[K] extends ManyOf<any>
176+
? number
177+
: boolean
178+
}
179+
}
180+
158181
export type Database = Record<KeyType, Map<string, EntityInstance<any, any>>>

src/utils/getRandomNumber.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Return a random number in range.
3+
* @param min The lowest number to return.
4+
* @param max The highest number to return.
5+
*/
6+
export function getRandomNumber(min: number = 5, max: number = 50) {
7+
return Math.floor(Math.random() * (max - min + 1) + min)
8+
}

test/model/createMany.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { name, random } from 'faker'
2+
import { factory, manyOf, oneOf, primaryKey } from '../../src'
3+
4+
test('creates multiple entities with a fixed count', () => {
5+
const db = factory({
6+
user: {
7+
id: primaryKey(random.uuid),
8+
firstName: name.firstName,
9+
},
10+
})
11+
12+
db.user.createMany(5)
13+
14+
const allUsers = db.user.getAll()
15+
expect(allUsers).toHaveLength(5)
16+
})
17+
18+
test('allows to specify count of relational entities to create', () => {
19+
const db = factory({
20+
user: {
21+
id: primaryKey(random.uuid),
22+
country: oneOf('country'),
23+
posts: manyOf('post'),
24+
},
25+
post: {
26+
id: primaryKey(random.uuid),
27+
title: random.words,
28+
},
29+
country: {
30+
id: primaryKey(random.uuid),
31+
name: random.word,
32+
},
33+
})
34+
35+
db.user.createMany(3, {
36+
relations: {
37+
/**
38+
* @todo How to specify that multiple entities should reuse the same relational model?
39+
* I.e. multiple users from the same country.
40+
*
41+
* @todo `Boolean` value of `oneOf` relation has no sense:
42+
* an entity cannot be created without all relational models specified.
43+
*/
44+
country: false,
45+
posts: 2,
46+
},
47+
})
48+
49+
const allUsers = db.user.getAll()
50+
const allPosts = db.post.getAll()
51+
const allCountries = db.country.getAll()
52+
53+
expect(allUsers).toHaveLength(3)
54+
// Each of 3 random "user" should have "2" posts created.
55+
expect(allPosts).toHaveLength(6)
56+
// Each of 3 random "user" should have its own "country".
57+
expect(allCountries).toHaveLength(3)
58+
59+
allUsers.forEach((user, index) => {
60+
expect(user.posts).toHaveLength(2)
61+
expect(user.country).toHaveProperty('id', allCountries[index].id)
62+
expect(user.country).toHaveProperty('name', allCountries[index].name)
63+
})
64+
})

0 commit comments

Comments
 (0)