diff --git a/package.json b/package.json index e2909bb40..918338e8a 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "dependencies": { "@babel/runtime-corejs3": "^7.11.2", "@swagger-api/apidom-core": "^0.63.0", - "@swagger-api/apidom-reference": "^0.63.0", + "@swagger-api/apidom-reference": "^0.63.1", "@swagger-api/apidom-ns-openapi-3-1": "^0.63.0", "@swagger-api/apidom-json-pointer": "^0.63.0", "cookie": "~0.5.0", diff --git a/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitor.js b/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitor.js index 83ab42d61..86376cd6b 100644 --- a/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitor.js +++ b/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitor.js @@ -4,6 +4,7 @@ import { isPrimitiveElement, isStringElement, visit, + includesClasses, } from '@swagger-api/apidom-core'; import { isReferenceElementExternal, @@ -43,26 +44,38 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c props: { useCircularStructures: true, allowMetaPatches: false, - ancestors: [], }, - init({ useCircularStructures, allowMetaPatches, ancestors = this.ancestors }) { + init({ useCircularStructures, allowMetaPatches }) { this.useCircularStructures = useCircularStructures; this.allowMetaPatches = allowMetaPatches; - this.ancestors = [...ancestors]; }, methods: { - async ReferenceElement(referenceElement) { + async ReferenceElement(referenceElement, key, parent, path, ancestors) { + const [ancestorsLineage, directAncestors] = this.toAncestorLineage(ancestors); + + // skip already identified cycled Path Item Objects + if (includesClasses(['cycle'], referenceElement.$ref)) { + return false; + } + + // detect possible cycle in traversal and avoid it + if (ancestorsLineage.some((ancs) => ancs.has(referenceElement))) { + // skip processing this schema and all it's child schemas + return false; + } + // ignore resolving external Reference Objects if (!this.options.resolve.external && isReferenceElementExternal(referenceElement)) { return false; } - // @ts-ignore const reference = await this.toReference(referenceElement.$ref.toValue()); + const retrievalURI = reference.uri; + const $refBaseURI = url.resolve(retrievalURI, referenceElement.$ref.toValue()); this.indirections.push(referenceElement); - const jsonPointer = uriToPointer(referenceElement.$ref?.toValue()); + const jsonPointer = uriToPointer($refBaseURI); // possibly non-semantic fragment let fragment = jsonPointerEvaluate(jsonPointer, reference.value.result); @@ -94,24 +107,48 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c ); } + // append referencing schema to ancestors lineage + directAncestors.add(referenceElement); + // dive deep into the fragment const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], options: this.options, + ancestors: ancestorsLineage, allowMetaPatches: this.allowMetaPatches, + useCircularStructures: this.useCircularStructures, }); fragment = await visitAsync(fragment, visitor, { keyMap, nodeTypeGetter: getNodeType }); - fragment = fragment.clone(); + // remove referencing schema from ancestors lineage + directAncestors.delete(referenceElement); + + this.indirections.pop(); - // annotate fragment with info about original Reference element + if (!this.useCircularStructures) { + const hasCycles = ancestorsLineage.some((ancs) => ancs.has(fragment)); + if (hasCycles) { + if (url.isHttpUrl(retrievalURI) || url.isFileSystemPath(retrievalURI)) { + // make the referencing URL or file system path absolute + const cycledReferenceElement = new ReferenceElement( + { $ref: $refBaseURI }, + referenceElement.meta.clone(), + referenceElement.attributes.clone() + ); + cycledReferenceElement.get('$ref').classes.push('cycle'); + return cycledReferenceElement; + } + // skip processing this schema but traverse all it's child schemas + return false; + } + } + + fragment = fragment.clone(); fragment.setMetaProperty('ref-fields', { $ref: referenceElement.$ref?.toValue(), - // @ts-ignore description: referenceElement.description?.toValue(), - // @ts-ignore summary: referenceElement.summary?.toValue(), }); // annotate fragment with info about origin @@ -121,11 +158,9 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c const hasDescription = typeof referenceElement.description !== 'undefined'; const hasSummary = typeof referenceElement.description !== 'undefined'; if (hasDescription && 'description' in fragment) { - // @ts-ignore fragment.description = referenceElement.description; } if (hasSummary && 'summary' in fragment) { - // @ts-ignore fragment.summary = referenceElement.summary; } @@ -134,34 +169,46 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c const objectFragment = fragment; // apply meta patch only when not already applied if (typeof objectFragment.get('$$ref') === 'undefined') { - const absoluteURI = url.resolve(reference.uri, referenceElement.$ref?.toValue()); - objectFragment.set('$$ref', absoluteURI); + const baseURI = url.resolve(retrievalURI, $refBaseURI); + objectFragment.set('$$ref', baseURI); } } - this.indirections.pop(); - // transclude the element for a fragment return fragment; }, - async PathItemElement(pathItemElement) { + async PathItemElement(pathItemElement, key, parent, path, ancestors) { + const [ancestorsLineage, directAncestors] = this.toAncestorLineage(ancestors); + // ignore PathItemElement without $ref field if (!isStringElement(pathItemElement.$ref)) { return undefined; } + // skip already identified cycled Path Item Objects + if (includesClasses(['cycle'], pathItemElement.$ref)) { + return false; + } + + // detect possible cycle in traversal and avoid it + if (ancestorsLineage.some((ancs) => ancs.has(pathItemElement))) { + // skip processing this schema and all it's child schemas + return false; + } + // ignore resolving external Path Item Elements if (!this.options.resolve.external && isPathItemElementExternal(pathItemElement)) { return undefined; } - // @ts-ignore const reference = await this.toReference(pathItemElement.$ref.toValue()); + const retrievalURI = reference.uri; + const $refBaseURI = url.resolve(retrievalURI, pathItemElement.$ref.toValue()); this.indirections.push(pathItemElement); - const jsonPointer = uriToPointer(pathItemElement.$ref?.toValue()); + const jsonPointer = uriToPointer($refBaseURI); // possibly non-semantic referenced element let referencedElement = jsonPointerEvaluate(jsonPointer, reference.value.result); @@ -183,58 +230,82 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c ); } + // append referencing schema to ancestors lineage + directAncestors.add(pathItemElement); + // dive deep into the referenced element const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], options: this.options, + ancestors: ancestorsLineage, allowMetaPatches: this.allowMetaPatches, + useCircularStructures: this.useCircularStructures, }); referencedElement = await visitAsync(referencedElement, visitor, { keyMap, nodeTypeGetter: getNodeType, }); + // remove referencing schema from ancestors lineage + directAncestors.delete(pathItemElement); + this.indirections.pop(); + if (!this.useCircularStructures) { + const hasCycles = ancestorsLineage.some((ancs) => ancs.has(referencedElement)); + if (hasCycles) { + if (url.isHttpUrl(retrievalURI) || url.isFileSystemPath(retrievalURI)) { + // make the referencing URL or file system path absolute + const cycledPathItemElement = new PathItemElement( + { $ref: $refBaseURI }, + pathItemElement.meta.clone(), + pathItemElement.attributes.clone() + ); + cycledPathItemElement.get('$ref').classes.push('cycle'); + return cycledPathItemElement; + } + // skip processing this schema but traverse all it's child schemas + return false; + } + } + // merge fields from referenced Path Item with referencing one - const mergedResult = new PathItemElement( - // @ts-ignore + const mergedPathItemElement = new PathItemElement( [...referencedElement.content], referencedElement.meta.clone(), referencedElement.attributes.clone() ); // existing keywords from referencing PathItemElement overrides ones from referenced element - pathItemElement.forEach((value, key, item) => { - mergedResult.remove(key.toValue()); - mergedResult.content.push(item); + pathItemElement.forEach((valueElement, keyElement, item) => { + mergedPathItemElement.remove(keyElement.toValue()); + mergedPathItemElement.content.push(item); }); - mergedResult.remove('$ref'); + mergedPathItemElement.remove('$ref'); // annotate referenced element with info about original referencing element - mergedResult.setMetaProperty('ref-fields', { + mergedPathItemElement.setMetaProperty('ref-fields', { $ref: pathItemElement.$ref?.toValue(), }); // annotate referenced element with info about origin - mergedResult.setMetaProperty('ref-origin', reference.uri); + mergedPathItemElement.setMetaProperty('ref-origin', reference.uri); // apply meta patches if (this.allowMetaPatches) { // apply meta patch only when not already applied - if (typeof mergedResult.get('$$ref') === 'undefined') { - const absoluteURI = url.resolve(reference.uri, pathItemElement.$ref?.toValue()); - mergedResult.set('$$ref', absoluteURI); + if (typeof mergedPathItemElement.get('$$ref') === 'undefined') { + const baseURI = url.resolve(retrievalURI, $refBaseURI); + mergedPathItemElement.set('$$ref', baseURI); } } // transclude referencing element with merged referenced element - return mergedResult; + return mergedPathItemElement; }, async SchemaElement(referencingElement, key, parent, path, ancestors) { - // compute full ancestors lineage - const ancestorsLineage = [...this.ancestors, ...ancestors]; + const [ancestorsLineage, directAncestors] = this.toAncestorLineage(ancestors); // skip current referencing schema as $ref keyword was not defined if (!isStringElement(referencingElement.$ref)) { @@ -242,9 +313,14 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c return undefined; } - // detect possible cycle and avoid it - if (ancestorsLineage.includes(referencingElement)) { - // skip processing this schema but all it's child schemas + // skip already identified cycled schemas + if (includesClasses(['cycle'], referencingElement.$ref)) { + return false; + } + + // detect possible cycle in traversal and avoid it + if (ancestorsLineage.some((ancs) => ancs.has(referencingElement))) { + // skip processing this schema and all it's child schemas return false; } @@ -275,7 +351,6 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c const selector = $refBaseURI; referencedElement = uriEvaluate( selector, - // @ts-ignore maybeRefractToSchemaElement(reference.value.result) ); } else { @@ -284,7 +359,6 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c retrievalURI = reference.uri; const selector = uriToPointer($refBaseURI); referencedElement = maybeRefractToSchemaElement( - // @ts-ignore jsonPointerEvaluate(selector, reference.value.result) ); } @@ -301,7 +375,6 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c const selector = uriToAnchor($refBaseURI); referencedElement = $anchorEvaluate( selector, - // @ts-ignore maybeRefractToSchemaElement(reference.value.result) ); } else { @@ -310,7 +383,6 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c retrievalURI = reference.uri; const selector = uriToPointer($refBaseURI); referencedElement = maybeRefractToSchemaElement( - // @ts-ignore jsonPointerEvaluate(selector, reference.value.result) ); } @@ -332,10 +404,10 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c } // append referencing schema to ancestors lineage - ancestorsLineage.push(referencingElement); + directAncestors.add(referencingElement); // dive deep into the fragment - const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({ + const mergeVisitor = OpenApi3_1SwaggerClientDereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], @@ -344,11 +416,14 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c allowMetaPatches: this.allowMetaPatches, ancestors: ancestorsLineage, }); - referencedElement = await visitAsync(referencedElement, visitor, { + referencedElement = await visitAsync(referencedElement, mergeVisitor, { keyMap, nodeTypeGetter: getNodeType, }); + // remove referencing schema from ancestors lineage + directAncestors.delete(referencingElement); + this.indirections.pop(); if (isBooleanJsonSchemaElement(referencedElement)) { @@ -365,23 +440,27 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = OpenApi3_1DereferenceVisitor.c } // useCircularStructures option processing - const hasCycle = referencedElement.content.some((memberElement) => - ancestorsLineage.includes(memberElement) - ); - if (hasCycle && !this.useCircularStructures) { - if (url.isHttpUrl(retrievalURI) || url.isFileSystemPath(retrievalURI)) { - // make the referencing URL or file system path absolute - const baseURI = url.resolve(retrievalURI, $refBaseURI); - referencingElement.set('$ref', baseURI); + if (!this.useCircularStructures) { + const hasCycles = ancestorsLineage.some((ancs) => ancs.has(referencedElement)); + if (hasCycles) { + if (url.isHttpUrl(retrievalURI) || url.isFileSystemPath(retrievalURI)) { + // make the referencing URL or file system path absolute + const baseURI = url.resolve(retrievalURI, $refBaseURI); + const cycledSchemaElement = new SchemaElement( + { $ref: baseURI }, + referencingElement.meta.clone(), + referencingElement.attributes.clone() + ); + cycledSchemaElement.get('$ref').classes.push('cycle'); + return cycledSchemaElement; + } + // skip processing this schema but traverse all it's child schemas + return false; } - - // skip processing this schema but traverse all it's child schemas - return undefined; } // Schema Object - merge keywords from referenced schema with referencing schema const mergedSchemaElement = new SchemaElement( - // @ts-ignore [...referencedElement.content], referencedElement.meta.clone(), referencedElement.attributes.clone() diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/reference-object/index.js b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/reference-object/index.js index 0f06227f8..5b73251d0 100644 --- a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/reference-object/index.js +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/reference-object/index.js @@ -421,7 +421,6 @@ describe('dereference', () => { const parseResult = await parse(uri, { parse: { mediaType: mediaTypes.latest('json') }, }); - // @ts-ignore const referenceElement = parseResult.api?.components.parameters.get('externalRef'); const refSet = ReferenceSet(); const rootFileReference = Reference({ uri, value: parseResult }); diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/dereferenced.json new file mode 100644 index 000000000..c6da2ad51 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/dereferenced.json @@ -0,0 +1,34 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$anchor": "user-profile", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "parent": { + "$ref": "/home/smartbear/ex.json#user-profile" + } + } + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/ex.json new file mode 100644 index 000000000..e48c98336 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/ex.json @@ -0,0 +1,18 @@ +{ + "$defs": { + "UserProfile": { + "$anchor": "user-profile", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "parent": { + "$ref": "#user-profile" + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/root.json new file mode 100644 index 000000000..5f5527668 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-external-circular-structures/root.json @@ -0,0 +1,21 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$ref": "./ex.json#user-profile" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-internal-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-internal-circular-structures/dereferenced.json new file mode 100644 index 000000000..3ecad4938 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-internal-circular-structures/dereferenced.json @@ -0,0 +1,48 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$anchor": "user-profile", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "parent": { + "$ref": "/home/smartbear/root.json#user-profile" + } + } + } + } + }, + "UserProfile": { + "$anchor": "user-profile", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "parent": { + "$ref": "/home/smartbear/root.json#user-profile" + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-internal-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-internal-circular-structures/root.json new file mode 100644 index 000000000..371d7c2d0 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$anchor-internal-circular-structures/root.json @@ -0,0 +1,35 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$ref": "#user-profile" + } + } + }, + "UserProfile": { + "$anchor": "user-profile", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "parent": { + "$ref": "#user-profile" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/dereferenced.json new file mode 100644 index 000000000..25c744379 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/dereferenced.json @@ -0,0 +1,32 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$id": "./nested/", + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "user": { + "$ref": "/home/smartbear/root.json#/components/schemas/User" + } + } + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/nested/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/nested/ex.json new file mode 100644 index 000000000..c1daea440 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/nested/ex.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "user": { + "$ref": "../root.json#/components/schemas/User" + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/root.json new file mode 100644 index 000000000..2c9fd869f --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-direct-circular-structures/root.json @@ -0,0 +1,22 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$id": "./nested/", + "$ref": "./ex.json" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/dereferenced.json new file mode 100644 index 000000000..08bf3edfe --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/dereferenced.json @@ -0,0 +1,32 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "$id": "./nested/", + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "user": { + "$ref": "/home/smartbear/root.json#/components/schemas/User" + } + } + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/nested/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/nested/ex.json new file mode 100644 index 000000000..c1daea440 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/nested/ex.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "user": { + "$ref": "../root.json#/components/schemas/User" + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/root.json new file mode 100644 index 000000000..4c3fa339c --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-enclosing-circular-structures/root.json @@ -0,0 +1,22 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "$id": "./nested/", + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$ref": "./ex.json" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/dereferenced.json new file mode 100644 index 000000000..25c744379 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/dereferenced.json @@ -0,0 +1,32 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$id": "./nested/", + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "user": { + "$ref": "/home/smartbear/root.json#/components/schemas/User" + } + } + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/nested/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/nested/ex.json new file mode 100644 index 000000000..d5a6ae02c --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/nested/ex.json @@ -0,0 +1,8 @@ +{ + "$defs": { + "UserProfile": { + "$id": "./nested/", + "$ref": "./ex.json" + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/nested/nested/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/nested/nested/ex.json new file mode 100644 index 000000000..20e881216 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/nested/nested/ex.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "user": { + "$ref": "../../root.json#/components/schemas/User" + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/root.json new file mode 100644 index 000000000..ede427d60 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$id-uri-external-circular-structures/root.json @@ -0,0 +1,21 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + }, + "profile": { + "$ref": "./nested/ex.json#/$defs/UserProfile" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-circular-structures/dereferenced.json new file mode 100644 index 000000000..a13a1ef00 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-circular-structures/dereferenced.json @@ -0,0 +1,33 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "$id": "https://swagger.io/schemas/user", + "type": "object", + "properties": { + "profile": { + "$id": "https://swagger.io/schemas/user-profile", + "type": "object", + "properties": { + "user": { + "$ref": "https://swagger.io/schemas/user" + } + } + } + } + }, + "UserProfile": { + "$id": "https://swagger.io/schemas/user-profile", + "type": "object", + "properties": { + "user": { + "$ref": "https://swagger.io/schemas/user" + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-circular-structures/root.json new file mode 100644 index 000000000..4c08ee6da --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-circular-structures/root.json @@ -0,0 +1,25 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "$id": "https://swagger.io/schemas/user", + "type": "object", + "properties": { + "profile": { + "$ref": "https://swagger.io/schemas/user-profile" + } + } + }, + "UserProfile": { + "$id": "https://swagger.io/schemas/user-profile", + "type": "object", + "properties": { + "user": { + "$ref": "https://swagger.io/schemas/user" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-pointer-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-pointer-circular-structures/dereferenced.json new file mode 100644 index 000000000..d5c2360f0 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-pointer-circular-structures/dereferenced.json @@ -0,0 +1,30 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "profile": { + "type": "object", + "properties": { + "user": { + "$ref": "/home/smartbear/root.json#/components/schemas/User" + } + } + } + } + }, + "UserProfile": { + "type": "object", + "properties": { + "user": { + "$ref": "/home/smartbear/root.json#/components/schemas/User" + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-pointer-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-pointer-circular-structures/root.json new file mode 100644 index 000000000..ea483202c --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-pointer-circular-structures/root.json @@ -0,0 +1,23 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "profile": { + "$ref": "#/components/schemas/UserProfile" + } + } + }, + "UserProfile": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-relative-reference-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-relative-reference-circular-structures/dereferenced.json new file mode 100644 index 000000000..79daa8162 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-relative-reference-circular-structures/dereferenced.json @@ -0,0 +1,33 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "$id": "https://swagger.io/schemas/user", + "properties": { + "profile": { + "$id": "https://swagger.io/schemas/user-profile", + "type": "object", + "properties": { + "user": { + "$ref": "https://swagger.io/schemas/user" + } + } + } + } + }, + "UserProfile": { + "$id": "https://swagger.io/schemas/user-profile", + "type": "object", + "properties": { + "user": { + "$ref": "https://swagger.io/schemas/user" + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-relative-reference-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-relative-reference-circular-structures/root.json new file mode 100644 index 000000000..50ea31ef3 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-relative-reference-circular-structures/root.json @@ -0,0 +1,25 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "$id": "https://swagger.io/schemas/user", + "properties": { + "profile": { + "$ref": "/schemas/user-profile" + } + } + }, + "UserProfile": { + "$id": "https://swagger.io/schemas/user-profile", + "type": "object", + "properties": { + "user": { + "$ref": "/schemas/user" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/dereferenced.json new file mode 100644 index 000000000..01384313b --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/dereferenced.json @@ -0,0 +1,22 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "profile": { + "type": "object", + "properties": { + "user": { + "$ref": "/home/smartbear/root.json#/components/schemas/User" + } + } + } + } + } + } + } + } +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/ex.json new file mode 100644 index 000000000..080220b1e --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/ex.json @@ -0,0 +1,8 @@ +{ + "type": "object", + "properties": { + "user": { + "$ref": "./root.json#/components/schemas/User" + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/root.json new file mode 100644 index 000000000..145b97c62 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-url-resolvable-circular-structures/root.json @@ -0,0 +1,15 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "profile": { + "$ref": "./ex.json" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-urn-circular-structures/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-urn-circular-structures/dereferenced.json new file mode 100644 index 000000000..5bc23ccb6 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-urn-circular-structures/dereferenced.json @@ -0,0 +1,34 @@ +[ + { + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "$id": "urn:uuid:ff564b8a-7a87-4125-8c96-e9f123d6766f", + "type": "object", + "properties": { + "profile": { + "$id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", + "type": "object", + "properties": { + "user": { + "$ref": "urn:uuid:ff564b8a-7a87-4125-8c96-e9f123d6766f" + } + } + } + } + }, + "UserProfile": { + "$id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", + "type": "object", + "properties": { + "user": { + "$ref": "urn:uuid:ff564b8a-7a87-4125-8c96-e9f123d6766f" + } + } + } + } + } + } + +] diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-urn-circular-structures/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-urn-circular-structures/root.json new file mode 100644 index 000000000..0e1558e18 --- /dev/null +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/$ref-urn-circular-structures/root.json @@ -0,0 +1,25 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "$id": "urn:uuid:ff564b8a-7a87-4125-8c96-e9f123d6766f", + "type": "object", + "properties": { + "profile": { + "$ref": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f" + } + } + }, + "UserProfile": { + "$id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", + "type": "object", + "properties": { + "user": { + "$ref": "urn:uuid:ff564b8a-7a87-4125-8c96-e9f123d6766f" + } + } + } + } + } +} diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-circular-structures/dereferenced.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled/dereferenced.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-circular-structures/dereferenced.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled-http/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-circular-structures/ex.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled-http/ex.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-circular-structures/ex.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled-http/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-circular-structures/root.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled-http/root.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-circular-structures/root.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled-http/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-http-circular-structures/dereferenced.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled-http/dereferenced.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-http-circular-structures/dereferenced.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled/ex.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-http-circular-structures/ex.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled/ex.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-http-circular-structures/ex.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-http-circular-structures/root.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-disabled/root.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-external-http-circular-structures/root.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-circular-structures/dereferenced.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled/dereferenced.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-circular-structures/dereferenced.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled-http/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-circular-structures/root.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled-http/root.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-circular-structures/root.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled-http/dereferenced.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-http-circular-structures/dereferenced.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled-http/dereferenced.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-http-circular-structures/dereferenced.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled/root.json b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-http-circular-structures/root.json similarity index 100% rename from test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-disabled/root.json rename to test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/__fixtures__/cycle-internal-http-circular-structures/root.json diff --git a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/index.js b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/index.js index 6f1fa954e..72d8d62de 100644 --- a/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/index.js +++ b/test/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/schema-object/index.js @@ -116,7 +116,7 @@ describe('dereference', () => { describe('and useCircularStructures=false', () => { test('should avoid cycles by skipping transclusion', async () => { - const fixturePath = path.join(rootFixturePath, 'cycle-internal-disabled'); + const fixturePath = path.join(rootFixturePath, 'cycle-internal-circular-structures'); const rootFilePath = path.join(fixturePath, 'root.json'); const refSet = await resolve(rootFilePath, { parse: { mediaType: mediaTypes.latest('json') }, @@ -138,7 +138,10 @@ describe('dereference', () => { describe('and using HTTP protocol', () => { test('should make JSON Pointer absolute', async () => { - const fixturePath = path.join(rootFixturePath, 'cycle-internal-disabled-http'); + const fixturePath = path.join( + rootFixturePath, + 'cycle-internal-http-circular-structures' + ); const dereferenceThunk = async () => { const httpServer = globalThis.createHTTPServer({ port: 8123, cwd: fixturePath }); @@ -190,7 +193,7 @@ describe('dereference', () => { describe('and useCircularStructures=false', () => { test('should avoid cycles by skipping transclusion', async () => { - const fixturePath = path.join(rootFixturePath, 'cycle-external-disabled'); + const fixturePath = path.join(rootFixturePath, 'cycle-external-circular-structures'); const rootFilePath = path.join(fixturePath, 'root.json'); const refSet = await resolve(rootFilePath, { parse: { mediaType: mediaTypes.latest('json') }, @@ -529,6 +532,30 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join(rootFixturePath, '$id-uri-direct-circular-structures'); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs[0].uri = '/home/smartbear/root.json'; + refSet.refs[1].uri = '/home/smartbear/nested/ex.json'; + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $id keyword defined in enclosing Schema Object', () => { @@ -566,6 +593,33 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$id-uri-enclosing-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs[0].uri = '/home/smartbear/root.json'; + refSet.refs[1].uri = '/home/smartbear/nested/ex.json'; + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $id keyword pointing to external files', () => { @@ -604,6 +658,34 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$id-uri-external-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs[0].uri = '/home/smartbear/root.json'; + refSet.refs[1].uri = '/home/smartbear/nested/ex.json'; + refSet.refs[2].uri = '/home/smartbear/nested/nested/ex.json'; + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with unresolvable $id values', () => { @@ -663,6 +745,28 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join(rootFixturePath, '$ref-url-circular-structures'); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $ref keyword containing relative references', () => { @@ -702,6 +806,31 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$ref-url-relative-reference-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $ref keyword containing URL and JSON Pointer fragment', () => { @@ -738,6 +867,32 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$ref-url-pointer-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs[0].uri = '/home/smartbear/root.json'; + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $ref keyword containing URL and $anchor', () => { @@ -811,6 +966,33 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$ref-url-resolvable-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs[0].uri = '/home/smartbear/root.json'; + refSet.refs[1].uri = '/home/smartbear/ex.json'; + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $ref keyword containing unresolvable URL', () => { @@ -866,6 +1048,28 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test.only('should dereference', async () => { + const fixturePath = path.join(rootFixturePath, '$ref-urn-circular-structures'); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + refSet, + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $ref keyword containing Uniform Resource Name and JSON Pointer fragment', () => { @@ -971,6 +1175,32 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$anchor-internal-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs[0].uri = '/home/smartbear/root.json'; + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + refSet, + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with $anchor keyword pointing to external schema', () => { @@ -1009,6 +1239,34 @@ describe('dereference', () => { expect(toValue(actual)).toEqual(expected); }); }); + + describe('and useCircularStructures=false', () => { + test('should dereference', async () => { + const fixturePath = path.join( + rootFixturePath, + '$anchor-external-circular-structures' + ); + const rootFilePath = path.join(fixturePath, 'root.json'); + const refSet = await resolve(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + refSet.refs.forEach((ref) => { + ref.uri = `/home/smartbear/${path.basename(ref.uri)}`; + }); + const actual = await dereference(refSet.refs[0].uri, { + parse: { mediaType: mediaTypes.latest('json') }, + dereference: { + strategies: [ + OpenApi3_1SwaggerClientDereferenceStrategy({ useCircularStructures: false }), + ], + refSet, + }, + }); + const expected = globalThis.loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + expect(toValue(actual)).toEqual(expected); + }); + }); }); describe('given Schema Objects with various document boundaries', () => { diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/circular-structures.json b/test/resolver/strategies/openapi-3-1/__fixtures__/circular-structures.json new file mode 100644 index 000000000..ea483202c --- /dev/null +++ b/test/resolver/strategies/openapi-3-1/__fixtures__/circular-structures.json @@ -0,0 +1,23 @@ +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "profile": { + "$ref": "#/components/schemas/UserProfile" + } + } + }, + "UserProfile": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + } + } + } + } + } +} diff --git a/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap b/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap index 6f91e60cb..0d9b77e9a 100644 --- a/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap +++ b/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap @@ -309,6 +309,385 @@ exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition and allow } `; +exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition and allowMetaPatches=true should resolve 2`] = ` +{ + "errors": [], + "spec": { + "$$normalized": true, + "components": { + "schemas": { + "Error": { + "properties": { + "code": { + "format": "int32", + "type": "integer", + }, + "message": { + "type": "string", + }, + }, + "required": [ + "code", + "message", + ], + "type": "object", + }, + "Pet": { + "properties": { + "id": { + "format": "int64", + "type": "integer", + }, + "name": { + "type": "string", + }, + "tag": { + "type": "string", + }, + }, + "required": [ + "id", + "name", + ], + "type": "object", + }, + "Pets": { + "items": { + "properties": { + "id": { + "format": "int64", + "type": "integer", + }, + "name": { + "type": "string", + }, + "tag": { + "type": "string", + }, + }, + "required": [ + "id", + "name", + ], + "type": "object", + }, + "maxItems": 100, + "type": "array", + }, + }, + }, + "info": { + "license": { + "name": "MIT", + }, + "title": "Swagger Petstore", + "version": "1.0.0", + }, + "openapi": "3.1.0", + "paths": { + "/pets": { + "get": { + "operationId": "listPets", + "parameters": [ + { + "description": "How many items to return at one time (max 100)", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int32", + "maximum": 100, + "type": "integer", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "properties": { + "id": { + "format": "int64", + "type": "integer", + }, + "name": { + "type": "string", + }, + "tag": { + "type": "string", + }, + }, + "required": [ + "id", + "name", + ], + "type": "object", + }, + "maxItems": 100, + "type": "array", + }, + }, + }, + "description": "A paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string", + }, + }, + }, + }, + "default": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "format": "int32", + "type": "integer", + }, + "message": { + "type": "string", + }, + }, + "required": [ + "code", + "message", + ], + "type": "object", + }, + }, + }, + "description": "unexpected error", + }, + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1", + }, + ], + "summary": "List all pets", + "tags": [ + "pets", + ], + }, + "post": { + "operationId": "createPets", + "responses": { + "201": { + "description": "Null response", + }, + "default": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "format": "int32", + "type": "integer", + }, + "message": { + "type": "string", + }, + }, + "required": [ + "code", + "message", + ], + "type": "object", + }, + }, + }, + "description": "unexpected error", + }, + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1", + }, + ], + "summary": "Create a pet", + "tags": [ + "pets", + ], + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1", + }, + ], + }, + "/pets/{petId}": { + "get": { + "operationId": "showPetById", + "parameters": [ + { + "description": "The id of the pet to retrieve", + "in": "path", + "name": "petId", + "required": true, + "schema": { + "type": "string", + }, + }, + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "format": "int64", + "type": "integer", + }, + "name": { + "type": "string", + }, + "tag": { + "type": "string", + }, + }, + "required": [ + "id", + "name", + ], + "type": "object", + }, + }, + }, + "description": "Expected response to a valid request", + }, + "default": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "format": "int32", + "type": "integer", + }, + "message": { + "type": "string", + }, + }, + "required": [ + "code", + "message", + ], + "type": "object", + }, + }, + }, + "description": "unexpected error", + }, + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1", + }, + ], + "summary": "Info for a specific pet", + "tags": [ + "pets", + ], + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1", + }, + ], + }, + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1", + }, + ], + }, +} +`; + +exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition and useCircularStructures=false should resolve 1`] = ` +{ + "errors": [], + "spec": { + "$$normalized": true, + "components": { + "schemas": { + "User": { + "properties": { + "profile": { + "properties": { + "user": { + "$ref": "https://example.com/circular-structures.json#/components/schemas/User", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "UserProfile": { + "properties": { + "user": { + "$ref": "https://example.com/circular-structures.json#/components/schemas/User", + }, + }, + "type": "object", + }, + }, + }, + "openapi": "3.1.0", + }, +} +`; + +exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition and useCircularStructures=true should resolve 1`] = ` +{ + "errors": [], + "spec": { + "$$normalized": true, + "components": { + "schemas": { + "User": { + "properties": { + "profile": { + "properties": { + "user": { + "properties": [Circular], + "type": "object", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "UserProfile": { + "properties": { + "user": { + "properties": { + "profile": { + "properties": [Circular], + "type": "object", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + }, + }, + "openapi": "3.1.0", + }, +} +`; + exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition should resolve 1`] = ` { "errors": [], diff --git a/test/resolver/strategies/openapi-3-1/index.js b/test/resolver/strategies/openapi-3-1/index.js index 342b89152..0a7ad60c3 100644 --- a/test/resolver/strategies/openapi-3-1/index.js +++ b/test/resolver/strategies/openapi-3-1/index.js @@ -18,6 +18,7 @@ describe('resolve', () => { fetchMock.get(url, response, { repeat: 1 }); const resolvedSpec = await Swagger.resolve({ url: 'https://example.com/petstore.json', + allowMetaPatches: false, }); expect(resolvedSpec).toMatchSnapshot(); @@ -42,6 +43,60 @@ describe('resolve', () => { fetchMock.restore(); }); }); + + describe('and allowMetaPatches=true', () => { + test('should resolve', async () => { + const url = 'https://example.com/petstore.json'; + const response = new Response( + globalThis.loadFile(path.join(fixturePath, 'petstore.json')) + ); + fetchMock.get(url, response, { repeat: 1 }); + const resolvedSpec = await Swagger.resolve({ + url: 'https://example.com/petstore.json', + allowMetaPatches: false, + }); + + expect(resolvedSpec).toMatchSnapshot(); + + fetchMock.restore(); + }); + }); + + describe('and useCircularStructures=true', () => { + test('should resolve', async () => { + const url = 'https://example.com/circular-structures.json'; + const response = new Response( + globalThis.loadFile(path.join(fixturePath, 'circular-structures.json')) + ); + fetchMock.get(url, response, { repeat: 1 }); + const resolvedSpec = await Swagger.resolve({ + url: 'https://example.com/circular-structures.json', + useCircularStructures: true, + }); + + expect(resolvedSpec).toMatchSnapshot(); + + fetchMock.restore(); + }); + }); + + describe('and useCircularStructures=false', () => { + test('should resolve', async () => { + const url = 'https://example.com/circular-structures.json'; + const response = new Response( + globalThis.loadFile(path.join(fixturePath, 'circular-structures.json')) + ); + fetchMock.get(url, response, { repeat: 1 }); + const resolvedSpec = await Swagger.resolve({ + url: 'https://example.com/circular-structures.json', + useCircularStructures: false, + }); + + expect(resolvedSpec).toMatchSnapshot(); + + fetchMock.restore(); + }); + }); }); }); });