UNPKG

ts-japi

Version:

A highly-modular (typescript-friendly)-framework agnostic library for serializing data to the JSON:API specification

174 lines 7.26 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Helpers = exports.normalizeRelators = exports.recurseRelators = void 0; const relator_1 = __importDefault(require("../classes/relator")); async function recurseRelatorsDepth(data, relators, depth, keys, relatorDataCache) { const included = []; let curRelatorDataCache = relatorDataCache || new Map(); // Required to support backwards compatability where the first dataCache may // not be passed in. All subsequent iterations will contain a dataCache if (!relatorDataCache && depth > 0) { for (const name in relators) { const cache = curRelatorDataCache.get(relators[name]) || []; curRelatorDataCache.set(relators[name], cache); for (const datum of data) { const relatedData = await relators[name].getRelatedData(datum); if (relatedData !== null) { cache.push(...(Array.isArray(relatedData) ? relatedData : [relatedData])); } } } } while (depth-- > 0 && curRelatorDataCache.size > 0) { const newRelatorDataCache = new Map(); for (const [relator, cache] of curRelatorDataCache) { for (let i = 0; i < cache.length; i++) { const resource = await relator.getRelatedResource(cache[i], undefined, undefined, newRelatorDataCache); const key = resource.getKey(); if (!keys.includes(key)) { keys.push(key); included.push(resource); } } } curRelatorDataCache = newRelatorDataCache; } return included; } async function recurseRelators(data, relators, include, keys, relatorDataCache) { if (include === undefined || typeof include === 'number') { return recurseRelatorsDepth(data, relators, include ?? 0, keys, relatorDataCache); } const included = []; let curRelatorDataCache = relatorDataCache || new Map(); // Required to support backwards compatability where the first dataCache may // not be passed in. All subsequent iterations will contain a dataCache if (!relatorDataCache && include.length > 0) { for (const name in relators) { const cache = curRelatorDataCache.get(relators[name]) || []; curRelatorDataCache.set(relators[name], cache); for (const datum of data) { const relatedData = await relators[name].getRelatedData(datum); if (relatedData !== null) { cache.push(...(Array.isArray(relatedData) ? relatedData : [relatedData])); } } } } const maxDepth = Math.max(...include.map((i) => i.split('.').length)); let currentDepth = 0; while (currentDepth < maxDepth) { const newRelatorDataCache = new Map(); const includeFields = include .map((i) => i.split('.')) .filter((i) => i[currentDepth]) .map((i) => ({ field: i[currentDepth], hasMore: i.length > currentDepth + 1 })) .reduce((acc, i) => { const match = acc.find((j) => j.field === i.field); if (match) { match.hasMore = match.hasMore || i.hasMore; } else { acc.push(i); } return acc; }, []); for (const [relator, cache] of curRelatorDataCache) { const shouldBuildRelatedCache = (!includeFields || includeFields?.filter((i) => i.field === relator.relatedName)?.some((i) => i.hasMore)) ?? false; for (let i = 0; i < cache.length; i++) { // Include if, // - includeFields !== undefined // - includeFields has entry where field = relatedName if (!includeFields || includeFields.map((i) => i.field).includes(relator.relatedName)) { const key = `${relator.serializer.collectionName}:${cache[i].id}`; if (!keys.includes(key)) { // const key = resource.getKey(); const resource = await relator.getRelatedResource(cache[i], undefined, undefined, // Only build the cache for the next iteration if needed. shouldBuildRelatedCache ? newRelatorDataCache : undefined); keys.push(key); included.push(resource); } } } } currentDepth++; curRelatorDataCache = newRelatorDataCache; } return included; } exports.recurseRelators = recurseRelators; function normalizeRelators(relators) { const normalizedRelators = {}; if (relators) { if (relators instanceof relator_1.default) { normalizedRelators[relators.relatedName] = relators; return normalizedRelators; } else if (relators instanceof Array) { for (const relator of relators) { normalizedRelators[relator.relatedName] = relator; } return normalizedRelators; } else { return relators; } } return undefined; } exports.normalizeRelators = normalizeRelators; class Helpers { projectAttributes; relators; constructor(options) { // Relators this.relators = normalizeRelators(options.relators); // Projection if (options.projection === undefined) { this.projectAttributes = () => undefined; } else if (options.projection === null) { this.projectAttributes = (data) => { const attributes = Object.assign({}, data); delete attributes[options.idKey]; return attributes; }; } else { const projection = options.projection; const type = Object.values(projection)[0]; if (type === 0) { this.projectAttributes = (data) => { const keys = Object.keys(data); const attributes = {}; for (let i = 0, len = keys.length; i < len; i++) { if (!(keys[i] in projection)) { attributes[keys[i]] = data[keys[i]]; } } delete attributes[options.idKey]; return attributes; }; } else { const keys = Object.keys(projection); this.projectAttributes = (data) => { const attributes = {}; for (let i = 0, len = keys.length; i < len; i++) { attributes[keys[i]] = data[keys[i]]; } delete attributes[options.idKey]; return attributes; }; } } } } exports.Helpers = Helpers; //# sourceMappingURL=serializer.utils.js.map