From 07f814984b6285876009af8ec0328c1ac953ebc5 Mon Sep 17 00:00:00 2001 From: Vincent Plannthin Date: Wed, 26 Apr 2023 15:27:42 +0200 Subject: [PATCH] fix(kitsu-core): Merge meta keys to preserve data Previously only the meta from the resource identifier object was preserved, while the resource object meta was discarded, resulting in some cases where meta would be set to undefined. The meta keys are now merged instead of picked between, preserving the largest amount of data possible. --- .../kitsu-core/src/deserialise/index.spec.js | 265 ++++++++++++++++++ .../kitsu-core/src/linkRelationships/index.js | 7 +- 2 files changed, 269 insertions(+), 3 deletions(-) diff --git a/packages/kitsu-core/src/deserialise/index.spec.js b/packages/kitsu-core/src/deserialise/index.spec.js index 51986f2e..dd0f14d3 100644 --- a/packages/kitsu-core/src/deserialise/index.spec.js +++ b/packages/kitsu-core/src/deserialise/index.spec.js @@ -815,6 +815,271 @@ describe('kitsu-core', () => { expect(input).toEqual(output) }) + it('merges meta from relationship object and resource object', () => { + expect.assertions(1) + + const input = JSON.parse(stringify(deserialise({ + data: [ + { + id: 1, + type: 'anime', + relationships: { + primary_category: { + meta: { qux: true }, + data: { id: 1, type: 'category', meta: { foo: true } } + }, + categories: { + meta: { qux: true }, + data: [ { id: 1, type: 'category', meta: { foo: true } } ] + } + } + }, + { + id: 2, + type: 'anime', + relationships: { + primary_category: { + meta: { quux: true }, + data: { id: 1, type: 'category', meta: { bar: true } } + }, + categories: { + meta: { quux: true }, + data: [ { id: 1, type: 'category', meta: { bar: true } } ] + } + } + }, + { + id: 3, + type: 'anime', + relationships: { + primary_category: { + meta: { corge: true }, + data: { id: 1, type: 'category' } + }, + categories: { + meta: { corge: true }, + data: [ { id: 1, type: 'category' } ] + } + } + }, + { + id: 4, + type: 'anime', + relationships: { + primary_category: { + meta: { grault: true }, + data: { id: 1, type: 'category', meta: { baz: false } } + }, + categories: { + meta: { grault: true }, + data: [ { id: 1, type: 'category', meta: { baz: false } } ] + } + } + } + ], + included: [ { id: 1, type: 'category', attributes: { title: 'foobar' }, meta: { baz: true } } ] + }))) + + const output = { + data: [ + { + primary_category: { + meta: { qux: true }, + data: { + id: 1, + type: 'category', + title: 'foobar', + meta: { + baz: true, + foo: true + } + } + }, + categories: { + meta: { qux: true }, + data: [ + { + id: 1, + meta: { + baz: true, + foo: true + }, + title: 'foobar', + type: 'category' + } + ] + }, + id: 1, + type: 'anime' + }, + { + primary_category: { + meta: { quux: true }, + data: { + id: 1, + type: 'category', + title: 'foobar', + meta: { + baz: true, + bar: true + } + } + }, + categories: { + meta: { quux: true }, + data: [ + { + id: 1, + meta: { + baz: true, + bar: true + }, + title: 'foobar', + type: 'category' + } + ] + }, + id: 2, + type: 'anime' + }, + { + primary_category: { + meta: { corge: true }, + data: { + id: 1, + type: 'category', + title: 'foobar', + meta: { + baz: true + } + } + }, + categories: { + meta: { corge: true }, + data: [ + { + id: 1, + title: 'foobar', + type: 'category', + meta: { + baz: true + } + } + ] + }, + id: 3, + type: 'anime' + }, + { + primary_category: { + meta: { grault: true }, + data: { + id: 1, + type: 'category', + title: 'foobar', + meta: { + baz: false + } + } + }, + categories: { + meta: { grault: true }, + data: [ + { + id: 1, + title: 'foobar', + type: 'category', + meta: { + baz: false + } + } + ] + }, + id: 4, + type: 'anime' + } + ] + } + + expect(input).toEqual(output) + }) + + it('preserves meta from included resource object', () => { + expect.assertions(1) + + const input = JSON.parse(stringify(deserialise({ + data: { + id: '1', + type: 'users', + relationships: { + followers: { + links: { + self: 'https://kitsu.example/users/1/relationships/followers', + related: 'https://kitsu.example/users/1/followers' + }, + data: [ { + type: 'follows', + id: '1' + } ] + } + } + }, + included: [ + { + id: '1', + type: 'follows', + attributes: { a: 123 }, + links: { + self: 'https://kitsu.example/follows/1' + }, + meta: { value: 1 }, + relationships: { + follower: { + links: { + self: 'https://kitsu.io/follows/1/relationships/follower', + related: 'https://kitsu.io/follows/1/follower' + } + } + } + } + ] + }))) + + const output = { + data: { + id: '1', + type: 'users', + followers: { + links: { + self: 'https://kitsu.example/users/1/relationships/followers', + related: 'https://kitsu.example/users/1/followers' + }, + data: [ + { + id: '1', + type: 'follows', + a: 123, + links: { + self: 'https://kitsu.example/follows/1' + }, + meta: { + value: 1 + }, + follower: { + links: { + self: 'https://kitsu.io/follows/1/relationships/follower', + related: 'https://kitsu.io/follows/1/follower' + } + } + } + ] + } + } + } + + expect(input).toEqual(output) + }) + it('Deserializes nested single circular resource', () => { expect.assertions(1) diff --git a/packages/kitsu-core/src/linkRelationships/index.js b/packages/kitsu-core/src/linkRelationships/index.js index a085ed9c..81063575 100644 --- a/packages/kitsu-core/src/linkRelationships/index.js +++ b/packages/kitsu-core/src/linkRelationships/index.js @@ -21,7 +21,6 @@ function link ({ id, type, meta }, included, previouslyLinked, relationshipCache if (filtered.relationships) { linkRelationships(filtered, included, previouslyLinked, relationshipCache) } - if (meta) filtered.meta = meta return deattribute(filtered) } @@ -47,7 +46,7 @@ function linkArray (data, included, key, previouslyLinked, relationshipCache) { for (const resource of data.relationships[key].data) { const cache = previouslyLinked[`${resource.type}#${resource.id}`] let relationship = cache || link(resource, included, previouslyLinked, relationshipCache) - if (resource.meta !== relationship.meta) relationship = { ...relationship, meta: resource.meta } + if (resource.meta || relationship.meta) { relationship = { ...relationship, meta: { ...relationship.meta, ...resource.meta } } } data[key].data.push(relationship) } @@ -75,7 +74,7 @@ function linkObject (data, included, key, previouslyLinked, relationshipCache) { if (!isDeepEqual(cache.meta, resource.meta)) { resourceCache = { ...cache, - meta: resource.meta + meta: { ...cache.meta, ...resource.meta } } } else { resourceCache = cache @@ -86,6 +85,8 @@ function linkObject (data, included, key, previouslyLinked, relationshipCache) { data[key].data = link(resource, included, previouslyLinked, relationshipCache) } + if (resource.meta || data[key].data.meta) { data[key].data = { ...data[key].data, meta: { ...data[key].data.meta, ...resource.meta } } } + const cacheKey = `${data.type}#${data.id}#${key}` const relationships = relationshipCache[cacheKey] || data.relationships[key] if (!relationshipCache[cacheKey]) relationshipCache[cacheKey] = relationships