Skip to content

Instantly share code, notes, and snippets.

@slauzinho
Created July 18, 2022 15:22
Show Gist options
  • Save slauzinho/86e388cec11cb6aebc0dfb352b011c7a to your computer and use it in GitHub Desktop.
Save slauzinho/86e388cec11cb6aebc0dfb352b011c7a to your computer and use it in GitHub Desktop.
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
THIS IS A COPY OF https://github.com/yury-dymov/json-api-normalizer/blob/master/src/normalize.js
WE JUST UPDATED THE LODASH DEPENDANCIES TO REDUCE BUNDLE SIZE
*/
import camelCase from 'lodash.camelcase';
import isNull from 'lodash.isnull';
import keys from 'lodash.keys';
import merge from 'lodash.merge';
function wrap(json: any) {
if (Array.isArray(json)) {
return json;
}
return [json];
}
function isDate(attributeValue: any) {
return Object.prototype.toString.call(attributeValue) === '[object Date]';
}
function camelizeNestedKeys(attributeValue: any): any {
if (attributeValue === null || typeof attributeValue !== 'object' || isDate(attributeValue)) {
return attributeValue;
}
if (Array.isArray(attributeValue)) {
return attributeValue.map(camelizeNestedKeys);
}
const copy: any = {};
keys(attributeValue).forEach(k => {
copy[camelCase(k)] = camelizeNestedKeys(attributeValue[k]);
});
return copy;
}
function extractRelationships(relationships: any, { camelizeKeys, camelizeTypeValues }: any): any {
const ret: any = {};
keys(relationships).forEach(key => {
const relationship = relationships[key];
const name = camelizeKeys ? camelCase(key) : key;
ret[name] = {};
if (typeof relationship.data !== 'undefined') {
if (Array.isArray(relationship.data)) {
ret[name].data = relationship.data.map((e: any) => ({
id: e.id,
type: camelizeTypeValues ? camelCase(e.type) : e.type,
}));
} else if (!isNull(relationship.data)) {
ret[name].data = {
id: relationship.data.id,
type: camelizeTypeValues ? camelCase(relationship.data.type) : relationship.data.type,
};
} else {
ret[name].data = relationship.data;
}
}
if (relationship.links) {
ret[name].links = camelizeKeys ? camelizeNestedKeys(relationship.links) : relationship.links;
}
if (relationship.meta) {
ret[name].meta = camelizeKeys ? camelizeNestedKeys(relationship.meta) : relationship.meta;
}
});
return ret;
}
function processMeta(metaObject: any, { camelizeKeys }: any) {
if (camelizeKeys) {
const meta: any = {};
keys(metaObject).forEach(key => {
meta[camelCase(key)] = camelizeNestedKeys(metaObject[key]);
});
return meta;
}
return metaObject;
}
function extractEntities(json: any, { camelizeKeys, camelizeTypeValues }: any) {
const ret: any = {};
wrap(json).forEach(elem => {
const type = camelizeKeys ? camelCase(elem.type) : elem.type;
ret[type] = ret[type] || {};
ret[type][elem.id] = ret[type][elem.id] || {
id: elem.id,
};
ret[type][elem.id].type = camelizeTypeValues ? camelCase(elem.type) : elem.type;
if (camelizeKeys) {
ret[type][elem.id].attributes = {};
keys(elem.attributes).forEach(key => {
ret[type][elem.id].attributes[camelCase(key)] = camelizeNestedKeys(elem.attributes[key]);
});
} else {
ret[type][elem.id].attributes = elem.attributes;
}
if (elem.links) {
ret[type][elem.id].links = {};
keys(elem.links).forEach(key => {
const newKey = camelizeKeys ? camelCase(key) : key;
ret[type][elem.id].links[newKey] = elem.links[key];
});
}
if (elem.relationships) {
ret[type][elem.id].relationships = extractRelationships(elem.relationships, {
camelizeKeys,
camelizeTypeValues,
});
}
if (elem.meta) {
ret[type][elem.id].meta = processMeta(elem.meta, { camelizeKeys });
}
});
return ret;
}
function doFilterEndpoint(endpoint: any) {
return endpoint.replace(/\?.*$/, '');
}
function extractMetaData(
json: any,
endpoint: any,
{ camelizeKeys, camelizeTypeValues, filterEndpoint }: any
) {
const ret: any = {};
ret.meta = {};
let metaObject: any;
if (!filterEndpoint) {
const filteredEndpoint = doFilterEndpoint(endpoint);
ret.meta[filteredEndpoint] = {};
ret.meta[filteredEndpoint][endpoint.slice(filteredEndpoint.length)] = {};
metaObject = ret.meta[filteredEndpoint][endpoint.slice(filteredEndpoint.length)];
} else {
ret.meta[endpoint] = {};
metaObject = ret.meta[endpoint];
}
metaObject.data = {};
if (json.data) {
const meta: any = [];
wrap(json.data).forEach(object => {
const pObject: any = {
id: object.id,
type: camelizeTypeValues ? camelCase(object.type) : object.type,
};
if (object.relationships) {
pObject.relationships = extractRelationships(object.relationships, {
camelizeKeys,
camelizeTypeValues,
});
}
meta.push(pObject);
});
metaObject.data = meta;
}
if (json.links) {
metaObject.links = json.links;
ret.meta[doFilterEndpoint(endpoint)].links = json.links;
}
if (json.meta) {
metaObject.meta = processMeta(json.meta, { camelizeKeys });
}
return ret;
}
export default function normalize<T>(
json: any,
{ filterEndpoint = true, camelizeKeys = true, camelizeTypeValues = false, endpoint }: any = {}
): T {
const ret = {};
if (json.data) {
merge(ret, extractEntities(json.data, { camelizeKeys, camelizeTypeValues }));
}
if (json.included) {
merge(ret, extractEntities(json.included, { camelizeKeys, camelizeTypeValues }));
}
if (endpoint) {
const endpointKey = filterEndpoint ? doFilterEndpoint(endpoint) : endpoint;
merge(
ret,
extractMetaData(json, endpointKey, {
camelizeKeys,
camelizeTypeValues,
filterEndpoint,
})
);
}
return ret as T;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment