source: node_modules/@swagger-api/apidom-reference/cjs/dereference/strategies/asyncapi-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: 13.8 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 _apidomNsAsyncapi = require("@swagger-api/apidom-ns-asyncapi-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 AsyncApi2DereferenceVisitor = (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 but traverse 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, _apidomNsAsyncapi.isReferenceLikeElement)(referencedElement)) {
121 // handling indirect references
122 referencedElement = _apidomNsAsyncapi.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 = AsyncApi2DereferenceVisitor({
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: _apidomNsAsyncapi.keyMap,
154 nodeTypeGetter: _apidomNsAsyncapi.getNodeType
155 });
156
157 // remove referencing reference from ancestors lineage
158 directAncestors.delete(referencingElement);
159 this.indirections.pop();
160
161 // Boolean JSON Schemas
162 if ((0, _apidomNsAsyncapi.isBooleanJsonSchemaElement)(referencedElement)) {
163 const booleanJsonSchemaElement = (0, _apidomCore.cloneDeep)(referencedElement);
164 // annotate referenced element with info about original referencing element
165 booleanJsonSchemaElement.setMetaProperty('ref-fields', {
166 $ref: (0, _apidomCore.toValue)(referencingElement.$ref)
167 });
168 // annotate referenced element with info about origin
169 booleanJsonSchemaElement.setMetaProperty('ref-origin', reference.uri);
170 // annotate fragment with info about referencing element
171 booleanJsonSchemaElement.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
172 return booleanJsonSchemaElement;
173 }
174 const mergeAndAnnotateReferencedElement = refedElement => {
175 const copy = (0, _apidomCore.cloneShallow)(refedElement);
176
177 // annotate referenced element with info about original referencing element
178 copy.setMetaProperty('ref-fields', {
179 $ref: (0, _apidomCore.toValue)(referencingElement.$ref)
180 });
181 // annotate fragment with info about origin
182 copy.setMetaProperty('ref-origin', reference.uri);
183 // annotate fragment with info about referencing element
184 copy.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
185 return copy;
186 };
187
188 // attempting to create cycle
189 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
190 var _ancestorsLineage$fin;
191 const replaceWith = (_ancestorsLineage$fin = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin : mergeAndAnnotateReferencedElement(referencedElement);
192 if ((0, _apidomCore.isMemberElement)(parent)) {
193 parent.value = replaceWith; // eslint-disable-line no-param-reassign
194 } else if (Array.isArray(parent)) {
195 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
196 }
197 return false;
198 }
199
200 // transclude referencing element with merged referenced element
201 return mergeAndAnnotateReferencedElement(referencedElement);
202 },
203 async ChannelItemElement(referencingElement, key, parent, path, ancestors) {
204 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
205
206 // ignore ChannelItemElement without $ref field
207 if (!(0, _apidomCore.isStringElement)(referencingElement.$ref)) {
208 return undefined;
209 }
210
211 // detect possible cycle in traversal and avoid it
212 if (ancestorsLineage.includesCycle(referencingElement)) {
213 return false;
214 }
215 const retrievalURI = this.toBaseURI((0, _apidomCore.toValue)(referencingElement.$ref));
216
217 // ignore resolving external Channel Item Objects
218 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
219 // skip traversing this channel item but traverse all it's child elements
220 return undefined;
221 }
222 const reference = await this.toReference((0, _apidomCore.toValue)(referencingElement.$ref));
223 const $refBaseURI = url.resolve(retrievalURI, (0, _apidomCore.toValue)(referencingElement.$ref));
224 this.indirections.push(referencingElement);
225 const jsonPointer = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
226
227 // possibly non-semantic referenced element
228 let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
229
230 // applying semantics to a referenced element
231 if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
232 referencedElement = _apidomNsAsyncapi.ChannelItemElement.refract(referencedElement);
233 }
234
235 // detect direct or indirect reference
236 if (this.indirections.includes(referencedElement)) {
237 throw new _apidomError.ApiDOMError('Recursive Channel Item Object reference detected');
238 }
239
240 // detect maximum depth of dereferencing
241 if (this.indirections.length > this.options.dereference.maxDepth) {
242 throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
243 }
244
245 // append referencing channel item to ancestors lineage
246 directAncestors.add(referencingElement);
247
248 // dive deep into the referenced element
249 const visitor = AsyncApi2DereferenceVisitor({
250 reference,
251 namespace: this.namespace,
252 indirections: [...this.indirections],
253 options: this.options,
254 ancestors: ancestorsLineage
255 });
256 referencedElement = await visitAsync(referencedElement, visitor, {
257 keyMap: _apidomNsAsyncapi.keyMap,
258 nodeTypeGetter: _apidomNsAsyncapi.getNodeType
259 });
260
261 // remove referencing channel item from ancestors lineage
262 directAncestors.delete(referencingElement);
263 this.indirections.pop();
264 const mergeAndAnnotateReferencedElement = refedElement => {
265 // merge fields from referenced Channel Item with referencing one
266 const mergedElement = new _apidomNsAsyncapi.ChannelItemElement([...refedElement.content], (0, _apidomCore.cloneDeep)(refedElement.meta), (0, _apidomCore.cloneDeep)(refedElement.attributes));
267 // existing keywords from referencing ChannelItemElement overrides ones from referenced ChannelItemElement
268 referencingElement.forEach((value, keyElement, item) => {
269 mergedElement.remove((0, _apidomCore.toValue)(keyElement));
270 mergedElement.content.push(item);
271 });
272 mergedElement.remove('$ref');
273
274 // annotate referenced element with info about original referencing element
275 mergedElement.setMetaProperty('ref-fields', {
276 $ref: (0, _apidomCore.toValue)(referencingElement.$ref)
277 });
278 // annotate referenced with info about origin
279 mergedElement.setMetaProperty('ref-origin', reference.uri);
280 // annotate fragment with info about referencing element
281 mergedElement.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
282 return mergedElement;
283 };
284
285 // attempting to create cycle
286 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
287 var _ancestorsLineage$fin2;
288 const replaceWith = (_ancestorsLineage$fin2 = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin2 : mergeAndAnnotateReferencedElement(referencedElement);
289 if ((0, _apidomCore.isMemberElement)(parent)) {
290 parent.value = replaceWith; // eslint-disable-line no-param-reassign
291 } else if (Array.isArray(parent)) {
292 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
293 }
294 return false;
295 }
296
297 // transclude referencing element with merged referenced element
298 return mergeAndAnnotateReferencedElement(referencedElement);
299 }
300 }
301});
302var _default = exports.default = AsyncApi2DereferenceVisitor;
Note: See TracBrowser for help on using the repository browser.