Skip to content

Commit 50d82e8

Browse files
authored
Merge pull request mrichar1#54 from mrichar1/axios_conf
Refactor axios to allow more config overrides.
2 parents 25ccc6b + 4f39779 commit 50d82e8

File tree

6 files changed

+87
-12
lines changed

6 files changed

+87
-12
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ A module to access [JSONAPI](https://jsonapi.org) data from an API, using a Vuex
1515
- Uses [jsonpath](https://github.com/dchester/jsonpath) for filtering when getting objects from the store.
1616
- Records the status of actions (LOADING, SUCCESS, ERROR).
1717
- New data can overwrite, or be merged onto, existing records. (See [mergeRecords](#Configuration))
18+
- Override endpoint names per-request (for plural names etc). (See [Endpoints](#Endpoints))
1819

1920
## Setup
2021

@@ -56,6 +57,8 @@ There are a number of features which are worth explaining in more detail. Many o
5657

5758
- _Preserve JSON_ - The original JSONAPI record(s) can optionally be preserved in `_jv` if needed - for example if you need access to `meta` or other sections. To avoid duplication, the `data` section (`attributes`, `relationships` etc) is removed.
5859

60+
- _Endpoints_ - by default this module assumes that object types and API endpoints (item and collection) all share the same name. however, some APIs use plurals or other variations on the endpoint names. You can override the endpoint name via the `axios` `url` config option (see [Endpoints](#endpoints))
61+
5962
### Actions
6063

6164
The usual way to use this module is to use `actions` wherever possible. All actions are asynchronous, and both query the API and update the store, then return data in a normalized form. Every action call's state is tracked as it progresses, and this status can be easily queried (see the `status` getter).
@@ -373,6 +376,43 @@ For many of these options, more information is provided in the [Usage](#usage) s
373376
- `actionStatusCleanAge` - What age must action status records be before they are removed (defaults to 600 seconds). Set to `0` to disable.
374377
- `mergeRecords`- Whether new records should be merged onto existing records in the store, or just replace them (defaults to `false`).
375378

379+
## Endpoints
380+
381+
By default `jsonapi-vuex` assumes that object type and API endpoint are the same. For example, `type: person` would have endpoint URLs of `/person` and `/person/1` for collections and single items.
382+
383+
However many APIs vary how endpoints are named - for example plurals (e.g. `type:person`, `/person/1` and `/people`), or cases where the endpoint doesn't match the type (e.g. `type: person` `/author` and `/author/1`) or even a combination (e.g. `type: person` `/author/1` and `/authors`)
384+
385+
To solve this it is possible to override the endpoint for a request by explicitly setting the `axios` `url` configuration option:
386+
387+
```
388+
data = { _jv: { type: 'person' } }
389+
390+
// Default behaviour - will always treat type = itemEP = collectionEP
391+
this.$store.dispatch('jv/get', data)
392+
// GETs /person
393+
394+
// Explicitly override the endpoint url
395+
this.$store.dispatch('jv/get', [data, { url: '/people' }])
396+
397+
this.$store.dispatch('jv/get', [data, { url: '/author/1' }])
398+
399+
// Override using a dynamic function
400+
const customUrl = (data) => {
401+
if (data.hasOwnProperty('id')) {
402+
// single item (singular)
403+
return `person/${data.id}`
404+
} else {
405+
// collection (plural)
406+
return '/people'
407+
}
408+
}
409+
410+
this.$store.dispatch('jv/get', [{ _jv: { type: 'widget' } }, { url: customUrl(data) }])
411+
412+
```
413+
414+
_Note_ - If provided the `url` option is used as-is - you are responsible for setting a valid collection or item url (with `id`) as appropriate.
415+
376416
## Restructured Data
377417

378418
JSONAPI is an extremely useful format for clearly interacting with an API - however it is less useful for the end developer, who is generally more interested in the data contained in a record than the structure surrounding it.

src/jsonapi-vuex.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,13 @@ const actions = (api) => {
6868
get: (context, args) => {
6969
const [data, config] = unpackArgs(args)
7070
const path = getURL(data)
71+
const apiConf = { method: 'get', url: path }
7172
// https://github.com/axios/axios/issues/362
7273
config['data'] = config['data'] || {}
74+
merge(apiConf, config)
7375
const actionId = actionSequence(context)
7476
context.commit('setStatus', { id: actionId, status: STATUS_LOAD })
75-
let action = api
76-
.get(path, config)
77+
let action = api(apiConf)
7778
.then((results) => {
7879
processIncludedRecords(context, results)
7980

@@ -185,10 +186,11 @@ const actions = (api) => {
185186
post: (context, args) => {
186187
let [data, config] = unpackArgs(args)
187188
const path = getURL(data, true)
189+
const apiConf = { method: 'post', url: path, data: normToJsonapi(data) }
190+
merge(apiConf, config)
188191
const actionId = actionSequence(context)
189192
context.commit('setStatus', { id: actionId, status: STATUS_LOAD })
190-
let action = api
191-
.post(path, normToJsonapi(data), config)
193+
let action = api(apiConf)
192194
.then((results) => {
193195
processIncludedRecords(context, results)
194196

@@ -215,9 +217,10 @@ const actions = (api) => {
215217
let [data, config] = unpackArgs(args)
216218
const path = getURL(data)
217219
const actionId = actionSequence(context)
220+
const apiConf = { method: 'patch', url: path, data: normToJsonapi(data) }
221+
merge(apiConf, config)
218222
context.commit('setStatus', { id: actionId, status: STATUS_LOAD })
219-
let action = api
220-
.patch(path, normToJsonapi(data), config)
223+
let action = api(apiConf)
221224
.then((results) => {
222225
// If the server handed back data, store it
223226
if (results.status === 200) {
@@ -249,10 +252,11 @@ const actions = (api) => {
249252
delete: (context, args) => {
250253
const [data, config] = unpackArgs(args)
251254
const path = getURL(data)
255+
const apiConf = { method: 'delete', url: path }
256+
merge(apiConf, config)
252257
const actionId = actionSequence(context)
253258
context.commit('setStatus', { id: actionId, status: STATUS_LOAD })
254-
let action = api
255-
.delete(path, config)
259+
let action = api(apiConf)
256260
.then((results) => {
257261
processIncludedRecords(context, results)
258262

tests/unit/actions/delete.spec.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,15 @@ describe('delete', function() {
3737
{ params: params },
3838
])
3939

40-
expect(this.mockApi.history.delete[0].params).to.equal(params)
40+
expect(this.mockApi.history.delete[0].params).to.deep.equal(params)
41+
})
42+
43+
it('should allow the endpoint url to be overridden in config', async function() {
44+
this.mockApi.onAny().reply(200, { data: jsonWidget1 })
45+
const url = '/fish/1'
46+
47+
await jsonapiModule.actions.delete(stubContext, [normWidget1, { url: url }])
48+
expect(this.mockApi.history.delete[0].url).to.equal(url)
4149
})
4250

4351
it('should delete a record from the store', async function() {

tests/unit/actions/get.spec.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,15 @@ describe('get', function() {
8181
normWidget1,
8282
{ params: params },
8383
])
84+
expect(this.mockApi.history.get[0].params).to.deep.equal(params)
85+
})
86+
87+
it('should allow the endpoint url to be overridden in config', async function() {
88+
this.mockApi.onAny().reply(200, { data: jsonWidget1 })
89+
const url = '/fish/1'
8490

85-
expect(this.mockApi.history.get[0].params).to.equal(params)
91+
await jsonapiModule.actions.get(stubContext, [normWidget1, { url: url }])
92+
expect(this.mockApi.history.get[0].url).to.equal(url)
8693
})
8794

8895
it('should add record(s) in the store', async function() {

tests/unit/actions/patch.spec.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ describe('patch', function() {
5252
{ params: params },
5353
])
5454

55-
expect(this.mockApi.history.patch[0].params).to.equal(params)
55+
expect(this.mockApi.history.patch[0].params).to.deep.equal(params)
56+
})
57+
58+
it('should allow the endpoint url to be overridden in config', async function() {
59+
this.mockApi.onAny().reply(200, { data: jsonWidget1 })
60+
const url = '/fish/1'
61+
62+
await jsonapiModule.actions.patch(stubContext, [normWidget1, { url: url }])
63+
expect(this.mockApi.history.patch[0].url).to.equal(url)
5664
})
5765

5866
it('should delete then add record(s) in the store (from server response)', async function() {

tests/unit/actions/post.spec.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@ describe('post', function() {
3838
{ params: params },
3939
])
4040

41-
expect(this.mockApi.history.post[0].params).to.equal(params)
41+
expect(this.mockApi.history.post[0].params).to.deep.equal(params)
42+
})
43+
44+
it('should allow the endpoint url to be overridden in config', async function() {
45+
this.mockApi.onAny().reply(200, { data: jsonWidget1 })
46+
const url = '/fish/1'
47+
48+
await jsonapiModule.actions.post(stubContext, [normWidget1, { url: url }])
49+
expect(this.mockApi.history.post[0].url).to.equal(url)
4250
})
4351

4452
it('should add record(s) to the store', async function() {

0 commit comments

Comments
 (0)