UNPKG

5.14 kBJavaScriptView Raw
1'use strict';
2var isPlainObject = require('lodash/isPlainObject');
3var isFunction = require('lodash/isFunction');
4var _find = require('lodash/find');
5var _extend = require('lodash/extend');
6var _transform = require('lodash/transform');
7var _merge = require('lodash/merge');
8var _get = require('lodash/get');
9var _set = require('lodash/set');
10var Inflector = require('./inflector');
11
12module.exports = function (jsonapi, data, opts) {
13 var alreadyIncluded = {};
14
15 function isComplexType(obj) {
16 return Array.isArray(obj) || isPlainObject(obj);
17 }
18
19 function getValueForRelationship(relationshipData, included) {
20 if (opts && relationshipData && opts[relationshipData.type]) {
21 var valueForRelationshipFct = opts[relationshipData.type]
22 .valueForRelationship;
23
24 return valueForRelationshipFct(relationshipData, included);
25 } else {
26 return included;
27 }
28 }
29
30 function findIncluded(relationshipData, relationshipName, from) {
31 return new Promise(function (resolve) {
32 if (!jsonapi.included || !relationshipData) { return resolve(null); }
33
34 var included = _find(jsonapi.included, {
35 id: relationshipData.id,
36 type: relationshipData.type
37 });
38
39 var path = [
40 from.type,
41 from.id,
42 relationshipName,
43 relationshipData.type,
44 relationshipData.id,
45 ]
46
47 // Check if the include is already processed (prevent circular
48 // references).
49 if (_get(alreadyIncluded, path, false)) {
50 return resolve(null);
51 } else {
52 _merge(alreadyIncluded, _set({}, path, true));
53 }
54
55 if (included) {
56 return Promise
57 .all([extractAttributes(included), extractRelationships(included)])
58 .then(function (results) {
59 var attributes = results[0];
60 var relationships = results[1];
61 resolve(_extend(attributes, relationships));
62 });
63 } else {
64 return resolve(null);
65 }
66 });
67 }
68
69 function keyForAttribute(attribute) {
70 if (isPlainObject(attribute)) {
71 return _transform(attribute, function (result, value, key) {
72 if (isComplexType(value)) {
73 result[keyForAttribute(key)] = keyForAttribute(value);
74 } else {
75 result[keyForAttribute(key)] = value;
76 }
77 });
78 } else if (Array.isArray(attribute)) {
79 return attribute.map(function (attr) {
80 if (isComplexType(attr)) {
81 return keyForAttribute(attr);
82 } else {
83 return attr;
84 }
85 });
86 } else {
87 if (isFunction(opts.keyForAttribute)) {
88 return opts.keyForAttribute(attribute);
89 } else {
90 return Inflector.caserize(attribute, opts);
91 }
92 }
93 }
94
95 function extractAttributes(from) {
96 var dest = keyForAttribute(from.attributes || {});
97 if ('id' in from) { dest[opts.id || 'id'] = from.id; }
98
99 if (opts.typeAsAttribute) {
100 if ('type' in from) { dest.type = from.type; }
101 }
102 if ('meta' in from) { dest.meta = keyForAttribute(from.meta || {}) }
103
104 return dest;
105 }
106
107 function extractRelationships(from) {
108 if (!from.relationships) { return; }
109
110 var dest = {};
111
112 return Promise
113 .all(Object.keys(from.relationships).map(function (key) {
114 var relationship = from.relationships[key];
115
116 if (relationship.data === null) {
117 dest[keyForAttribute(key)] = null;
118 } else if (Array.isArray(relationship.data)) {
119 return Promise
120 .all(relationship.data.map(function (relationshipData) {
121 return extractIncludes(relationshipData, key, from);
122 }))
123 .then(function (includes) {
124 if (includes) { dest[keyForAttribute(key)] = includes; }
125 });
126 } else {
127 return extractIncludes(relationship.data, key, from)
128 .then(function (include) {
129 if (include) { dest[keyForAttribute(key)] = include; }
130 });
131 }
132 }))
133 .then(function() {
134 return dest;
135 });
136 }
137
138 function extractIncludes(relationshipData, relationshipName, from) {
139 return findIncluded(relationshipData, relationshipName, from)
140 .then(function (included) {
141 var valueForRelationship = getValueForRelationship(relationshipData,
142 included);
143
144 if (valueForRelationship && isFunction(valueForRelationship.then)) {
145 return valueForRelationship.then(function (value) {
146 return value;
147 });
148 } else {
149 return valueForRelationship;
150 }
151 });
152 }
153
154 this.perform = function () {
155 return Promise
156 .all([extractAttributes(data), extractRelationships(data)])
157 .then(function (results) {
158 var attributes = results[0];
159 var relationships = results[1];
160 var record = _extend(attributes, relationships);
161
162 // Links
163 if (jsonapi.links) {
164 record.links = jsonapi.links;
165 }
166
167
168 // If option is present, transform record
169 if (opts && opts.transform) {
170 record = opts.transform(record);
171 }
172
173 return record;
174 });
175 };
176};