Skip to content

Commit

Permalink
fix(kitsu-core): ensure linkedRelationships does not overwrite meta o…
Browse files Browse the repository at this point in the history
…bject references (#783)

* Fix: Pick the meta object from actual resource rather from cached resource if it is different

* Fix code review comments

* Address code review comments

* docs: fix jsdoc syntax to use Object instead of object

* test(deepEqual): add additional tests to cover uncovered branches

* test(linkRelationships): add additional test to cover isDeepEqual path

Co-authored-by: wopian <wopian@wopian.me>
  • Loading branch information
mountainfirefly and wopian committed Oct 30, 2022
1 parent d34e871 commit 97ba151
Show file tree
Hide file tree
Showing 4 changed files with 480 additions and 1 deletion.
59 changes: 59 additions & 0 deletions packages/kitsu-core/src/deepEqual/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Compare two objects equality
*
* @param {Object} left Object to compare against the right object
* @param {Object} right Object to compare against the left object
* @returns {boolean} Whether the objects are equal
* @example <caption>Deep equality check</caption>
* isDeepEqual({
* firstName: 'John',
* lastName: 'Doe',
* age: 35
* },{
* firstName: 'John',
* lastName: 'Doe',
* age: 35
* }) // true
*/
export const isDeepEqual = (left, right) => {
if (!left || !right) {
return left === right
}

const leftKeys = Object.keys(left)
const rightKeys = Object.keys(right)

if (leftKeys.length !== rightKeys.length) return false

for (const key of leftKeys) {
const leftValue = left[key]
const rightValue = right[key]

const isObjects = isObject(leftValue) && isObject(rightValue)

if ((isObjects && !isDeepEqual(leftValue, rightValue)) ||
(!isObjects && leftValue !== rightValue)
) {
return false
}
}

return true
}

/**
* Check for Object
*
* @param {Object} object Value to check if it is an object
* @returns {boolean} Whether the value is an object
* @private
* @example <caption>Check for object</caption>
* isObject({
* firstName: 'John',
* lastName: 'Doe',
* age: 35
* }) // true
*/
const isObject = (object) => {
return object != null && typeof object === 'object'
}
63 changes: 63 additions & 0 deletions packages/kitsu-core/src/deepEqual/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { isDeepEqual } from './'

describe('kitsu-core', () => {
describe('isDeepEqual', () => {
const people = {
one: {
firstName: 'John',
lastName: 'Doe',
age: 35
},
two: {
firstName: 'John',
lastName: 'Doe',
age: 35
},
three: {
firstName: 'Akash',
lastName: 'Thakur',
age: 35
},
four: {
firstName: 'Jane',
lastName: 'Doe'
},
five: {
address: {
street: '123 Main St'
}
},
six: {
address: {
street: '123 Main St'
}
},
seven: {
address: {
street: '456 Main St'
}
}
}

it('checks indentical objects are equal', () => {
expect.assertions(1)
expect(isDeepEqual(people.one, people.two)).toBe(true)
})

it('checks different objects are not equal', () => {
expect.assertions(1)
expect(isDeepEqual(people.one, people.three)).toBe(false)
})

it('checks objects have the same number of keys', () => {
expect.assertions(1)
expect(isDeepEqual(people.one, people.four)).toBe(false)
})

it('checks nested objects are equal', () => {
expect.assertions(2)
expect(isDeepEqual(people.five, people.six)).toBe(true)
expect(isDeepEqual(people.five, people.seven)).toBe(false)
})
})
})
19 changes: 18 additions & 1 deletion packages/kitsu-core/src/linkRelationships/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { deattribute } from '../deattribute'
import { filterIncludes } from '../filterIncludes'
import { isDeepEqual } from '../deepEqual'

/**
* Core function to link relationships to included data
Expand Down Expand Up @@ -67,7 +68,23 @@ function linkObject (data, included, key, previouslyLinked, relationshipCache) {
data[key] = {}
const resource = data.relationships[key].data
const cache = previouslyLinked[`${resource.type}#${resource.id}`]
data[key].data = cache || link(resource, included, previouslyLinked, relationshipCache)

if (cache) {
let resourceCache = null
// Comparing for cache entity meta and resource entity meta object.
if (!isDeepEqual(cache.meta, resource.meta)) {
resourceCache = {
...cache,
meta: resource.meta
}
} else {
resourceCache = cache
}

data[key].data = resourceCache
} else {
data[key].data = link(resource, included, previouslyLinked, relationshipCache)
}

const cacheKey = `${data.type}#${data.id}#${key}`
const relationships = relationshipCache[cacheKey] || data.relationships[key]
Expand Down

0 comments on commit 97ba151

Please sign in to comment.