Skip to content

Commit 605ba54

Browse files
Resolves #263 added a means to disable attaching relationship metadata on unresolved includes
1 parent 0ffa3df commit 605ba54

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ let { data, errors, meta, links } = jsonApi.findAll('post', {include: 'comments'
128128
// => data.comment will be populated with any comments included by your API
129129
```
130130

131+
By default, Devour will include relationship metadata (id, type, and other metadata) in each response. It may be handy to exclude this metadata from the response when the relationship is not resolved by the backend (not listed in the "includes" Json:api response). For this, make sure the `attachRelationshipDataOnUnresolvedIncludes` client flag is set to `false`, (default value is `true`).
132+
131133
### Flexibility
132134

133135
Devour uses a fully middleware based approach. This allows you to easily manipulate any part of the request and response cycle by injecting your own middleware. In fact, it's entirely possible to fully remove our default middleware and write your own. Moving forward we hope to see adapters for different server implementations. If you'd like to take a closer look at the middleware layer, please checkout:

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ class JsonApi {
7070
auth: {},
7171
bearer: null,
7272
trailingSlash: { collection: false, resource: false },
73-
disableErrorsForMissingResourceDefinitions: false
73+
disableErrorsForMissingResourceDefinitions: false,
74+
attachRelationshipDataOnUnresolvedIncludes: true
7475
}
7576

7677
const deprecatedConstructors = (args) => {
@@ -95,6 +96,7 @@ class JsonApi {
9596
this.apiUrl = options.apiUrl
9697
this.bearer = options.bearer
9798
this.disableErrorsForMissingResourceDefinitions = options.disableErrorsForMissingResourceDefinitions
99+
this.attachRelationshipDataOnUnresolvedIncludes = options.attachRelationshipDataOnUnresolvedIncludes
98100
this.models = {}
99101
this.deserialize = deserialize
100102
this.serialize = serialize

src/middleware/json-api/_deserialize.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,13 @@ function attachHasOneFor (model, attribute, item, included, key) {
120120
return resource.call(this, relatedItems[0], included, true)
121121
}
122122

123-
const relationshipData = _get(item.relationships, [key, 'data'], false)
124-
if (relationshipData) {
125-
return relationshipData
123+
if (this.attachRelationshipDataOnUnresolvedIncludes) {
124+
const relationshipData = _get(item.relationships, [key, 'data'], false)
125+
if (relationshipData) {
126+
return relationshipData
127+
}
126128
}
129+
127130
return null
128131
}
129132

@@ -137,9 +140,11 @@ function attachHasManyFor (model, attribute, item, included, key) {
137140
return collection.call(this, relatedItems, included, true)
138141
}
139142

140-
const relationshipData = _get(item.relationships, [key, 'data'], false)
141-
if (relationshipData) {
142-
return relationshipData
143+
if (this.attachRelationshipDataOnUnresolvedIncludes) {
144+
const relationshipData = _get(item.relationships, [key, 'data'], false)
145+
if (relationshipData) {
146+
return relationshipData
147+
}
143148
}
144149

145150
return []

test/api/deserialize-test.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,4 +426,81 @@ describe('deserialize', () => {
426426
expect(product.tags[1].id).to.eql('6')
427427
expect(product.tags[1].type).to.eql('tags')
428428
})
429+
430+
it('should not include relationship data on unresolved hasMany relationships if attachRelationshipDataOnUnresolvedIncludes flag is set to false', () => {
431+
jsonApi = new JsonApi({
432+
apiUrl: 'http://myapi.com',
433+
attachRelationshipDataOnUnresolvedIncludes: false
434+
})
435+
jsonApi.define('product', {
436+
title: '',
437+
tags: {
438+
jsonApi: 'hasMany',
439+
type: 'tag'
440+
}
441+
})
442+
jsonApi.define('tag', {
443+
name: ''
444+
})
445+
const mockResponse = {
446+
data: {
447+
id: '1',
448+
type: 'products',
449+
attributes: {
450+
title: 'hello'
451+
},
452+
relationships: {
453+
tags: {
454+
data: [
455+
{ id: '5', type: 'tags' },
456+
{ id: '6', type: 'tags' }
457+
]
458+
}
459+
}
460+
}
461+
}
462+
const product = deserialize.resource.call(jsonApi, mockResponse.data, mockResponse.included)
463+
expect(product.id).to.eql('1')
464+
expect(product.type).to.eql('products')
465+
expect(product.title).to.eql('hello')
466+
expect(product.tags).to.be.an('array')
467+
expect(product.tags).to.be.empty()
468+
})
469+
470+
it('should not include relationship data on unresolved hasOne relationships if attachRelationshipDataOnUnresolvedIncludes flag is set to false', () => {
471+
jsonApi = new JsonApi({
472+
apiUrl: 'http://myapi.com',
473+
attachRelationshipDataOnUnresolvedIncludes: false
474+
})
475+
jsonApi.define('product', {
476+
title: '',
477+
tag: {
478+
jsonApi: 'hasOne',
479+
type: 'tag'
480+
}
481+
})
482+
jsonApi.define('tag', {
483+
name: ''
484+
})
485+
const mockResponse = {
486+
data: {
487+
id: '1',
488+
type: 'products',
489+
attributes: {
490+
title: 'hello'
491+
},
492+
relationships: {
493+
tag: {
494+
data: { id: '5', type: 'tag' }
495+
}
496+
}
497+
}
498+
}
499+
const product = deserialize.resource.call(jsonApi, mockResponse.data, mockResponse.included)
500+
expect(product.id).to.eql('1')
501+
expect(product.type).to.eql('products')
502+
expect(product.title).to.eql('hello')
503+
expect(product.tag).to.not.be.an('array')
504+
expect(product.tag).to.eql(null)
505+
})
429506
})

0 commit comments

Comments
 (0)