Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
usu committed Mar 23, 2021
1 parent 6fc848d commit 1db66dc
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 50 deletions.
13 changes: 1 addition & 12 deletions src/EmbeddedCollection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// import LoadingStoreCollection from './LoadingStoreCollection'
import Resource, { EmbeddedCollectionType } from './interfaces/Resource'
import { EmbeddedCollectionType } from './interfaces/Resource'
import { Link } from './interfaces/StoreData'

/**
Expand Down Expand Up @@ -43,17 +43,6 @@ class EmbeddedCollection implements EmbeddedCollectionType {
}
}
}

/*
$loadItems () :Promise<Array<Resource>> {
return new Promise((resolve) => {
const items = this._storeData.items
// TODO: this is probably broken as LoadingStoreCollection has no constructor anymore
// if (items instanceof LoadingStoreCollection) items._meta.load.then(result => resolve(result))
// else resolve(items)
resolve(items)
})
} */
}

export default EmbeddedCollection
54 changes: 37 additions & 17 deletions src/HasItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@ type HasStoreData = GConstructor<{ _storeData: { items: Array<Link> } }>;

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActions, config: InternalConfig, reloadUri?: string, reloadProperty?: string) {
/**
* Returns a promise that resolves to items, once all items have been loaded
*/
function itemLoader (array: Array<Link>) : Promise<Array<Resource>> {
if (!containsUnknownEntityReference(array)) {
return Promise.resolve(replaceEntityReferences(array))
}

// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
if (config.avoidNPlusOneRequests) {
return apiActions.reload({ _meta: { reload: { uri: reloadUri || '', property: reloadProperty || '' } } })
.then(() => replaceEntityReferences(array))

// no eager loading: replace each reference (Link) with a StoreValue (Resource)
} else {
const arrayWithReplacedReferences = replaceEntityReferences(array)

return Promise.all(
arrayWithReplacedReferences.map(entry => entry._meta.load)
)
}
}

/**
* Filter out items that are marked as deleting (eager removal)
*/
Expand All @@ -35,26 +58,19 @@ function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActio
* @returns array the new array with replaced items, or a LoadingStoreCollection if any of the array
* elements is still loading.
*/
function mapArrayOfEntityReferences (array: Array<Link>, fetchAllUri: string, fetchAllProperty: string): Array<Resource> {
function mapArrayOfEntityReferences (array: Array<Link>): Array<Resource> {
if (!containsUnknownEntityReference(array)) {
return replaceEntityReferences(array)
}

const itemsLoaded = itemLoader(array)

// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
if (config.avoidNPlusOneRequests) {
const completelyLoaded = apiActions.reload({ _meta: { reload: { uri: fetchAllUri, property: fetchAllProperty } } })
.then(() => replaceEntityReferences(array))
return LoadingStoreCollection.create(completelyLoaded)

return LoadingStoreCollection.create(itemsLoaded)
// no eager loading: replace each reference (Link) with a StoreValue (Resource)
} else {
const arrayWithReplacedReferences = replaceEntityReferences(array)

const arrayCompletelyLoaded = Promise.all(
arrayWithReplacedReferences.map(entry => entry._meta.load)
)

return LoadingStoreCollection.create(arrayCompletelyLoaded, arrayWithReplacedReferences)
return LoadingStoreCollection.create(itemsLoaded, replaceEntityReferences(array))
}
}

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

const HasItems = class extends Base {
fetchAllUri = reloadUri || ''
fetchAllProperty = reloadProperty || ''

/**
* Get items excluding ones marked as 'deleting' (eager remove)
* The items property should always be a getter, in order to make the call to mapArrayOfEntityReferences
* lazy, since that potentially fetches a large number of entities from the API.
*/
public get items (): Array<Resource> {
return filterDeleting(mapArrayOfEntityReferences(this._storeData.items, this.fetchAllUri, this.fetchAllProperty))
return filterDeleting(mapArrayOfEntityReferences(this._storeData.items))
}

/**
* Get all items including ones marked as 'deleting' (lazy remove)
*/
public get allItems (): Array<Resource> {
return mapArrayOfEntityReferences(this._storeData.items, this.fetchAllUri, this.fetchAllProperty)
return mapArrayOfEntityReferences(this._storeData.items)
}

/**
* Returns a promise that resolves to items, once all items have been loaded
*/
public $loadItems () :Promise<Array<Resource>> {
return itemLoader(this._storeData.items)
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/LoadingStoreValue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import LoadingStoreCollection from './LoadingStoreCollection'
import Resource from './interfaces/Resource'
import Collection from './interfaces/Collection'

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

public $loadItems (): Promise<Resource> {
return this._meta.load
public $loadItems (): Promise<Array<Resource>> {
return this._meta.load.then(resource => (resource as Collection).$loadItems())
}

public $post (data: unknown):Promise<Resource> {
Expand Down
4 changes: 0 additions & 4 deletions src/StoreValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ class StoreValue implements Resource {
return this.apiActions.reload(this._meta.self)
}

$loadItems (): Promise<Resource> {
return this._meta.load
}

$post (data: unknown): Promise<Resource> {
return this.apiActions.post(this._meta.self, data)
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
const storeData = load(uri, forceReload)

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

Expand Down
1 change: 1 addition & 0 deletions src/interfaces/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import Resource from './Resource'
export default interface Collection {
items: Array<Resource>
allItems: Array<Resource>
$loadItems: () => Promise<Array<Resource>>
}
5 changes: 4 additions & 1 deletion src/interfaces/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ interface Resource {
}

$reload: () => Promise<Resource>
$loadItems: () => Promise<Resource>
$post: (data: unknown) => Promise<Resource>
$patch: (data: unknown) => Promise<Resource>
$del: () => Promise<string | void>

/**
* for collections only
*/
$loadItems?: () => Promise<Array<Resource>>
items?: Array<Resource>
allItems?: Array<Resource>
}
Expand Down
37 changes: 24 additions & 13 deletions tests/apiOperations.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ let vm
let stateCopy

describe('Using dollar methods', () => {

beforeAll(() => {
axios.defaults.baseURL = 'http://localhost'
Vue.use(Vuex)
Expand Down Expand Up @@ -313,9 +312,8 @@ describe('Using dollar methods', () => {
// then
await letNetworkRequestFinish()
const result = await load
expect(result).toMatchObject({ _meta: { self: 'http://localhost/camps' } })
expect(result.items).toHaveLength(1)
expect(result.items[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
expect(result).toHaveLength(1)
expect(result[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
done()
})

Expand Down Expand Up @@ -349,9 +347,8 @@ describe('Using dollar methods', () => {
// then
await letNetworkRequestFinish()
const result = await load
expect(result).toMatchObject({ _meta: { self: 'http://localhost/camps' } })
expect(result.items).toHaveLength(1)
expect(result.items[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
expect(result).toHaveLength(1)
expect(result[0]).toMatchObject({ id: 123, _meta: { self: 'http://localhost/items/123' } })
done()
})

Expand Down Expand Up @@ -412,7 +409,7 @@ describe('Using dollar methods', () => {
const bookResponse = {
id: 555,
_embedded: {
chapters: [ chapter1Response, chapter2Response, chapter3Response ]
chapters: [chapter1Response, chapter2Response, chapter3Response]
},
_links: {
self: {
Expand All @@ -433,8 +430,15 @@ describe('Using dollar methods', () => {

// then
await letNetworkRequestFinish()
await load
// expect no errors
const result = await load
expect(result).toHaveLength(3)
expect(result[0]).toMatchObject({
id: 1028,
name: 'The first chapter',
_meta: {
self: 'http://localhost/chapters/1028'
}
})
done()
})

Expand Down Expand Up @@ -495,7 +499,7 @@ describe('Using dollar methods', () => {
const bookResponse = {
id: 555,
_embedded: {
chapters: [ chapter1Response, chapter2Response, chapter3Response ]
chapters: [chapter1Response, chapter2Response, chapter3Response]
},
_links: {
self: {
Expand All @@ -514,8 +518,15 @@ describe('Using dollar methods', () => {

// then
await letNetworkRequestFinish()
await load
// expect no errors
const result = await load
expect(result).toHaveLength(3)
expect(result[0]).toMatchObject({
id: 1028,
name: 'The first chapter',
_meta: {
self: 'http://localhost/chapters/1028'
}
})
done()
})
})

0 comments on commit 1db66dc

Please sign in to comment.