From 474d91bfbe1671855ff026cba5e9e71d5a6b1e62 Mon Sep 17 00:00:00 2001 From: pedep Date: Wed, 26 Apr 2023 16:30:43 +0200 Subject: [PATCH] fix(kitsu-core): Merge meta keys to preserve data (#853) 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. Co-authored-by: James Harris --- .../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