Skip to content

Commit ddcc17a

Browse files
committed
fix(kitsu-core): preverve serialised relationship attributes
Resolves regression introduced in 9.0.x with change in relationship behaviour. Closes #418
1 parent 395f88b commit ddcc17a

2 files changed

Lines changed: 101 additions & 23 deletions

File tree

packages/kitsu-core/src/serialise/index.js

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,64 @@ function isValid (isArray, type, payload, method) {
3434
}
3535
}
3636

37+
/**
38+
* Serialises a relational data object to JSON:API format
39+
*
40+
* @param {Object} node Existing relation object
41+
* @param {Object} relations Relation object being built
42+
* @param {string} key Name of the relationship
43+
* @returns {Object} Serialised relationship
44+
* @private
45+
*/
46+
function serialiseRelationOne (node, relations, key) {
47+
relations[key] = { data: {} }
48+
for (const prop of Object.keys(node[key].data)) {
49+
const propNode = node[key].data[prop]
50+
let propRelations = relations[key].data
51+
// Pull everything but id and type into data.attributes
52+
if (prop && ![ 'id', 'type' ].includes(prop)) {
53+
propRelations = serialiseAttr(propNode, prop, propRelations)
54+
} else propRelations[prop] = propNode
55+
}
56+
return relations
57+
}
58+
59+
/**
60+
* Serialises a relational data array to JSON:API format
61+
*
62+
* @param {Object} node Existing relation object
63+
* @param {Object} relations Relation object being built
64+
* @param {string} key Name of the relationship
65+
* @returns {Object} Serialised relationship
66+
* @private
67+
*/
68+
function serialiseRelationMany (node, relations, key) {
69+
relations[key] = { data: [] }
70+
for (const prop of node[key].data) {
71+
relations[key].data.push(prop)
72+
}
73+
return relations
74+
}
75+
76+
/**
77+
* Serialises a relational object to JSON:API format
78+
*
79+
* @param {Object} node Relation object
80+
* @returns {Object} Serialised relationship
81+
* @private
82+
*/
83+
function serialiseRelation (node) {
84+
// Create a new object to handle collisions with attributes.attributes
85+
let relations = {}
86+
for (const key in node) {
87+
const isToMany = Array.isArray(node[key].data)
88+
relations = isToMany
89+
? serialiseRelationMany(node, relations, key)
90+
: serialiseRelationOne(node, relations, key)
91+
}
92+
return relations
93+
}
94+
3795
/**
3896
* Serialises an object to JSON:API format
3997
*
@@ -50,6 +108,7 @@ function serialiseObject (node, nodeType, key, data) {
50108
data.relationships[key] = {
51109
data: Object.assign(node)
52110
}
111+
data.relationships = serialiseRelation(data.relationships)
53112
return data
54113
}
55114

@@ -65,13 +124,15 @@ function serialiseObject (node, nodeType, key, data) {
65124
function serialiseArray (node, nodeType, key, data) {
66125
if (!data.relationships) data.relationships = {}
67126
data.relationships[key] = {
68-
data: node.map(({ id, type }) => {
127+
data: node.map(({ id, type, ...attributes }) => {
69128
return {
70129
id,
71-
type: type || nodeType
130+
type: type || nodeType,
131+
attributes: Object.keys(attributes).length ? attributes : undefined
72132
}
73133
})
74134
}
135+
data.relationships = serialiseRelation(data.relationships)
75136
return data
76137
}
77138

@@ -137,24 +198,21 @@ function serialiseRootObject (type, payload, method, options) {
137198
isValid(false, type, payload, method)
138199
type = options.pluralTypes(options.camelCaseTypes(type))
139200
let data = { type }
140-
141201
if (payload?.id) data.id = String(payload.id)
142-
143202
for (const key in payload) {
144203
const node = payload[key]
145204
const nodeType = options.pluralTypes(options.camelCaseTypes(key))
146205
// 1. Skip null nodes, 2. Only grab objects, 3. Filter to only serialise relationable objects
147206
if (node !== null && node?.constructor === Object && hasID(node)) {
148-
data = serialiseObject(node, nodeType, key, data, method)
207+
data = serialiseObject(node, nodeType, key, data)
149208
// 1. Skip null nodes, 2. Only grab arrays, 3. Filter to only serialise relationable arrays
150209
} else if (node !== null && Array.isArray(node) && (node.length > 0 && hasID(node[0]))) {
151-
data = serialiseArray(node, nodeType, key, data, method)
210+
data = serialiseArray(node, nodeType, key, data)
152211
// 1. Don't place id/key inside attributes object
153212
} else if (key !== 'id' && key !== 'type') {
154213
data = serialiseAttr(node, key, data)
155214
}
156215
}
157-
158216
return { data }
159217
}
160218

packages/kitsu-core/src/serialise/index.spec.js

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ describe('kitsu-core', () => {
113113
user: [
114114
{
115115
id: '2',
116-
type: 'users'
116+
type: 'users',
117+
content: 'yuzu'
117118
},
118119
{
119120
id: '3'
@@ -133,7 +134,10 @@ describe('kitsu-core', () => {
133134
data: [
134135
{
135136
id: '2',
136-
type: 'users'
137+
type: 'users',
138+
attributes: {
139+
content: 'yuzu'
140+
}
137141
},
138142
{
139143
id: '3',
@@ -300,19 +304,25 @@ describe('kitsu-core', () => {
300304
it('serialises type objects into relationships', () => {
301305
expect.assertions(1)
302306
const input = serialise('resourceModel', {
303-
object: {
307+
myRelationship: {
304308
id: '1',
305-
type: 'relationshipModel'
309+
type: 'relationshipModel',
310+
content: 'Hello',
311+
attributes: 'Keep me'
306312
}
307313
})
308314
expect(input).toEqual({
309315
data: {
310316
type: 'resourceModel',
311317
relationships: {
312-
object: {
318+
myRelationship: {
313319
data: {
314320
id: '1',
315-
type: 'relationshipModel'
321+
type: 'relationshipModel',
322+
attributes: {
323+
content: 'Hello',
324+
attributes: 'Keep me'
325+
}
316326
}
317327
}
318328
}
@@ -323,19 +333,23 @@ describe('kitsu-core', () => {
323333
it('serialises type objects into relationships inside arrays', () => {
324334
expect.assertions(1)
325335
const input = serialise('resourceModel', [ {
326-
object: {
336+
myRelationship: {
327337
id: '1',
328-
type: 'relationshipModel'
338+
type: 'relationshipModel',
339+
content: 'Hello'
329340
}
330341
} ])
331342
expect(input).toEqual({
332343
data: [ {
333344
type: 'resourceModel',
334345
relationships: {
335-
object: {
346+
myRelationship: {
336347
data: {
337348
id: '1',
338-
type: 'relationshipModel'
349+
type: 'relationshipModel',
350+
attributes: {
351+
content: 'Hello'
352+
}
339353
}
340354
}
341355
}
@@ -366,20 +380,26 @@ describe('kitsu-core', () => {
366380

367381
it('serialises type arrays into relationships', () => {
368382
expect.assertions(1)
369-
const input = serialise('resourceModel', {
370-
array: [ {
383+
const input = serialise('resourceModels', {
384+
arrayRelation: [ {
371385
id: '1',
372-
type: 'relationshipModel'
386+
type: 'arrayRelations',
387+
content: 'Hey',
388+
attributes: 'Keep me'
373389
} ]
374390
})
375391
expect(input).toEqual({
376392
data: {
377-
type: 'resourceModel',
393+
type: 'resourceModels',
378394
relationships: {
379-
array: {
395+
arrayRelation: {
380396
data: [ {
381397
id: '1',
382-
type: 'relationshipModel'
398+
type: 'arrayRelations',
399+
attributes: {
400+
content: 'Hey',
401+
attributes: 'Keep me'
402+
}
383403
} ]
384404
}
385405
}

0 commit comments

Comments
 (0)