Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 9d2ab4e

Browse files
committed
feat(core): dereference elements in valueOf
1 parent 7c2152d commit 9d2ab4e

File tree

7 files changed

+1177
-33
lines changed

7 files changed

+1177
-33
lines changed

packages/api-elements/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# API Elements (JavaScript) CHANGELOG
22

3+
## 0.3.2 (2020-09-15)
4+
5+
### Enhancements
6+
7+
- Dereference elements in valueOf
8+
39
## 0.3.1 (2020-09-07)
410

511
### Bug Fixes

packages/api-elements/lib/define-value-of.js

+20-15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const {
2121
isRef,
2222
isObjectWithUndefinedValues,
2323
trivialValue,
24+
getStructureMembers,
2425
} = require('./utils');
2526

2627
/**
@@ -35,7 +36,7 @@ function mapValue(e, f, elements) {
3536
return undefined;
3637
}
3738

38-
if (e.content && !isEmptyArray(e) && !isObjectWithUndefinedValues(e)) {
39+
if (e.content && !isEmptyArray(e, elements) && !isObjectWithUndefinedValues(e, elements)) {
3940
const result = f(e, elements, 'content');
4041

4142
if (result !== undefined) {
@@ -59,8 +60,8 @@ function mapValue(e, f, elements) {
5960
}
6061
}
6162

62-
// reconsider content for array element (prefer sample/default first)
63-
if (isNonEmptyArray(e)) {
63+
// reconsider content for array and object element (prefer sample/default first)
64+
if (isNonEmptyArray(e, elements) && isObject(e, elements)) {
6465
const result = f(e, elements, 'content');
6566

6667
if (result !== undefined) {
@@ -88,16 +89,17 @@ function mapValue(e, f, elements) {
8889
}
8990

9091
const result = elements[e.element];
91-
if (result) {
92+
if (result !== undefined) {
9293
const inheritedElements = R.filter(el => !el.id.equals(e.element), elements);
9394
return mapValue(result, f, inheritedElements);
9495
}
9596
}
9697

97-
if (isEnum(e)) {
98-
const enums = e.enumerations;
99-
if (enums && enums.content && enums.content[0]) {
100-
const result = f(enums.content[0], elements, 'generated');
98+
if (isEnum(e, elements)) {
99+
const content = getStructureMembers(e, elements);
100+
101+
if (content && content[0]) {
102+
const result = f(content[0], elements, 'generated');
101103
if (result !== undefined) {
102104
return result;
103105
}
@@ -112,7 +114,7 @@ function mapValue(e, f, elements) {
112114
}
113115
}
114116

115-
if (isArray(e) && e.isEmpty) {
117+
if ((isArray(e, elements) && e.isEmpty) || isObject(e, elements)) {
116118
return f(e, elements, 'generated');
117119
}
118120

@@ -130,24 +132,26 @@ function reduceValue(e, elements) {
130132
return mapValue(e, e => e.content, elements);
131133
}
132134

133-
if (isPrimitive(e)) {
135+
if (isPrimitive(e, elements)) {
134136
return e.content;
135137
}
136138

137139
if (e instanceof NullElement) {
138140
return null;
139141
}
140142

141-
if (isEnum(e)) {
143+
if (isEnum(e, elements)) {
142144
return mapValue(e.content, reduceValue, elements);
143145
}
144146

145-
if (isObject(e)) {
147+
if (isObject(e, elements)) {
146148
let result = {};
147149

148150
const isFixedElement = isFixed(e);
149151

150-
e.content.some((item) => {
152+
const content = getStructureMembers(e, elements);
153+
154+
content.some((item) => {
151155
const isSkippable = isOptional(item) || (!isFixedElement && !isRequired(item));
152156

153157
const key = mapValue(item.key, reduceValue, elements);
@@ -177,8 +181,9 @@ function reduceValue(e, elements) {
177181
return result;
178182
}
179183

180-
if (isArray(e)) {
181-
const result = e.map(item => mapValue(item, reduceValue, elements));
184+
if (isArray(e, elements)) {
185+
const content = getStructureMembers(e, elements);
186+
const result = content.map(item => mapValue(item, reduceValue, elements));
182187

183188
if (!isFixed(e)) {
184189
return result.filter(item => item !== undefined);

packages/api-elements/lib/utils.js

+164-14
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
/* eslint-disable no-underscore-dangle */
2-
32
const R = require('ramda');
3+
44
const {
5-
ArrayElement,
6-
ObjectElement,
75
StringElement,
86
BooleanElement,
97
NumberElement,
108
NullElement,
119
} = require('minim');
1210

13-
const EnumElement = require('./elements/Enum');
11+
/**
12+
* Get element attribute
13+
* @param {element} e - element - element
14+
* @param {string} attribute
15+
* @return {element}
16+
*/
17+
const getAttribute = (e, attribute) => e._attributes && e.attributes.get(attribute);
1418

1519
/**
1620
* Check if element has a typeAttribute
@@ -19,7 +23,7 @@ const EnumElement = require('./elements/Enum');
1923
* @return {boolean}
2024
*/
2125
function hasTypeAttribute(e, attribute) {
22-
const typeAttributes = e._attributes && e.attributes.get('typeAttributes');
26+
const typeAttributes = getAttribute(e, 'typeAttributes');
2327
if (typeAttributes) {
2428
return typeAttributes.includes(attribute);
2529
}
@@ -55,33 +59,85 @@ const isNullable = e => hasTypeAttribute(e, 'nullable');
5559
*/
5660
const isOptional = e => hasTypeAttribute(e, 'optional');
5761

62+
const baseTypes = new Set([
63+
'boolean',
64+
'string',
65+
'number',
66+
'array',
67+
'object',
68+
'enum',
69+
'null',
70+
'member',
71+
'select',
72+
'option',
73+
'extend',
74+
'ref',
75+
'link',
76+
]);
77+
/**
78+
* Check if element is one of the base types
79+
* @param {element} e - element
80+
* @return {boolean}
81+
*/
82+
const isBaseType = e => baseTypes.has(e && e.element);
83+
84+
/**
85+
* Get the element type - prefer a base type, if found
86+
* @param {element} e - element
87+
* @param {object=} elements - object map of elements to look for inherited type
88+
* @return {string}
89+
*/
90+
const getType = (e, elements) => {
91+
if (e === undefined) {
92+
return undefined;
93+
}
94+
if (isBaseType(e)) {
95+
return e.element;
96+
}
97+
98+
const inheritedType = e.element && elements && elements[e.element];
99+
if (inheritedType !== undefined) {
100+
return getType(inheritedType, elements);
101+
}
102+
103+
return e && e.element;
104+
};
105+
106+
const primitives = new Set(['string', 'number', 'boolean', 'null']);
58107
/**
59108
* Check if the element is of a primitive type
60109
* @param {element} e - element
110+
* @param {object=} elements - object map of elements to look for inherited type
61111
* @return {boolean}
62112
*/
63-
const isPrimitive = e => (e instanceof StringElement) || (e instanceof NumberElement) || (e instanceof BooleanElement);
113+
const isPrimitive = (e, elements) => {
114+
const type = getType(e, elements);
115+
return primitives.has(String(type));
116+
};
64117

65118
/**
66119
* Check if the element type is Enum
67120
* @param {element} e - element
121+
* @param {object=} elements - object map of elements to look for inherited type
68122
* @return {boolean}
69123
*/
70-
const isEnum = e => e instanceof EnumElement;
124+
const isEnum = (e, elements) => getType(e, elements) === 'enum';
71125

72126
/**
73127
* Check if the element type is Array
74128
* @param {element} e - element
129+
* @param {object=} elements - object map of elements to look for inherited type
75130
* @return {boolean}
76131
*/
77-
const isArray = e => e instanceof ArrayElement;
132+
const isArray = (e, elements) => getType(e, elements) === 'array';
78133

79134
/**
80135
* Check if the element type is Object
81136
* @param {element} e - element
137+
* @param {object=} elements - object map of elements to look for inherited type
82138
* @return {boolean}
83139
*/
84-
const isObject = e => e instanceof ObjectElement;
140+
const isObject = (e, elements) => getType(e, elements) === 'object';
85141

86142
/**
87143
* Get the element default
@@ -149,23 +205,27 @@ const hasNoValue = R.complement(hasValue);
149205
/**
150206
* Check if the element is of a primitive type and has no value (content/sample/default)
151207
* @param {element} e - element
208+
* @param {object=} elements - object map of elements to look for inherited type
152209
* @return {boolean}
153210
*/
154-
const isNoValuePrimitive = R.both(isPrimitive, hasNoValue);
211+
const isNoValuePrimitive = (e, elements) => isPrimitive(e, elements) && hasNoValue(e);
155212

156213
/**
157214
* Check if the element type is array and is not empty
158215
* @param {element} e - element
216+
* @param {object=} elements - object map of elements to look for inherited type
159217
* @return {boolean}
160218
*/
161-
const isNonEmptyArray = e => isArray(e) && e.content && !e.isEmpty;
219+
const isNonEmptyArray = (e, elements) => isArray(e, elements) && e.content !== undefined && !e.isEmpty;
162220

163221
/**
164222
* Check if the element type is array and has only primitive elements with no value
165223
* @param {element} e - element
224+
* @param {object=} elements - object map of elements to look for inherited type
166225
* @return {boolean}
167226
*/
168-
const isEmptyArray = e => isArray(e) && e.content.every(isNoValuePrimitive);
227+
const isEmptyArray = (e, elements) => isArray(e, elements)
228+
&& (e.content === undefined || e.content.every(member => isNoValuePrimitive(member, elements)));
169229

170230
/**
171231
* Check if the element type is 'ref'
@@ -177,10 +237,12 @@ const isRef = e => e && e.element === 'ref';
177237
/**
178238
* Check if the element type is object and has all property values undefined
179239
* @param {element} e - element
240+
* @param {object=} elements - object map of elements to look for inherited type
180241
* @return {boolean}
181242
*/
182-
const isObjectWithUndefinedValues = e => isObject(e)
183-
&& e.content.every(prop => prop.value === undefined || prop.value.content === undefined);
243+
const isObjectWithUndefinedValues = (e, elements) => isObject(e, elements) && e.content.every(
244+
prop => prop.value === undefined || prop.value.content === undefined
245+
);
184246

185247
/**
186248
* Get a trivial value, to fill the unset, according to the element type
@@ -207,6 +269,93 @@ function trivialValue(e) {
207269
return undefined;
208270
}
209271

272+
/**
273+
* Get a key for the element member
274+
* This is used to identify each member on the Map, allowing overrides in Objects
275+
* @param {element} e - element
276+
* @param {object=} elements - object map of elements to look for inherited type
277+
* @return {(string|number|object|null)} - Map key
278+
*/
279+
function getMemberKey(e, elements) {
280+
if (isPrimitive(e, elements)) {
281+
// return unique identifier
282+
return Math.random();
283+
}
284+
285+
const key = e && e.content && e.content.key && e.content.key.toValue();
286+
const content = e && e.content;
287+
const type = e.element;
288+
289+
return key || content || type;
290+
}
291+
292+
/**
293+
* Get an Array with the element members
294+
* @param {element} e - element
295+
* @param {object=} elements - object map of elements to look for inherited type
296+
* @return {element[]} - element members
297+
*/
298+
function getMembers(e, elements) {
299+
if (e === undefined) {
300+
return [];
301+
}
302+
303+
if (isEnum(e, elements)) {
304+
const enumerations = getAttribute(e, 'enumerations');
305+
if (enumerations && enumerations.content !== undefined) {
306+
return enumerations.content;
307+
}
308+
}
309+
310+
if (Array.isArray(e.content)) {
311+
return e.content;
312+
}
313+
314+
return [e];
315+
}
316+
317+
/**
318+
* Get a Map with all the element members, including references
319+
* @param {element} e - element
320+
* @param {object=} elements - object map of elements to look for inherited type
321+
* @return {Map<element>} - element members
322+
*/
323+
function getAllMembersMap(e, elements) {
324+
if (e === undefined) {
325+
return new Map();
326+
}
327+
328+
const typeElement = elements && elements[e.element];
329+
const typeMembersMap = getAllMembersMap(typeElement, elements);
330+
const ownMembers = getMembers(e, elements);
331+
const ownMembersMap = new Map();
332+
333+
ownMembers.forEach((member) => {
334+
if (isRef(member)) {
335+
const refElement = elements && elements[member.content];
336+
const refMembersMap = getAllMembersMap(refElement, elements);
337+
refMembersMap.forEach((refMember) => {
338+
ownMembersMap.set(getMemberKey(refMember, elements), refMember);
339+
});
340+
} else {
341+
ownMembersMap.set(getMemberKey(member, elements), member);
342+
}
343+
});
344+
return new Map([...typeMembersMap, ...ownMembersMap]);
345+
}
346+
347+
/**
348+
* Get an Array with all the element members, including references
349+
* @param {element} e - element
350+
* @param {object=} elements - object map of elements to look for inherited type
351+
* @return {element[]} - element members
352+
*/
353+
function getStructureMembers(e, elements) {
354+
const membersMap = getAllMembersMap(e, elements);
355+
356+
return Array.from(membersMap.values());
357+
}
358+
210359
module.exports = {
211360
isFixed,
212361
isRequired,
@@ -223,4 +372,5 @@ module.exports = {
223372
isRef,
224373
isObjectWithUndefinedValues,
225374
trivialValue,
375+
getStructureMembers,
226376
};

packages/api-elements/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "api-elements",
3-
"version": "0.3.1",
3+
"version": "0.3.2",
44
"description": "API Elements JavaScript",
55
"author": "Apiary.io <[email protected]>",
66
"license": "MIT",

0 commit comments

Comments
 (0)