UNPKG

kitsu-core

Version:

Simple, lightweight & framework agnostic JSON:API (de)serialsation components

126 lines (121 loc) 4.1 kB
'use strict'; var deattribute = require('./deattribute.js'); var filterIncludes = require('./filterIncludes.js'); require('./error.js'); 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; }; const isObject = object => { return object != null && typeof object === 'object'; }; function link({ id, type }, included, previouslyLinked, relationshipCache) { const filtered = filterIncludes.filterIncludes(included, { id, type }); previouslyLinked[`${type}#${id}`] = filtered; if (filtered.relationships) { linkRelationships(filtered, included, previouslyLinked, relationshipCache); } return deattribute.deattribute(filtered); } function linkArray(data, included, key, previouslyLinked, relationshipCache) { data[key] = {}; if (data.relationships[key].links) data[key].links = data.relationships[key].links; if (data.relationships[key].meta) data[key].meta = data.relationships[key].meta; data[key].data = []; 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: { ...relationship.meta, ...resource.meta } }; } data[key].data.push(relationship); } delete data.relationships[key]; } function linkObject(data, included, key, previouslyLinked, relationshipCache) { data[key] = {}; const resource = data.relationships[key].data; const cache = previouslyLinked[`${resource.type}#${resource.id}`]; if (cache) { let resourceCache = null; if (!isDeepEqual(cache.meta, resource.meta)) { resourceCache = { ...cache, meta: { ...cache.meta, ...resource.meta } }; } else { resourceCache = cache; } data[key].data = resourceCache; } else { 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; if (relationships?.links) data[key].links = relationships.links; if (relationships?.meta) data[key].meta = relationships.meta; delete data.relationships[key]; } function linkAttr(data, key) { data[key] = {}; if (data.relationships[key].links) data[key].links = data.relationships[key].links; if (data.relationships[key].meta) data[key].meta = data.relationships[key].meta; delete data.relationships[key]; } function linkRelationships(data, included = [], previouslyLinked = {}, relationshipCache = {}) { const { relationships } = data; for (const key in relationships) { if (Array.isArray(relationships[key]?.data)) { linkArray(data, included, key, previouslyLinked, relationshipCache); } else if (relationships[key].data) { linkObject(data, included, key, previouslyLinked, relationshipCache); } else { linkAttr(data, key); } } if (Object.keys(relationships || []).length === 0 && typeof relationships === 'object' && !Array.isArray(relationships) && relationships !== null) { delete data.relationships; } return data; } exports.linkRelationships = linkRelationships;