This repository was archived by the owner on Nov 8, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathparseComponentsObject.js
202 lines (173 loc) · 7.01 KB
/
parseComponentsObject.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
const R = require('ramda');
const {
isObject, isAnnotation, hasKey, isExtension, getValue,
} = require('../../predicates');
const {
createWarning,
createUnsupportedMemberWarning,
createInvalidMemberWarning,
} = require('../annotations');
const parseObject = require('../parseObject');
const pipeParseResult = require('../../pipeParseResult');
const parseSchemaObject = require('./parseSchemaObject');
const parseParameterObject = require('./parseParameterObject');
const parseResponseObject = require('./parseResponseObject');
const parseRequestBodyObject = require('./parseRequestBodyObject');
const parseHeaderObject = require('./parseHeaderObject');
const parseExampleObject = require('./parseExampleObject');
const parseSecuritySchemeObject = require('./parseSecuritySchemeObject');
const name = 'Components Object';
const unsupportedKeys = ['links', 'callbacks'];
const isUnsupportedKey = R.anyPass(R.map(hasKey, unsupportedKeys));
const valueIsObject = R.compose(isObject, getValue);
/**
* Is the given parse result empty (i.e, contains no elements other than annotations)
*
* @param parseResult {ParseResult}
* @returns boolean
* @private
*/
const isParseResultEmpty = parseResult => R.reject(isAnnotation, parseResult).isEmpty;
/**
* Parse a component member
*
* This function takes another parser that is capable of parsing the specific
* component type (schema parser, parameter parser etc) and parses a
* component using the given parser. It will return a parse result of member.
*
* In the cases that the given member cannot be parsed, it will result in
* returning a member element to represent the key without a value.
*
* @param context
* @param parser {function}
* @param member {Member} - Member element to represent the component,
* member key contains the reusable component name, value represents
* the reusable component
*
* @returns ParseResult
* @private
*/
const parseComponentMember = R.curry((context, parser, member) => {
// Create a Member Element with `member.key` as the key
const Member = R.constructN(2, context.namespace.elements.Member)(member.key);
const parseResult = R.map(
R.unless(isAnnotation, Member),
parser(context, member.value)
);
if (isParseResultEmpty(parseResult)) {
// parse result does not contain a member, that's because parsing a
// component has failed. We want to store the member without value in
// this case so that we can correctly know if a component with the name
// existed during dereferencing.
parseResult.unshift(Member(undefined));
}
return parseResult;
});
/**
* Parse Components Object
*
* @param namespace {Namespace}
* @param element {Element}
* @returns ParseResult
*
* @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#componentsObject
* @private
*/
function parseComponentsObject(context, element) {
const { namespace } = context;
// Schema Object supports recursive (and circular) references and thus we
// must know about all of the schema IDs upfront. Below we are putting
// in the unparsed schemas so we can keep the dereferencing logic simple,
// these are used during parsing the schema components and later on the
// components in our context is replaced by the final parsed result.
// eslint-disable-next-line no-param-reassign
context.state.components = new namespace.elements.Object();
if (isObject(element) && element.get('schemas') && isObject(element.get('schemas'))) {
// Take schemas and convert to object with members for each key (discarding value)
// We don't want the value making it into final parse results under any circumstance,
// for example if the parse errors out and we leave bad state
const schemas = new namespace.elements.Object(
element.get('schemas').map((value, key) => new namespace.elements.Member(key))
);
context.state.components.set('schemas', schemas);
}
const createMemberValueNotObjectWarning = member => createWarning(namespace,
`'${name}' '${member.key.toValue()}' is not an object`, member.value);
const validateIsObject = R.unless(valueIsObject, createMemberValueNotObjectWarning);
/**
* Parses a member representing a component object (such as an object
* representing the parameter components)
*
* @param parser {function}
* @param member {Member}
*
* @returns ParseResult<ObjectElement>
* @private
*/
const parseComponentObjectMember = (parser) => {
const parseMember = parseComponentMember(context, parser);
return member => pipeParseResult(context.namespace,
validateIsObject,
R.compose(parseObject(context, name, parseMember), getValue),
(object) => {
const contextMember = context.state.components.getMember(member.key.toValue());
if (contextMember) {
contextMember.value = object;
} else {
context.state.components.push(new namespace.elements.Member(member.key, object));
}
return object;
})(member);
};
const setDataStructureId = (dataStructure, key) => {
if (dataStructure) {
// eslint-disable-next-line no-param-reassign
dataStructure.content.id = key.clone();
}
};
const parseSchemas = pipeParseResult(namespace,
parseComponentObjectMember(parseSchemaObject),
(object) => {
object.forEach(setDataStructureId);
return object;
});
const parseSecuritySchemes = pipeParseResult(namespace,
parseComponentObjectMember(parseSecuritySchemeObject),
(object) => {
const array = new namespace.elements.Array([]);
object.forEach((value, key) => {
if (value) {
if (value instanceof namespace.elements.AuthScheme) {
// eslint-disable-next-line no-param-reassign
value.id = key.clone();
array.push(value);
return;
}
// append oauth2 flow names
value.forEach((flow) => {
// eslint-disable-next-line no-param-reassign
flow.id = `${key.toValue()} ${flow.grantTypeValue}`;
array.push(flow);
});
}
});
return array;
});
const parseMember = R.cond([
[hasKey('schemas'), parseSchemas],
[hasKey('parameters'), parseComponentObjectMember(parseParameterObject)],
[hasKey('responses'), parseComponentObjectMember(parseResponseObject)],
[hasKey('requestBodies'), parseComponentObjectMember(parseRequestBodyObject)],
[hasKey('examples'), parseComponentObjectMember(parseExampleObject)],
[hasKey('headers'), parseComponentObjectMember(parseHeaderObject)],
[hasKey('securitySchemes'), parseSecuritySchemes],
[isUnsupportedKey, createUnsupportedMemberWarning(namespace, name)],
// FIXME Support exposing extensions into parse result
[isExtension, () => new namespace.elements.ParseResult()],
// Return a warning for additional properties
[R.T, createInvalidMemberWarning(namespace, name)],
]);
const order = ['schemas', 'headers'];
return parseObject(context, name, parseMember, [], order)(element);
}
module.exports = R.curry(parseComponentsObject);