ts-japi
Version:
A highly-modular (typescript-friendly)-framework agnostic library for serializing data to the JSON:API specification
174 lines • 7.26 kB
JavaScript
;
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