This repository has been archived by the owner on Jun 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
139 lines (117 loc) · 3.45 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
137
138
139
/* Copyright (c) 2019 Tony Gonçalves. All rights reserved.
* See LICENSE.txt for licensing details */
module.exports = function denormalize(obj) {
if (!obj || !obj.data || obj.errors) {
return obj;
}
var denormalizedData;
var cache = {}; // Cache of already denormalized objects in "included"
var included = extractIncluded(obj);
if (obj.data.length == null) {
// There's a single top-level object
denormalizedData = denormalizeObj(obj.data, included, cache);
} else {
// We are dealing with a list of objects
denormalizedData = obj.data.map(function(o) {
return denormalizeObj(o, included, cache);
});
}
var returnObj = { data: denormalizedData };
if (obj.meta) {
returnObj.meta = obj.meta;
}
if (obj.links) {
returnObj.links = obj.links;
}
return returnObj;
};
/**
* Extracts all of the objects in the "included" key of the JSON:API response
* into an object with a shape like:
* {
* type1: { obj1Id: obj1, obj2Id: obj2, ... },
* type2: { obj1Id: obj1, obj2Id: obj2, ... },
* ...
* }
*
* This will allow us to directly get at each object we need when building the
* denormalized response, instead of iterating through the "included" array
* multiple times.
*/
function extractIncluded(obj) {
var included = {};
if (obj.included == null) {
return included;
}
for (var i = 0; i < obj.included.length; i++) {
var o = obj.included[i];
if (included[o.type] == null) {
included[o.type] = {};
}
included[o.type][o.id] = o;
}
return included;
}
function denormalizeObj(obj, included, cache) {
var type = obj.type;
var cachedType = cache[type];
if (cachedType != null) {
var cachedObject = cachedType[obj.id];
if (cachedObject != null) {
return cachedObject;
}
}
var k;
var newObj = {};
// Copy top-level keys to new obj (except attributes and relationships)
for (k in obj) {
if (k !== 'relationships' && k !== 'attributes') {
newObj[k] = obj[k];
}
}
// Copy attributes to top-level of new object
// (this does nothing if there are no attributes)
assign(newObj, obj.attributes);
// Copy related objects to top-level of new object
assign(newObj, denormalizeRelationships(obj.relationships, included, cache));
// Cache the denormalized object and return it
if (cache[type] == null) {
cache[type] = {};
}
cache[type][newObj.id] = newObj;
return newObj;
}
function denormalizeRelationships(relationships, included, cache) {
var newRelationships = {};
for (var k in relationships) {
var rel = relationships[k].data;
if (rel == null) {
// relationship is null
newRelationships[k] = null;
} else if (rel.length == null) {
// relationship is a single object
var obj = isIncluded(rel, included) ? included[rel.type][rel.id] : rel;
newRelationships[k] = denormalizeObj(obj, included, cache);
} else {
// relationship is an array of objects
newRelationships[k] = rel.map(function(o) {
o = isIncluded(o, included) ? included[o.type][o.id] : o;
return denormalizeObj(o, included, cache);
});
}
}
return newRelationships;
}
function isIncluded(obj, included) {
return included[obj.type] != null && included[obj.type][obj.id] != null;
}
/**
* Replacement for Object.assign when running in ES5 environments
*/
var assign =
Object.assign ||
function(target, source) {
for (var k in source) {
target[k] = source[k];
}
};