Skip to content

Commit 1db66dc

Browse files
committed
fix
1 parent 6fc848d commit 1db66dc

8 files changed

+71
-50
lines changed

src/EmbeddedCollection.ts

+1-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// import LoadingStoreCollection from './LoadingStoreCollection'
2-
import Resource, { EmbeddedCollectionType } from './interfaces/Resource'
2+
import { EmbeddedCollectionType } from './interfaces/Resource'
33
import { Link } from './interfaces/StoreData'
44

55
/**
@@ -43,17 +43,6 @@ class EmbeddedCollection implements EmbeddedCollectionType {
4343
}
4444
}
4545
}
46-
47-
/*
48-
$loadItems () :Promise<Array<Resource>> {
49-
return new Promise((resolve) => {
50-
const items = this._storeData.items
51-
// TODO: this is probably broken as LoadingStoreCollection has no constructor anymore
52-
// if (items instanceof LoadingStoreCollection) items._meta.load.then(result => resolve(result))
53-
// else resolve(items)
54-
resolve(items)
55-
})
56-
} */
5746
}
5847

5948
export default EmbeddedCollection

src/HasItems.ts

+37-17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ type HasStoreData = GConstructor<{ _storeData: { items: Array<Link> } }>;
1818

1919
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2020
function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActions, config: InternalConfig, reloadUri?: string, reloadProperty?: string) {
21+
/**
22+
* Returns a promise that resolves to items, once all items have been loaded
23+
*/
24+
function itemLoader (array: Array<Link>) : Promise<Array<Resource>> {
25+
if (!containsUnknownEntityReference(array)) {
26+
return Promise.resolve(replaceEntityReferences(array))
27+
}
28+
29+
// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
30+
if (config.avoidNPlusOneRequests) {
31+
return apiActions.reload({ _meta: { reload: { uri: reloadUri || '', property: reloadProperty || '' } } })
32+
.then(() => replaceEntityReferences(array))
33+
34+
// no eager loading: replace each reference (Link) with a StoreValue (Resource)
35+
} else {
36+
const arrayWithReplacedReferences = replaceEntityReferences(array)
37+
38+
return Promise.all(
39+
arrayWithReplacedReferences.map(entry => entry._meta.load)
40+
)
41+
}
42+
}
43+
2144
/**
2245
* Filter out items that are marked as deleting (eager removal)
2346
*/
@@ -35,26 +58,19 @@ function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActio
3558
* @returns array the new array with replaced items, or a LoadingStoreCollection if any of the array
3659
* elements is still loading.
3760
*/
38-
function mapArrayOfEntityReferences (array: Array<Link>, fetchAllUri: string, fetchAllProperty: string): Array<Resource> {
61+
function mapArrayOfEntityReferences (array: Array<Link>): Array<Resource> {
3962
if (!containsUnknownEntityReference(array)) {
4063
return replaceEntityReferences(array)
4164
}
4265

66+
const itemsLoaded = itemLoader(array)
67+
4368
// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
4469
if (config.avoidNPlusOneRequests) {
45-
const completelyLoaded = apiActions.reload({ _meta: { reload: { uri: fetchAllUri, property: fetchAllProperty } } })
46-
.then(() => replaceEntityReferences(array))
47-
return LoadingStoreCollection.create(completelyLoaded)
48-
70+
return LoadingStoreCollection.create(itemsLoaded)
4971
// no eager loading: replace each reference (Link) with a StoreValue (Resource)
5072
} else {
51-
const arrayWithReplacedReferences = replaceEntityReferences(array)
52-
53-
const arrayCompletelyLoaded = Promise.all(
54-
arrayWithReplacedReferences.map(entry => entry._meta.load)
55-
)
56-
57-
return LoadingStoreCollection.create(arrayCompletelyLoaded, arrayWithReplacedReferences)
73+
return LoadingStoreCollection.create(itemsLoaded, replaceEntityReferences(array))
5874
}
5975
}
6076

@@ -75,23 +91,27 @@ function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActio
7591
}
7692

7793
const HasItems = class extends Base {
78-
fetchAllUri = reloadUri || ''
79-
fetchAllProperty = reloadProperty || ''
80-
8194
/**
8295
* Get items excluding ones marked as 'deleting' (eager remove)
8396
* The items property should always be a getter, in order to make the call to mapArrayOfEntityReferences
8497
* lazy, since that potentially fetches a large number of entities from the API.
8598
*/
8699
public get items (): Array<Resource> {
87-
return filterDeleting(mapArrayOfEntityReferences(this._storeData.items, this.fetchAllUri, this.fetchAllProperty))
100+
return filterDeleting(mapArrayOfEntityReferences(this._storeData.items))
88101
}
89102

90103
/**
91104
* Get all items including ones marked as 'deleting' (lazy remove)
92105
*/
93106
public get allItems (): Array<Resource> {
94-
return mapArrayOfEntityReferences(this._storeData.items, this.fetchAllUri, this.fetchAllProperty)
107+
return mapArrayOfEntityReferences(this._storeData.items)
108+
}
109+
110+
/**
111+
* Returns a promise that resolves to items, once all items have been loaded
112+
*/
113+
public $loadItems () :Promise<Array<Resource>> {
114+
return itemLoader(this._storeData.items)
95115
}
96116
}
97117

src/LoadingStoreValue.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import LoadingStoreCollection from './LoadingStoreCollection'
22
import Resource from './interfaces/Resource'
3+
import Collection from './interfaces/Collection'
34

45
/**
56
* Creates a placeholder for an entity which has not yet finished loading from the API.
@@ -78,8 +79,8 @@ class LoadingStoreValue implements Resource {
7879
return this._meta.load
7980
}
8081

81-
public $loadItems (): Promise<Resource> {
82-
return this._meta.load
82+
public $loadItems (): Promise<Array<Resource>> {
83+
return this._meta.load.then(resource => (resource as Collection).$loadItems())
8384
}
8485

8586
public $post (data: unknown):Promise<Resource> {

src/StoreValue.ts

-4
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,6 @@ class StoreValue implements Resource {
8282
return this.apiActions.reload(this._meta.self)
8383
}
8484

85-
$loadItems (): Promise<Resource> {
86-
return this._meta.load
87-
}
88-
8985
$post (data: unknown): Promise<Resource> {
9086
return this.apiActions.post(this._meta.self, data)
9187
}

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
146146
const storeData = load(uri, forceReload)
147147

148148
return forceReloadingEmbeddedCollection
149-
? storeValueCreator.wrap(storeData)[(uriOrEntity as EmbeddedCollection)._meta.reload.property]()
149+
? storeValueCreator.wrap(storeData)[(uriOrEntity as EmbeddedCollection)._meta.reload.property]() // TODO: this line doesn't return a Resource, although return type of get() is Resource
150150
: storeValueCreator.wrap(storeData)
151151
}
152152

src/interfaces/Collection.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ import Resource from './Resource'
33
export default interface Collection {
44
items: Array<Resource>
55
allItems: Array<Resource>
6+
$loadItems: () => Promise<Array<Resource>>
67
}

src/interfaces/Resource.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ interface Resource {
1111
}
1212

1313
$reload: () => Promise<Resource>
14-
$loadItems: () => Promise<Resource>
1514
$post: (data: unknown) => Promise<Resource>
1615
$patch: (data: unknown) => Promise<Resource>
1716
$del: () => Promise<string | void>
1817

18+
/**
19+
* for collections only
20+
*/
21+
$loadItems?: () => Promise<Array<Resource>>
1922
items?: Array<Resource>
2023
allItems?: Array<Resource>
2124
}

tests/apiOperations.spec.js

+24-13
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ let vm
2323
let stateCopy
2424

2525
describe('Using dollar methods', () => {
26-
2726
beforeAll(() => {
2827
axios.defaults.baseURL = 'http://localhost'
2928
Vue.use(Vuex)
@@ -313,9 +312,8 @@ describe('Using dollar methods', () => {
313312
// then
314313
await letNetworkRequestFinish()
315314
const result = await load
316-
expect(result).toMatchObject({ _meta: { self: 'http://localhost/camps' } })
317-
expect(result.items).toHaveLength(1)
318-
expect(result.items[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
315+
expect(result).toHaveLength(1)
316+
expect(result[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
319317
done()
320318
})
321319

@@ -349,9 +347,8 @@ describe('Using dollar methods', () => {
349347
// then
350348
await letNetworkRequestFinish()
351349
const result = await load
352-
expect(result).toMatchObject({ _meta: { self: 'http://localhost/camps' } })
353-
expect(result.items).toHaveLength(1)
354-
expect(result.items[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
350+
expect(result).toHaveLength(1)
351+
expect(result[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
355352
done()
356353
})
357354

@@ -412,7 +409,7 @@ describe('Using dollar methods', () => {
412409
const bookResponse = {
413410
id: 555,
414411
_embedded: {
415-
chapters: [ chapter1Response, chapter2Response, chapter3Response ]
412+
chapters: [chapter1Response, chapter2Response, chapter3Response]
416413
},
417414
_links: {
418415
self: {
@@ -433,8 +430,15 @@ describe('Using dollar methods', () => {
433430

434431
// then
435432
await letNetworkRequestFinish()
436-
await load
437-
// expect no errors
433+
const result = await load
434+
expect(result).toHaveLength(3)
435+
expect(result[0]).toMatchObject({
436+
id: 1028,
437+
name: 'The first chapter',
438+
_meta: {
439+
self: 'http://localhost/chapters/1028'
440+
}
441+
})
438442
done()
439443
})
440444

@@ -495,7 +499,7 @@ describe('Using dollar methods', () => {
495499
const bookResponse = {
496500
id: 555,
497501
_embedded: {
498-
chapters: [ chapter1Response, chapter2Response, chapter3Response ]
502+
chapters: [chapter1Response, chapter2Response, chapter3Response]
499503
},
500504
_links: {
501505
self: {
@@ -514,8 +518,15 @@ describe('Using dollar methods', () => {
514518

515519
// then
516520
await letNetworkRequestFinish()
517-
await load
518-
// expect no errors
521+
const result = await load
522+
expect(result).toHaveLength(3)
523+
expect(result[0]).toMatchObject({
524+
id: 1028,
525+
name: 'The first chapter',
526+
_meta: {
527+
self: 'http://localhost/chapters/1028'
528+
}
529+
})
519530
done()
520531
})
521532
})

0 commit comments

Comments
 (0)