-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathindex.js
136 lines (122 loc) · 4.39 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* Converts a string to camelCase.
* @param {string} str - The input string to convert.
* @returns {string} The camelCased string.
*/
const camelCase = (str) => {
return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase());
};
/**
* Recursively transforms keys of an object or array using the provided function.
* @param {Object|Array} obj - The object or array to transform.
* @param {Function|null} transformFunc - The function to transform keys with.
* @returns {Object|Array} The transformed object or array.
*/
const transformKeys = (obj, transformFunc) => {
if (Array.isArray(obj)) {
return obj.map(item => transformKeys(item, transformFunc));
} else if (obj && typeof obj === 'object') {
return Object.keys(obj).reduce((acc, key) => {
const transformedKey = transformFunc ? transformFunc(key) : key;
acc[transformedKey] = transformKeys(obj[key], transformFunc);
return acc;
}, {});
}
return obj;
};
/**
* Finds an included resource by type and id.
* @param {Array} included - The array of included resources.
* @param {string} type - The type of the resource to find.
* @param {string} id - The id of the resource to find.
* @returns {Object|undefined} The found resource or undefined.
*/
const findIncluded = (included, type, id) => {
return included.find(item => item.type === type && item.id === id);
};
/**
* Deserializes a single resource, including its relationships.
* @param {Object} resource - The resource to deserialize.
* @param {Array} included - The array of included resources.
* @param {Function|null} transformFunc - The function to transform keys with.
* @returns {Object|null} The deserialized resource.
*/
const deserializeResource = (resource, included, transformFunc) => {
if (!resource) return null;
const { id, type, attributes = {}, relationships = {}, links, meta } = resource;
const deserialized = {
id,
type,
...transformKeys(attributes, transformFunc),
links,
meta,
};
Object.keys(relationships).forEach(key => {
const relationship = relationships[key].data;
if (Array.isArray(relationship)) {
deserialized[transformFunc ? transformFunc(key) : key] = relationship.map(rel => {
const includedResource = findIncluded(included, rel.type, rel.id);
return {
...deserializeResource(includedResource, included, transformFunc),
id: rel.id,
type: rel.type,
links: rel.links,
meta: rel.meta,
};
});
} else {
const includedResource = findIncluded(included, relationship.type, relationship.id);
deserialized[transformFunc ? transformFunc(key) : key] = {
...deserializeResource(includedResource, included, transformFunc),
id: relationship.id,
type: relationship.type,
links: relationship.links,
meta: relationship.meta,
};
}
});
return deserialized;
};
/**
* Recursively removes undefined properties from an object or array.
* @param {Object|Array} obj - The object or array to process.
* @returns {Object|Array} The processed object or array.
*/
const removeUndefinedProperties = (obj) => {
if (Array.isArray(obj)) {
return obj.map(removeUndefinedProperties);
} else if (obj !== null && typeof obj === 'object') {
return Object.fromEntries(
Object.entries(obj)
.filter(([_, v]) => v !== undefined)
.map(([k, v]) => [k, removeUndefinedProperties(v)])
);
}
return obj;
};
/**
* Deserializes a JSON:API response.
* @param {Object} response - The JSON:API response to deserialize.
* @param {Object} [options={}] - Options for deserialization.
* @param {string} [options.transformKeys] - Key transformation option ('camelCase' or undefined).
* @returns {Object} The deserialized response.
*/
const deserialize = (response, options = {}) => {
if (!response.data) return response;
const transformFunc = options.transformKeys === 'camelCase' ? camelCase : null;
const { included, ...rest } = response;
let data;
if (Array.isArray(response.data)) {
data = {
...transformKeys(rest, transformFunc),
data: response.data.map(resource => deserializeResource(resource, included, transformFunc)),
};
} else {
data = {
...transformKeys(rest, transformFunc),
data: deserializeResource(response.data, included, transformFunc),
};
}
return removeUndefinedProperties(data);
};
module.exports = { deserialize };