Skip to content

Commit 09496aa

Browse files
authored
fix(repository): implement missing add & replace methods (#58)
1 parent 44c4303 commit 09496aa

10 files changed

+273
-21
lines changed

src/model/Model.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,12 +417,18 @@ export class Model {
417417
/**
418418
* Get the index id of this model or for a given record.
419419
*/
420-
$getIndexId(record?: Element): string | null {
420+
$getIndexId(record?: Element): string {
421421
const target = record ?? this
422422

423423
const id = this.$getKey(target)
424424

425-
return id === null ? null : this.$stringifyId(id)
425+
assert(id !== null, [
426+
'The record is missing the primary key. If you want to persist record',
427+
'without the primary key, please define the primary key field with the',
428+
'`uid` attribute.'
429+
])
430+
431+
return this.$stringifyId(id)
426432
}
427433

428434
/**

src/query/Query.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ export class Query<M extends Model = Model> {
450450
const recordsArray = isArray(records) ? records : [records]
451451

452452
const models = recordsArray.reduce<Collection<M>>((collection, record) => {
453-
const model = this.pick(this.model.$getIndexId(record)!)
453+
const model = this.pick(this.model.$getIndexId(record))
454454

455455
model && collection.push(model.$fill(record))
456456

@@ -615,7 +615,7 @@ export class Query<M extends Model = Model> {
615615
* Get an array of ids from the given collection.
616616
*/
617617
protected getIndexIdsFromCollection(models: Collection<M>): string[] {
618-
return models.map((model) => model.$getIndexId()!)
618+
return models.map((model) => model.$getIndexId())
619619
}
620620

621621
/**
@@ -637,7 +637,7 @@ export class Query<M extends Model = Model> {
637637
const modelArray = isArray(models) ? models : [models]
638638

639639
return modelArray.reduce<Elements>((records, model) => {
640-
records[model.$getIndexId()!] = model.$getAttributes()
640+
records[model.$getIndexId()] = model.$getAttributes()
641641
return records
642642
}, {})
643643
}

src/repository/Repository.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,19 +175,38 @@ export class Repository<M extends Model = Model> {
175175
}
176176

177177
/**
178-
* Insert the given record to the store.
178+
* Insert the given records to the store.
179179
*/
180180
insert(records: Element | Element[]): Promise<Collections> {
181181
return this.query().insert(records)
182182
}
183183

184+
/**
185+
* Insert the given records to the store without normalization.
186+
*/
187+
add(records: Element[]): Promise<Collection<M>>
188+
add(record: Element): Promise<M>
189+
add(records: any): Promise<any> {
190+
return this.query().add(records)
191+
}
192+
184193
/**
185194
* Insert the given records to the store by replacing any existing records.
186195
*/
187196
fresh(records: Element | Element[]): Promise<Collections> {
188197
return this.query().fresh(records)
189198
}
190199

200+
/**
201+
* Insert the given records to the store by replacing any existing records
202+
* without normalization.
203+
*/
204+
replace(records: Element[]): Promise<Collection<M>>
205+
replace(record: Element): Promise<M>
206+
replace(records: any): Promise<any> {
207+
return this.query().replace(records)
208+
}
209+
191210
/**
192211
* Update records in the store.
193212
*/

src/schema/Schema.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { schema as Normalizr, Schema as NormalizrSchema } from 'normalizr'
2-
import { isNullish, isArray, assert } from '../support/Utils'
2+
import { isNullish, isArray } from '../support/Utils'
33
import { Uid } from '../model/attributes/types/Uid'
44
import { Relation } from '../model/attributes/relations/Relation'
55
import { Model } from '../model/Model'
@@ -111,20 +111,8 @@ export class Schema {
111111
}
112112
}
113113

114-
// Finally, we'll check if the model has a valid index id. If not, that
115-
// means users have passed in the record without a primary key, and the
116-
// primary key field is not defined as uid field. In this case, we'll
117-
// throw an error. Otherwise, everything is fine, so let's return the
118-
// index id.
119-
const indexId = model.$getIndexId(record)
120-
121-
assert(indexId !== null, [
122-
'The record is missing the primary key. If you want to persist record',
123-
'without the primary key, please defined the primary key field as',
124-
'`uid` field.'
125-
])
126-
127-
return indexId
114+
// Finally, obtain the index id and return.
115+
return model.$getIndexId(record)
128116
}
129117
}
130118

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { createStore, assertState } from 'test/Helpers'
2+
import { Model, Attr, Str } from '@/index'
3+
4+
describe('feature/repository/inserts_add', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
@Attr() id!: any
9+
@Str('') name!: string
10+
}
11+
12+
it('inserts a record to the store', async () => {
13+
const store = createStore()
14+
15+
await store.$repo(User).add({ id: 1, name: 'John Doe' })
16+
17+
assertState(store, {
18+
users: {
19+
1: { id: 1, name: 'John Doe' }
20+
}
21+
})
22+
})
23+
24+
it('inserts multiple records to the store', async () => {
25+
const store = createStore()
26+
27+
await store.$repo(User).add([
28+
{ id: 1, name: 'John Doe' },
29+
{ id: 2, name: 'Jane Doe' }
30+
])
31+
32+
assertState(store, {
33+
users: {
34+
1: { id: 1, name: 'John Doe' },
35+
2: { id: 2, name: 'Jane Doe' }
36+
}
37+
})
38+
})
39+
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { createStore, assertState } from 'test/Helpers'
2+
import { Model, Attr, Str } from '@/index'
3+
4+
describe('feature/repository/inserts_add_composite_key', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
static primaryKey = ['idA', 'idB']
9+
10+
@Attr() idA!: any
11+
@Attr() idB!: any
12+
@Str('') name!: string
13+
}
14+
15+
it('inserts records with a composite key', async () => {
16+
const store = createStore()
17+
18+
await store.$repo(User).add([
19+
{ idA: 1, idB: 2, name: 'John Doe' },
20+
{ idA: 2, idB: 1, name: 'Jane Doe' }
21+
])
22+
23+
assertState(store, {
24+
users: {
25+
'[1,2]': { idA: 1, idB: 2, name: 'John Doe' },
26+
'[2,1]': { idA: 2, idB: 1, name: 'Jane Doe' }
27+
}
28+
})
29+
})
30+
31+
it('throws if composite key is incomplete', async () => {
32+
expect.assertions(1)
33+
34+
const store = createStore()
35+
36+
try {
37+
await store.$repo(User).add({ idA: 1, name: 'John Doe' })
38+
} catch (e) {
39+
expect(e).toBeInstanceOf(Error)
40+
}
41+
})
42+
})
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { createStore, assertState, mockUid } from 'test/Helpers'
2+
import { Model, Uid, Str } from '@/index'
3+
4+
describe('feature/uid/inserts_add_uid', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
@Uid() id!: string | null
9+
@Str('') name!: string
10+
}
11+
12+
it('generates a unique id for a `uid` attribute', async () => {
13+
mockUid(['uid1', 'uid2'])
14+
15+
const store = createStore()
16+
17+
await store.$repo(User).add([{ name: 'John Doe' }, { name: 'Jane Doe' }])
18+
19+
assertState(store, {
20+
users: {
21+
uid1: { id: 'uid1', name: 'John Doe' },
22+
uid2: { id: 'uid2', name: 'Jane Doe' }
23+
}
24+
})
25+
})
26+
})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { createStore, fillState, assertState } from 'test/Helpers'
2+
import { Model, Attr, Str } from '@/index'
3+
4+
describe('feature/repository/inserts_replace', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
@Attr() id!: any
9+
@Str('') name!: string
10+
}
11+
12+
it('inserts a record in the store', async () => {
13+
const store = createStore()
14+
15+
await store.$repo(User).replace({ id: 1, name: 'John Doe' })
16+
17+
assertState(store, {
18+
users: {
19+
1: { id: 1, name: 'John Doe' }
20+
}
21+
})
22+
})
23+
24+
it('inserts multiple records in the store', async () => {
25+
const store = createStore()
26+
27+
await store.$repo(User).replace([
28+
{ id: 1, name: 'John Doe' },
29+
{ id: 2, name: 'Jane Doe' }
30+
])
31+
32+
assertState(store, {
33+
users: {
34+
1: { id: 1, name: 'John Doe' },
35+
2: { id: 2, name: 'Jane Doe' }
36+
}
37+
})
38+
})
39+
40+
it('replaces existing records', async () => {
41+
const store = createStore()
42+
43+
fillState(store, {
44+
users: {
45+
1: { id: 1, name: 'John Doe' },
46+
2: { id: 2, name: 'Jane Doe' }
47+
}
48+
})
49+
50+
await store.$repo(User).replace([
51+
{ id: 3, name: 'Johnny Doe' },
52+
{ id: 4, name: 'David Doe' }
53+
])
54+
55+
assertState(store, {
56+
users: {
57+
3: { id: 3, name: 'Johnny Doe' },
58+
4: { id: 4, name: 'David Doe' }
59+
}
60+
})
61+
})
62+
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { createStore, assertState } from 'test/Helpers'
2+
import { Model, Attr, Str } from '@/index'
3+
4+
describe('feature/repository/inserts_replace_composite_key', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
static primaryKey = ['idA', 'idB']
9+
10+
@Attr() idA!: any
11+
@Attr() idB!: any
12+
@Str('') name!: string
13+
}
14+
15+
it('inserts records with a composite key', async () => {
16+
const store = createStore()
17+
18+
await store.$repo(User).replace([
19+
{ idA: 1, idB: 2, name: 'John Doe' },
20+
{ idA: 2, idB: 1, name: 'Jane Doe' }
21+
])
22+
23+
assertState(store, {
24+
users: {
25+
'[1,2]': { idA: 1, idB: 2, name: 'John Doe' },
26+
'[2,1]': { idA: 2, idB: 1, name: 'Jane Doe' }
27+
}
28+
})
29+
})
30+
31+
it('throws if composite key is incomplete', async () => {
32+
expect.assertions(1)
33+
34+
const store = createStore()
35+
36+
try {
37+
await store.$repo(User).replace({ idA: 1, name: 'John Doe' })
38+
} catch (e) {
39+
expect(e).toBeInstanceOf(Error)
40+
}
41+
})
42+
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { createStore, assertState, mockUid } from 'test/Helpers'
2+
import { Model, Uid, Str } from '@/index'
3+
4+
describe('feature/uid/inserts_replace_uid', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
@Uid() id!: string | null
9+
@Str('') name!: string
10+
}
11+
12+
it('generates a unique id for a `uid` attribute', async () => {
13+
mockUid(['uid1', 'uid2'])
14+
15+
const store = createStore()
16+
17+
await store
18+
.$repo(User)
19+
.replace([{ name: 'John Doe' }, { name: 'Jane Doe' }])
20+
21+
assertState(store, {
22+
users: {
23+
uid1: { id: 'uid1', name: 'John Doe' },
24+
uid2: { id: 'uid2', name: 'Jane Doe' }
25+
}
26+
})
27+
})
28+
})

0 commit comments

Comments
 (0)