Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ let someEntity = this.$api.get('/some/endpoint')
<li v-for="book in $api.get('/all/my/books').items" :key="book._meta.self">...</li>
```

**Known limitations:** The current implementation has so far only be tested to generate static HTML on server side (nuxt config `injectScripts` set to `false`) without client side SPA. Serialization of Vuex store data & hydration on client side will probably not work without errors.

# Available options

### apiName
Expand Down
6 changes: 5 additions & 1 deletion src/LoadingStoreValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class LoadingStoreValue implements Resource {

// This is necessary so that Vue's reactivity system understands to treat this LoadingStoreValue
// like a normal object.
if (['then', 'toJSON', Symbol.toStringTag, 'state', 'getters', '$options', '_isVue', '__file', 'render', 'constructor'].includes(prop as string)) {
if (['then', Symbol.toStringTag, 'state', 'getters', '$options', '_isVue', '__file', 'render', 'constructor'].includes(prop as string)) {
return undefined
}

Expand Down Expand Up @@ -99,6 +99,10 @@ class LoadingStoreValue implements Resource {
public $href (relation: string, templateParams = {}): Promise<string | undefined> {
return this._meta.load.then(resource => resource.$href(relation, templateParams))
}

public toJSON (): string {
return '{}'
}
}

export default LoadingStoreValue
10 changes: 10 additions & 0 deletions src/StoreValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ class StoreValue implements Resource {
$href (relation: string, templateParams = {}): Promise<string | undefined> {
return this.apiActions.href(this, relation, templateParams)
}

/**
* Serialize object to JSON
* this avoid warnings in Nuxt "Cannot stringify arbitrary non-POJOs"
*/
toJSON (): string {
// for the lack of any better alternative, return store data as JSON
// alternatively: could also return '{}', as the data cannot be used directly, anyway
return JSON.stringify(this._storeData)
}
}

export default StoreValue
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ExternalConfig } from './interfaces/Config'
import { Store } from 'vuex/types'
import { AxiosInstance, AxiosError } from 'axios'
import Resource from './interfaces/Resource'
import StoreData, { Link } from './interfaces/StoreData'
import StoreData, { Link, SerializablePromise } from './interfaces/StoreData'
import ApiActions from './interfaces/ApiActions'
import EmbeddedCollectionClass from './EmbeddedCollection'
import EmbeddedCollection, { EmbeddedCollectionMeta } from './interfaces/EmbeddedCollection'
Expand Down Expand Up @@ -407,7 +407,9 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
* @param loadStoreData
*/
function setLoadPromiseOnStore (uri: string, loadStoreData: Promise<StoreData> | null = null) {
store.state[opts.apiName][uri]._meta.load = loadStoreData || Promise.resolve(store.state[opts.apiName][uri])
const promise: SerializablePromise<StoreData> = loadStoreData || Promise.resolve(store.state[opts.apiName][uri])
promise.toJSON = () => '{}' // avoid warning in Nuxt when serializing the complete Vuex store ("Cannot stringify arbitrary non-POJOs Promise")
store.state[opts.apiName][uri]._meta.load = promise
}

/**
Expand Down
10 changes: 7 additions & 3 deletions src/interfaces/StoreData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ type TemplatedLink = Link & {
templated: string
}

type SerializablePromise<T> = Promise<T> & {
toJSON?: () => string
}

type StoreDataMeta = {
_meta: {
self: string
Expand All @@ -18,19 +22,19 @@ type StoreDataMeta = {
type StoreDataEntity = StoreDataMeta & {
items: never,
_meta: {
load: Promise<StoreDataEntity>
load: SerializablePromise<StoreDataEntity>
}
}

type StoreDataCollection = StoreDataMeta & {
items: Array<Link>,
_meta: {
load: Promise<StoreDataCollection>
load: SerializablePromise<StoreDataCollection>
}
}

type StoreData = StoreDataEntity | StoreDataCollection

export { StoreData, Link, TemplatedLink, StoreDataEntity, StoreDataCollection }
export { StoreData, Link, TemplatedLink, StoreDataEntity, StoreDataCollection, SerializablePromise }

export default StoreData
21 changes: 21 additions & 0 deletions tests/store.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import multipleReferencesToUser from './resources/multiple-references-to-user'
import templatedLink from './resources/templated-link'
import root from './resources/root'

import LoadingStoreValue from '../src/LoadingStoreValue'
import StoreValue from '../src/StoreValue'

async function letNetworkRequestFinish () {
await new Promise(resolve => {
setTimeout(() => resolve())
Expand Down Expand Up @@ -70,6 +73,24 @@ describe('API store', () => {
expect(vm.$store.state.api).toMatchObject(root.storeState)
})

it('can serialize StoreValue object', async done => {
// given
axiosMock.onGet('http://localhost/').reply(200, root.serverResponse)

// when
const loadingObject = vm.api.get()

// then (loading)
expect(loadingObject).toBeInstanceOf(LoadingStoreValue)
expect(loadingObject.toJSON()).toEqual('{}')

// then (loaded)
const loadedObject = await loadingObject._meta.load
expect(loadedObject).toBeInstanceOf(StoreValue)
expect(loadedObject.toJSON()).toEqual('{"this":"is","the":"root","_meta":{"self":"","loading":false,"reloading":false,"load":"{}"}}')
done()
})

it('imports embedded single entity', async () => {
// given
axiosMock.onGet('http://localhost/camps/1').reply(200, embeddedSingleEntity.serverResponse)
Expand Down