source: node_modules/@swagger-api/apidom-reference/cjs/dereference/strategies/openapi-2/visitor.cjs

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 17.6 KB
Line 
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
4var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
5exports.__esModule = true;
6exports.default = void 0;
7var _stampit = _interopRequireDefault(require("stampit"));
8var _ramda = require("ramda");
9var _apidomCore = require("@swagger-api/apidom-core");
10var _apidomError = require("@swagger-api/apidom-error");
11var _apidomJsonPointer = require("@swagger-api/apidom-json-pointer");
12var _apidomNsOpenapi = require("@swagger-api/apidom-ns-openapi-2");
13var _MaximumDereferenceDepthError = _interopRequireDefault(require("../../../errors/MaximumDereferenceDepthError.cjs"));
14var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
15var _util = require("../../util.cjs");
16var url = _interopRequireWildcard(require("../../../util/url.cjs"));
17var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
18var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
19// @ts-ignore
20const visitAsync = _apidomCore.visit[Symbol.for('nodejs.util.promisify.custom')];
21
22// initialize element identity manager
23const identityManager = (0, _apidomCore.IdentityManager)();
24
25/**
26 * Predicate for detecting if element was created by merging referencing
27 * element with particular element identity with a referenced element.
28 */
29const wasReferencedBy = referencingElement => element => element.meta.hasKey('ref-referencing-element-id') && element.meta.get('ref-referencing-element-id').equals((0, _apidomCore.toValue)(identityManager.identify(referencingElement)));
30const OpenApi2DereferenceVisitor = (0, _stampit.default)({
31 props: {
32 indirections: [],
33 namespace: null,
34 reference: null,
35 options: null,
36 ancestors: null
37 },
38 init({
39 indirections = [],
40 reference,
41 namespace,
42 options,
43 ancestors = new _util.AncestorLineage()
44 }) {
45 this.indirections = indirections;
46 this.namespace = namespace;
47 this.reference = reference;
48 this.options = options;
49 this.ancestors = new _util.AncestorLineage(...ancestors);
50 },
51 methods: {
52 toBaseURI(uri) {
53 return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
54 },
55 async toReference(uri) {
56 // detect maximum depth of resolution
57 if (this.reference.depth >= this.options.resolve.maxDepth) {
58 throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);
59 }
60 const baseURI = this.toBaseURI(uri);
61 const {
62 refSet
63 } = this.reference;
64
65 // we've already processed this Reference in past
66 if (refSet.has(baseURI)) {
67 return refSet.find((0, _ramda.propEq)(baseURI, 'uri'));
68 }
69 const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
70 ...this.options,
71 parse: {
72 ...this.options.parse,
73 mediaType: 'text/plain'
74 }
75 });
76
77 // register new Reference with ReferenceSet
78 const reference = (0, _Reference.default)({
79 uri: baseURI,
80 value: parseResult,
81 depth: this.reference.depth + 1
82 });
83 refSet.add(reference);
84 return reference;
85 },
86 toAncestorLineage(ancestors) {
87 /**
88 * Compute full ancestors lineage.
89 * Ancestors are flatten to unwrap all Element instances.
90 */
91 const directAncestors = new Set(ancestors.filter(_apidomCore.isElement));
92 const ancestorsLineage = new _util.AncestorLineage(...this.ancestors, directAncestors);
93 return [ancestorsLineage, directAncestors];
94 },
95 async ReferenceElement(referencingElement, key, parent, path, ancestors) {
96 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
97
98 // detect possible cycle in traversal and avoid it
99 if (ancestorsLineage.includesCycle(referencingElement)) {
100 return false;
101 }
102 const retrievalURI = this.toBaseURI((0, _apidomCore.toValue)(referencingElement.$ref));
103
104 // ignore resolving external Reference Objects
105 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
106 // skip traversing this reference element and all it's child elements
107 return false;
108 }
109 const reference = await this.toReference((0, _apidomCore.toValue)(referencingElement.$ref));
110 const $refBaseURI = url.resolve(retrievalURI, (0, _apidomCore.toValue)(referencingElement.$ref));
111 this.indirections.push(referencingElement);
112 const jsonPointer = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
113
114 // possibly non-semantic fragment
115 let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
116
117 // applying semantics to a fragment
118 if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
119 const referencedElementType = (0, _apidomCore.toValue)(referencingElement.meta.get('referenced-element'));
120 if ((0, _apidomNsOpenapi.isReferenceLikeElement)(referencedElement)) {
121 // handling indirect references
122 referencedElement = _apidomNsOpenapi.ReferenceElement.refract(referencedElement);
123 referencedElement.setMetaProperty('referenced-element', referencedElementType);
124 } else {
125 // handling direct references
126 const ElementClass = this.namespace.getElementClass(referencedElementType);
127 referencedElement = ElementClass.refract(referencedElement);
128 }
129 }
130
131 // detect direct or circular reference
132 if (this.indirections.includes(referencedElement)) {
133 throw new _apidomError.ApiDOMError('Recursive Reference Object detected');
134 }
135
136 // detect maximum depth of dereferencing
137 if (this.indirections.length > this.options.dereference.maxDepth) {
138 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
139 }
140
141 // append referencing reference to ancestors lineage
142 directAncestors.add(referencingElement);
143
144 // dive deep into the fragment
145 const visitor = OpenApi2DereferenceVisitor({
146 reference,
147 namespace: this.namespace,
148 indirections: [...this.indirections],
149 options: this.options,
150 ancestors: ancestorsLineage
151 });
152 referencedElement = await visitAsync(referencedElement, visitor, {
153 keyMap: _apidomNsOpenapi.keyMap,
154 nodeTypeGetter: _apidomNsOpenapi.getNodeType
155 });
156
157 // remove referencing reference from ancestors lineage
158 directAncestors.delete(referencingElement);
159 this.indirections.pop();
160 const mergeAndAnnotateReferencedElement = refedElement => {
161 const copy = (0, _apidomCore.cloneShallow)(refedElement);
162
163 // annotate referenced element with info about original referencing element
164 copy.setMetaProperty('ref-fields', {
165 // @ts-ignore
166 $ref: (0, _apidomCore.toValue)(referencingElement.$ref)
167 });
168 // annotate fragment with info about origin
169 copy.setMetaProperty('ref-origin', reference.uri);
170 // annotate fragment with info about referencing element
171 copy.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
172 return copy;
173 };
174
175 // attempting to create cycle
176 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
177 var _ancestorsLineage$fin;
178 const replaceWith = (_ancestorsLineage$fin = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin : mergeAndAnnotateReferencedElement(referencedElement);
179 if ((0, _apidomCore.isMemberElement)(parent)) {
180 parent.value = replaceWith; // eslint-disable-line no-param-reassign
181 } else if (Array.isArray(parent)) {
182 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
183 }
184 return false;
185 }
186
187 // transclude referencing element with merged referenced element
188 return mergeAndAnnotateReferencedElement(referencedElement);
189 },
190 async PathItemElement(referencingElement, key, parent, path, ancestors) {
191 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
192
193 // ignore PathItemElement without $ref field
194 if (!(0, _apidomCore.isStringElement)(referencingElement.$ref)) {
195 return undefined;
196 }
197
198 // detect possible cycle in traversal and avoid it
199 if (ancestorsLineage.includesCycle(referencingElement)) {
200 return false;
201 }
202 const retrievalURI = this.toBaseURI((0, _apidomCore.toValue)(referencingElement.$ref));
203
204 // ignore resolving external Path Item Objects
205 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
206 // skip traversing this Path Item element but traverse all it's child elements
207 return undefined;
208 }
209 const reference = await this.toReference((0, _apidomCore.toValue)(referencingElement.$ref));
210 const $refBaseURI = url.resolve(retrievalURI, (0, _apidomCore.toValue)(referencingElement.$ref));
211 this.indirections.push(referencingElement);
212 const jsonPointer = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
213
214 // possibly non-semantic referenced element
215 let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
216
217 // applying semantics to a referenced element
218 if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
219 referencedElement = _apidomNsOpenapi.PathItemElement.refract(referencedElement);
220 }
221
222 // detect direct or indirect reference
223 if (this.indirections.includes(referencedElement)) {
224 throw new _apidomError.ApiDOMError('Recursive Path Item Object reference detected');
225 }
226
227 // detect maximum depth of dereferencing
228 if (this.indirections.length > this.options.dereference.maxDepth) {
229 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
230 }
231
232 // append referencing path item to ancestors lineage
233 directAncestors.add(referencingElement);
234
235 // dive deep into the referenced element
236 const visitor = OpenApi2DereferenceVisitor({
237 reference,
238 namespace: this.namespace,
239 indirections: [...this.indirections],
240 options: this.options,
241 ancestors: ancestorsLineage
242 });
243 referencedElement = await visitAsync(referencedElement, visitor, {
244 keyMap: _apidomNsOpenapi.keyMap,
245 nodeTypeGetter: _apidomNsOpenapi.getNodeType
246 });
247
248 // remove referencing path item from ancestors lineage
249 directAncestors.delete(referencingElement);
250 this.indirections.pop();
251 const mergeAndAnnotateReferencedElement = refedElement => {
252 // merge fields from referenced Path Item with referencing one
253 const mergedElement = new _apidomNsOpenapi.PathItemElement([...refedElement.content], (0, _apidomCore.cloneDeep)(referencedElement.meta), (0, _apidomCore.cloneDeep)(referencedElement.attributes));
254 // existing keywords from referencing PathItemElement overrides ones from referenced element
255 referencingElement.forEach((value, keyElement, item) => {
256 mergedElement.remove((0, _apidomCore.toValue)(keyElement));
257 mergedElement.content.push(item);
258 });
259 mergedElement.remove('$ref');
260
261 // annotate referenced element with info about original referencing element
262 mergedElement.setMetaProperty('ref-fields', {
263 $ref: (0, _apidomCore.toValue)(referencingElement.$ref)
264 });
265 // annotate referenced element with info about origin
266 mergedElement.setMetaProperty('ref-origin', reference.uri);
267 // annotate fragment with info about referencing element
268 mergedElement.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
269 return mergedElement;
270 };
271
272 // attempting to create cycle
273 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
274 var _ancestorsLineage$fin2;
275 const replaceWith = (_ancestorsLineage$fin2 = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin2 : mergeAndAnnotateReferencedElement(referencedElement);
276 if ((0, _apidomCore.isMemberElement)(parent)) {
277 parent.value = replaceWith; // eslint-disable-line no-param-reassign
278 } else if (Array.isArray(parent)) {
279 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
280 }
281 return false;
282 }
283
284 // transclude referencing element with merged referenced element
285 return mergeAndAnnotateReferencedElement(referencedElement);
286 },
287 async JSONReferenceElement(referencingElement, key, parent, path, ancestors) {
288 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
289
290 // detect possible cycle in traversal and avoid it
291 if (ancestorsLineage.includesCycle(referencingElement)) {
292 return false;
293 }
294 const retrievalURI = this.toBaseURI((0, _apidomCore.toValue)(referencingElement.$ref));
295
296 // ignore resolving external JSONReference Objects
297 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
298 // skip traversing this JSONReference element and all it's child elements
299 return false;
300 }
301 const reference = await this.toReference((0, _apidomCore.toValue)(referencingElement.$ref));
302 const $refBaseURI = url.resolve(retrievalURI, (0, _apidomCore.toValue)(referencingElement.$ref));
303 this.indirections.push(referencingElement);
304 const jsonPointer = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
305
306 // possibly non-semantic fragment
307 let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
308
309 // applying semantics to a fragment
310 if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
311 const referencedElementType = (0, _apidomCore.toValue)(referencingElement.meta.get('referenced-element'));
312 if ((0, _apidomNsOpenapi.isJSONReferenceLikeElement)(referencedElement)) {
313 // handling indirect references
314 referencedElement = _apidomNsOpenapi.ReferenceElement.refract(referencedElement);
315 referencedElement.setMetaProperty('referenced-element', referencedElementType);
316 } else {
317 // handling direct references
318 const ElementClass = this.namespace.getElementClass(referencedElementType);
319 referencedElement = ElementClass.refract(referencedElement);
320 }
321 }
322
323 // detect direct or circular reference
324 if (this.indirections.includes(referencedElement)) {
325 throw new _apidomError.ApiDOMError('Recursive Reference Object detected');
326 }
327
328 // detect maximum depth of dereferencing
329 if (this.indirections.length > this.options.dereference.maxDepth) {
330 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
331 }
332
333 // append referencing reference to ancestors lineage
334 directAncestors.add(referencingElement);
335
336 // dive deep into the fragment
337 const visitor = OpenApi2DereferenceVisitor({
338 reference,
339 namespace: this.namespace,
340 indirections: [...this.indirections],
341 options: this.options,
342 ancestors: ancestorsLineage
343 });
344 referencedElement = await visitAsync(referencedElement, visitor, {
345 keyMap: _apidomNsOpenapi.keyMap,
346 nodeTypeGetter: _apidomNsOpenapi.getNodeType
347 });
348
349 // remove referencing reference from ancestors lineage
350 directAncestors.delete(referencingElement);
351 this.indirections.pop();
352 const mergeAndAnnotateReferencedElement = refedElement => {
353 const copy = (0, _apidomCore.cloneShallow)(refedElement);
354
355 // annotate referenced element with info about original referencing element
356 copy.setMetaProperty('ref-fields', {
357 // @ts-ignore
358 $ref: (0, _apidomCore.toValue)(referencingElement.$ref)
359 });
360 // annotate fragment with info about origin
361 copy.setMetaProperty('ref-origin', reference.uri);
362 // annotate fragment with info about referencing element
363 copy.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
364 return copy;
365 };
366
367 // attempting to create cycle
368 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
369 var _ancestorsLineage$fin3;
370 const replaceWith = (_ancestorsLineage$fin3 = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin3 : mergeAndAnnotateReferencedElement(referencedElement);
371 if ((0, _apidomCore.isMemberElement)(parent)) {
372 parent.value = replaceWith; // eslint-disable-line no-param-reassign
373 } else if (Array.isArray(parent)) {
374 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
375 }
376 return false;
377 }
378
379 // transclude referencing element with merged referenced element
380 return mergeAndAnnotateReferencedElement(referencedElement);
381 }
382 }
383});
384var _default = exports.default = OpenApi2DereferenceVisitor;
Note: See TracBrowser for help on using the repository browser.