source: node_modules/@swagger-api/apidom-reference/cjs/resolve/strategies/openapi-3-1/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: 19.0 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 _ramdaAdjunct = require("ramda-adjunct");
10var _apidomCore = require("@swagger-api/apidom-core");
11var _apidomError = require("@swagger-api/apidom-error");
12var _apidomJsonPointer = require("@swagger-api/apidom-json-pointer");
13var _apidomNsOpenapi = require("@swagger-api/apidom-ns-openapi-3-1");
14var _MaximumDereferenceDepthError = _interopRequireDefault(require("../../../errors/MaximumDereferenceDepthError.cjs"));
15var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
16var _EvaluationJsonSchemaUriError = _interopRequireDefault(require("../../../errors/EvaluationJsonSchemaUriError.cjs"));
17var url = _interopRequireWildcard(require("../../../util/url.cjs"));
18var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
19var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
20var _File = _interopRequireDefault(require("../../../util/File.cjs"));
21var _uri = require("../../../dereference/strategies/openapi-3-1/selectors/uri.cjs");
22var _util = require("./util.cjs");
23var _$anchor = require("../../../dereference/strategies/openapi-3-1/selectors/$anchor.cjs");
24// @ts-ignore
25const visitAsync = _apidomCore.visit[Symbol.for('nodejs.util.promisify.custom')];
26
27// eslint-disable-next-line @typescript-eslint/naming-convention
28const OpenApi3_1ResolveVisitor = (0, _stampit.default)({
29 props: {
30 indirections: [],
31 namespace: null,
32 reference: null,
33 crawledElements: null,
34 crawlingMap: null,
35 visited: null,
36 options: null
37 },
38 init({
39 reference,
40 namespace,
41 indirections = [],
42 visited = new WeakSet(),
43 options
44 }) {
45 this.indirections = indirections;
46 this.namespace = namespace;
47 this.reference = reference;
48 this.crawledElements = [];
49 this.crawlingMap = {};
50 this.visited = visited;
51 this.options = options;
52 },
53 methods: {
54 toBaseURI(uri) {
55 return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
56 },
57 async toReference(uri) {
58 // detect maximum depth of resolution
59 if (this.reference.depth >= this.options.resolve.maxDepth) {
60 throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);
61 }
62 const baseURI = this.toBaseURI(uri);
63 const {
64 refSet
65 } = this.reference;
66
67 // we've already processed this Reference in past
68 if (refSet.has(baseURI)) {
69 return refSet.find((0, _ramda.propEq)(baseURI, 'uri'));
70 }
71 const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
72 ...this.options,
73 parse: {
74 ...this.options.parse,
75 mediaType: 'text/plain'
76 }
77 });
78
79 // register new Reference with ReferenceSet
80 const reference = (0, _Reference.default)({
81 uri: baseURI,
82 value: parseResult,
83 depth: this.reference.depth + 1
84 });
85 refSet.add(reference);
86 return reference;
87 },
88 ReferenceElement(referenceElement) {
89 const uri = (0, _apidomCore.toValue)(referenceElement.$ref);
90 const retrievalURI = this.toBaseURI(uri);
91
92 // ignore resolving external Reference Objects
93 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
94 // skip traversing this reference element and all it's child elements
95 return false;
96 }
97 if (!(0, _ramda.has)(retrievalURI, this.crawlingMap)) {
98 this.crawlingMap[retrievalURI] = this.toReference(uri);
99 }
100 this.crawledElements.push(referenceElement);
101 return undefined;
102 },
103 PathItemElement(pathItemElement) {
104 // ignore PathItemElement without $ref field
105 if (!(0, _apidomCore.isStringElement)(pathItemElement.$ref)) {
106 return undefined;
107 }
108 const uri = (0, _apidomCore.toValue)(pathItemElement.$ref);
109 const retrievalURI = this.toBaseURI(uri);
110
111 // ignore resolving external Path Item Objects
112 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
113 // skip traversing this Path Item element but traverse all it's child elements
114 return undefined;
115 }
116 if (!(0, _ramda.has)(retrievalURI, this.crawlingMap)) {
117 this.crawlingMap[retrievalURI] = this.toReference(uri);
118 }
119 this.crawledElements.push(pathItemElement);
120 return undefined;
121 },
122 LinkElement(linkElement) {
123 // ignore LinkElement without operationRef or operationId field
124 if (!(0, _apidomCore.isStringElement)(linkElement.operationRef) && !(0, _apidomCore.isStringElement)(linkElement.operationId)) {
125 return undefined;
126 }
127 const uri = (0, _apidomCore.toValue)(linkElement.operationRef);
128 const retrievalURI = this.toBaseURI(uri);
129
130 // ignore resolving external Path Item Elements
131 const isExternal = url.stripHash(this.reference.uri) !== retrievalURI;
132 if (!this.options.resolve.external && isExternal) {
133 return undefined;
134 }
135
136 // operationRef and operationId are mutually exclusive
137 if ((0, _apidomCore.isStringElement)(linkElement.operationRef) && (0, _apidomCore.isStringElement)(linkElement.operationId)) {
138 throw new _apidomError.ApiDOMError('LinkElement operationRef and operationId are mutually exclusive.');
139 }
140 if (isExternal) {
141 if (!(0, _ramda.has)(retrievalURI, this.crawlingMap)) {
142 this.crawlingMap[retrievalURI] = this.toReference(uri);
143 }
144 }
145 return undefined;
146 },
147 ExampleElement(exampleElement) {
148 // ignore ExampleElement without externalValue field
149 if (!(0, _apidomCore.isStringElement)(exampleElement.externalValue)) {
150 return undefined;
151 }
152
153 // value and externalValue fields are mutually exclusive
154 if (exampleElement.hasKey('value') && (0, _apidomCore.isStringElement)(exampleElement.externalValue)) {
155 throw new _apidomError.ApiDOMError('ExampleElement value and externalValue fields are mutually exclusive.');
156 }
157 const uri = (0, _apidomCore.toValue)(exampleElement.externalValue);
158 const retrievalURI = this.toBaseURI(uri);
159
160 // ignore resolving external Example Objects
161 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
162 // skip traversing this Example element but traverse all it's child elements
163 return undefined;
164 }
165 if (!(0, _ramda.has)(retrievalURI, this.crawlingMap)) {
166 this.crawlingMap[retrievalURI] = this.toReference(uri);
167 }
168 return undefined;
169 },
170 async SchemaElement(schemaElement) {
171 /**
172 * Skip traversal for already visited schemas and all their child schemas.
173 * visit function detects cycles in path automatically.
174 */
175 if (this.visited.has(schemaElement)) {
176 return false;
177 }
178 // skip current referencing schema as $ref keyword was not defined
179 if (!(0, _apidomCore.isStringElement)(schemaElement.$ref)) {
180 // mark current referencing schema as visited
181 this.visited.add(schemaElement);
182 // skip traversing this schema but traverse all it's child schemas
183 return undefined;
184 }
185
186 // compute baseURI using rules around $id and $ref keywords
187 const reference = await this.toReference(url.unsanitize(this.reference.uri));
188 let {
189 uri: retrievalURI
190 } = reference;
191 const $refBaseURI = (0, _util.resolveSchema$refField)(retrievalURI, schemaElement);
192 const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
193 const file = (0, _File.default)({
194 uri: $refBaseURIStrippedHash
195 });
196 const isUnknownURI = (0, _ramda.none)(r => r.canRead(file), this.options.resolve.resolvers);
197 const isURL = !isUnknownURI;
198 const isExternalURL = uri => url.stripHash(this.reference.uri) !== uri;
199 if (!(0, _ramda.has)($refBaseURIStrippedHash, this.crawlingMap)) {
200 try {
201 if (isUnknownURI || isURL) {
202 this.crawlingMap[$refBaseURIStrippedHash] = reference;
203 } else {
204 retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
205
206 // ignore resolving external Schema Objects
207 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
208 // skip traversing this schema element but traverse all it's child elements
209 this.visited.add(schemaElement);
210 return undefined;
211 }
212 this.crawlingMap[$refBaseURIStrippedHash] = this.toReference(url.unsanitize($refBaseURI));
213 }
214 } catch (error) {
215 if (isURL && error instanceof _EvaluationJsonSchemaUriError.default) {
216 retrievalURI = this.toBaseURI(url.unsanitize($refBaseURI));
217
218 // ignore resolving external Schema Objects
219 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
220 // skip traversing this schema element but traverse all it's child elements
221 this.visited.add(schemaElement);
222 return undefined;
223 }
224 this.crawlingMap[$refBaseURIStrippedHash] = this.toReference(url.unsanitize($refBaseURI));
225 } else {
226 throw error;
227 }
228 }
229 }
230 this.crawledElements.push(schemaElement);
231 return undefined;
232 },
233 async crawlReferenceElement(referenceElement) {
234 // @ts-ignore
235 const reference = await this.toReference((0, _apidomCore.toValue)(referenceElement.$ref));
236 this.indirections.push(referenceElement);
237 const jsonPointer = (0, _apidomJsonPointer.uriToPointer)((0, _apidomCore.toValue)(referenceElement.$ref));
238
239 // possibly non-semantic fragment
240 let fragment = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
241
242 // applying semantics to a fragment
243 if ((0, _apidomCore.isPrimitiveElement)(fragment)) {
244 const referencedElementType = (0, _apidomCore.toValue)(referenceElement.meta.get('referenced-element'));
245 if ((0, _apidomNsOpenapi.isReferenceLikeElement)(fragment)) {
246 // handling indirect references
247 fragment = _apidomNsOpenapi.ReferenceElement.refract(fragment);
248 fragment.setMetaProperty('referenced-element', referencedElementType);
249 } else {
250 // handling direct references
251 const ElementClass = this.namespace.getElementClass(referencedElementType);
252 fragment = ElementClass.refract(fragment);
253 }
254 }
255
256 // detect direct or circular reference
257 if (this.indirections.includes(fragment)) {
258 throw new _apidomError.ApiDOMError('Recursive Reference Object detected');
259 }
260
261 // detect maximum depth of dereferencing
262 if (this.indirections.length > this.options.dereference.maxDepth) {
263 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
264 }
265
266 // dive deep into the fragment
267 const visitor = OpenApi3_1ResolveVisitor({
268 reference,
269 namespace: this.namespace,
270 indirections: [...this.indirections],
271 options: this.options
272 });
273 await visitAsync(fragment, visitor, {
274 keyMap: _apidomNsOpenapi.keyMap,
275 nodeTypeGetter: _apidomNsOpenapi.getNodeType
276 });
277 await visitor.crawl();
278 this.indirections.pop();
279 },
280 async crawlPathItemElement(pathItemElement) {
281 // @ts-ignore
282 const reference = await this.toReference((0, _apidomCore.toValue)(pathItemElement.$ref));
283 this.indirections.push(pathItemElement);
284 const jsonPointer = (0, _apidomJsonPointer.uriToPointer)((0, _apidomCore.toValue)(pathItemElement.$ref));
285
286 // possibly non-semantic fragment
287 let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
288
289 // applying semantics to a fragment
290 if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
291 referencedElement = _apidomNsOpenapi.PathItemElement.refract(referencedElement);
292 }
293
294 // detect direct or indirect reference
295 if (this.indirections.includes(referencedElement)) {
296 throw new _apidomError.ApiDOMError('Recursive Path Item Object reference detected');
297 }
298
299 // detect maximum depth of dereferencing
300 if (this.indirections.length > this.options.dereference.maxDepth) {
301 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
302 }
303
304 // dive deep into the fragment
305 const visitor = OpenApi3_1ResolveVisitor({
306 reference,
307 namespace: this.namespace,
308 indirections: [...this.indirections],
309 options: this.options
310 });
311 await visitAsync(referencedElement, visitor, {
312 keyMap: _apidomNsOpenapi.keyMap,
313 nodeTypeGetter: _apidomNsOpenapi.getNodeType
314 });
315 await visitor.crawl();
316 this.indirections.pop();
317 },
318 async crawlSchemaElement(referencingElement) {
319 // compute baseURI using rules around $id and $ref keywords
320 let reference = await this.toReference(url.unsanitize(this.reference.uri));
321 let {
322 uri: retrievalURI
323 } = reference;
324 const $refBaseURI = (0, _util.resolveSchema$refField)(retrievalURI, referencingElement);
325 const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
326 const file = (0, _File.default)({
327 uri: $refBaseURIStrippedHash
328 });
329 const isUnknownURI = (0, _ramda.none)(r => r.canRead(file), this.options.resolve.resolvers);
330 const isURL = !isUnknownURI;
331 const isExternalURL = uri => url.stripHash(this.reference.uri) !== uri;
332 this.indirections.push(referencingElement);
333
334 // determining reference, proper evaluation and selection mechanism
335 let referencedElement;
336 try {
337 if (isUnknownURI || isURL) {
338 // we're dealing with canonical URI or URL with possible fragment
339 const selector = $refBaseURI;
340 referencedElement = (0, _uri.evaluate)(selector,
341 // @ts-ignore
342 (0, _util.maybeRefractToSchemaElement)(reference.value.result));
343 } else {
344 // we're assuming here that we're dealing with JSON Pointer here
345 retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
346
347 // ignore resolving external Schema Objects
348 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
349 // skip traversing this schema element but traverse all it's child elements
350 return undefined;
351 }
352 reference = await this.toReference(url.unsanitize($refBaseURI));
353 const selector = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
354 referencedElement = (0, _util.maybeRefractToSchemaElement)(
355 // @ts-ignore
356 (0, _apidomJsonPointer.evaluate)(selector, reference.value.result));
357 }
358 } catch (error) {
359 /**
360 * No SchemaElement($id=URL) was not found, so we're going to try to resolve
361 * the URL and assume the returned response is a JSON Schema.
362 */
363 if (isURL && error instanceof _EvaluationJsonSchemaUriError.default) {
364 if ((0, _$anchor.isAnchor)((0, _$anchor.uriToAnchor)($refBaseURI))) {
365 // we're dealing with JSON Schema $anchor here
366 retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
367
368 // ignore resolving external Schema Objects
369 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
370 // skip traversing this schema element but traverse all it's child elements
371 return undefined;
372 }
373 reference = await this.toReference(url.unsanitize($refBaseURI));
374 const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
375 referencedElement = (0, _$anchor.evaluate)(selector,
376 // @ts-ignore
377 (0, _util.maybeRefractToSchemaElement)(reference.value.result));
378 } else {
379 // we're assuming here that we're dealing with JSON Pointer here
380 retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
381
382 // ignore resolving external Schema Objects
383 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
384 // skip traversing this schema element but traverse all it's child elements
385 return undefined;
386 }
387 reference = await this.toReference(url.unsanitize($refBaseURI));
388 const selector = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
389 referencedElement = (0, _util.maybeRefractToSchemaElement)(
390 // @ts-ignore
391 (0, _apidomJsonPointer.evaluate)(selector, reference.value.result));
392 }
393 } else {
394 throw error;
395 }
396 }
397
398 // mark current referencing schema as visited
399 this.visited.add(referencingElement);
400
401 // detect direct or indirect reference
402 if (this.indirections.includes(referencedElement)) {
403 throw new _apidomError.ApiDOMError('Recursive Schema Object reference detected');
404 }
405
406 // detect maximum depth of dereferencing
407 if (this.indirections.length > this.options.dereference.maxDepth) {
408 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
409 }
410
411 // dive deep into the fragment
412 const visitor = OpenApi3_1ResolveVisitor({
413 reference,
414 namespace: this.namespace,
415 indirections: [...this.indirections],
416 options: this.options,
417 visited: this.visited
418 });
419 await visitAsync(referencedElement, visitor, {
420 keyMap: _apidomNsOpenapi.keyMap,
421 nodeTypeGetter: _apidomNsOpenapi.getNodeType
422 });
423 await visitor.crawl();
424 this.indirections.pop();
425 return undefined;
426 },
427 async crawl() {
428 /**
429 * Synchronize all parallel resolutions in this place.
430 * After synchronization happened we can be sure that refSet
431 * contains resolved Reference objects.
432 */
433 await (0, _ramda.pipe)(_ramda.values, _ramdaAdjunct.allP)(this.crawlingMap);
434 this.crawlingMap = null;
435
436 /* eslint-disable no-await-in-loop */
437 for (const element of this.crawledElements) {
438 if ((0, _apidomNsOpenapi.isReferenceElement)(element)) {
439 await this.crawlReferenceElement(element);
440 } else if ((0, _apidomNsOpenapi.isSchemaElement)(element)) {
441 await this.crawlSchemaElement(element);
442 } else if ((0, _apidomNsOpenapi.isPathItemElement)(element)) {
443 await this.crawlPathItemElement(element);
444 }
445 }
446 /* eslint-enable */
447 }
448 }
449});
450var _default = exports.default = OpenApi3_1ResolveVisitor;
Note: See TracBrowser for help on using the repository browser.