Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,27 @@ const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')];
// eslint-disable-next-line @typescript-eslint/naming-convention
interface IOpenApi3_1SwaggerClientDereferenceStrategy extends IDereferenceStrategy {
useCircularStructures: boolean;
allowMetaPatches: boolean;
}

// eslint-disable-next-line @typescript-eslint/naming-convention
const OpenApi3_1SwaggerClientDereferenceStrategy: stampit.Stamp<IOpenApi3_1SwaggerClientDereferenceStrategy> =
stampit(DereferenceStrategy, {
props: {
useCircularStructures: true,
allowMetaPatches: false,
},
init(
this: IOpenApi3_1SwaggerClientDereferenceStrategy,
{ useCircularStructures = this.useCircularStructures } = {},
{
useCircularStructures = this.useCircularStructures,
allowMetaPatches = this.allowMetaPatches,
} = {},
) {
// @ts-ignore
this.name = 'openapi-3-1-swagger-client';
this.useCircularStructures = useCircularStructures;
this.allowMetaPatches = allowMetaPatches;
},
methods: {
canDereference(file: IFile): boolean {
Expand Down Expand Up @@ -69,6 +75,7 @@ const OpenApi3_1SwaggerClientDereferenceStrategy: stampit.Stamp<IOpenApi3_1Swagg
namespace,
options,
useCircularStructures: this.useCircularStructures,
allowMetaPatches: this.allowMetaPatches,
});
const dereferencedElement = await visitAsync(refSet.rootRef.value, visitor, {
keyMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import stampit from 'stampit';
import { hasIn, pathSatisfies, propEq, none } from 'ramda';
import { isUndefined, isNotUndefined } from 'ramda-adjunct';
import {
isObjectElement,
ObjectElement,
isPrimitiveElement,
isStringElement,
visit,
Expand Down Expand Up @@ -65,13 +67,15 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
namespace,
options,
useCircularStructures,
allowMetaPatches,
}) {
this.indirections = indirections;
this.visited = visited;
this.namespace = namespace;
this.reference = reference;
this.options = options;
this.useCircularStructures = useCircularStructures;
this.allowMetaPatches = allowMetaPatches;
},
methods: {
toBaseURI(uri: string): string {
Expand Down Expand Up @@ -160,6 +164,7 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
namespace: this.namespace,
indirections: [...this.indirections],
options: this.options,
allowMetaPatches: this.allowMetaPatches,
});
fragment = await visitAsync(fragment, visitor, { keyMap, nodeTypeGetter: getNodeType });

Expand Down Expand Up @@ -188,6 +193,19 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
fragment.summary = referenceElement.summary;
}

// apply meta patches
if (this.allowMetaPatches && isObjectElement(fragment)) {
const objectFragment = fragment as ObjectElement;
// apply meta patch only when not already applied
if (typeof objectFragment.get('$$ref') === 'undefined') {
const absoluteJSONPointerURL = url.resolve(
reference.uri,
referenceElement.$ref?.toValue(),
);
objectFragment.set('$$ref', absoluteJSONPointerURL);
}
}

this.indirections.pop();

// transclude the element for a fragment
Expand Down Expand Up @@ -238,6 +256,7 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
namespace: this.namespace,
indirections: [...this.indirections],
options: this.options,
allowMetaPatches: this.allowMetaPatches,
});
referencedElement = await visitAsync(referencedElement, visitor, {
keyMap,
Expand Down Expand Up @@ -267,6 +286,18 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
// annotate referenced element with info about origin
mergedResult.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 absoluteJSONPointerURL = url.resolve(
reference.uri,
pathItemElement.$ref?.toValue(),
);
mergedResult.set('$$ref', absoluteJSONPointerURL);
}
}

// transclude referencing element with merged referenced element
return mergedResult;
},
Expand Down Expand Up @@ -490,6 +521,7 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
options: this.options,
visited: this.visited,
useCircularStructures: this.useCircularStructures,
allowMetaPatches: this.allowMetaPatches,
});
referencedElement = await visitAsync(referencedElement, visitor, {
keyMap,
Expand Down Expand Up @@ -530,6 +562,17 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = stampit({
});
// annotate fragment with info about origin
mergedResult.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 absoluteJSONPointerURL = url.resolve(
reference.uri,
referencingElement.$ref?.toValue(),
);
mergedResult.set('$$ref', absoluteJSONPointerURL);
}
}

// transclude referencing element with merged referenced element
return mergedResult;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"openapi": "3.1.0",
"components": {
"pathItems": {
"externalRef": {
"description": "external ref",
"get": {},
"$$ref": "http://localhost:8123/ex3.json#/externalPathItem"
}
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"indirection": {
"$ref": "./ex2.json#/indirection"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"indirection": {
"$ref": "./ex3.json#/externalPathItem"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"externalPathItem": {
"description": "this is path item stored in external file",
"get": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"openapi": "3.1.0",
"components": {
"pathItems": {
"externalRef": {
"$ref": "./ex1.json#/indirection",
"description": "external ref"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"openapi": "3.1.0",
"components": {
"pathItems": {
"pathItem1": {
"summary": "path item summary",
"description": "path item description",
"$$ref": "http://localhost:8123/root.json#/components/pathItems/pathItem3"
},
"pathItem2": {
"summary": "path item summary",
"description": "path item description",
"$$ref": "http://localhost:8123/root.json#/components/pathItems/pathItem3"
},
"pathItem3": {
"summary": "path item summary",
"description": "path item description"
}
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"openapi": "3.1.0",
"components": {
"pathItems": {
"pathItem1": {
"$ref": "#/components/pathItems/pathItem2"
},
"pathItem2": {
"$ref": "#/components/pathItems/pathItem3"
},
"pathItem3": {
"summary": "path item summary",
"description": "path item description"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { assert } from 'chai';
import { toValue } from '@swagger-api/apidom-core';
import { mediaTypes } from '@swagger-api/apidom-ns-openapi-3-1';

import { loadJsonFile } from '../../../../helpers';
import { createHTTPServer, loadJsonFile } from '../../../../helpers';
import { dereference } from '../../../../../src';
import { DereferenceError, MaximumDereferenceDepthError } from '../../../../../src/util/errors';
import * as bootstrap from '../bootstrap';
import OpenApi3_1SwaggerClientDereferenceStrategy from '../../../../../src/dereference/strategies/openapi-3-1-swagger-client';

const rootFixturePath = path.join(__dirname, 'fixtures');

Expand Down Expand Up @@ -164,6 +165,58 @@ describe('dereference', function () {
});
});

context('given $ref field pointing internally', function () {
context('and allowMetaPatches=true', function () {
specify('should dereference', async function () {
let httpServer: any;

try {
const fixturePath = path.join(rootFixturePath, 'meta-patches-internal');
httpServer = createHTTPServer({ port: 8123, cwd: fixturePath });
const actual = await dereference('http://localhost:8123/root.json', {
parse: { mediaType: mediaTypes.latest('json') },
dereference: {
strategies: [
OpenApi3_1SwaggerClientDereferenceStrategy({ allowMetaPatches: true }),
],
},
});
const expected = loadJsonFile(path.join(fixturePath, 'dereferenced.json'));

assert.deepEqual(toValue(actual), expected);
} finally {
httpServer?.terminate();
}
});
});
});

context('given $ref field pointing externally', function () {
context('and allowMetaPatches=true', function () {
specify('should dereference', async function () {
let httpServer: any;

try {
const fixturePath = path.join(rootFixturePath, 'meta-patches-external');
httpServer = createHTTPServer({ port: 8123, cwd: fixturePath });
const actual = await dereference('http://localhost:8123/root.json', {
parse: { mediaType: mediaTypes.latest('json') },
dereference: {
strategies: [
OpenApi3_1SwaggerClientDereferenceStrategy({ allowMetaPatches: true }),
],
},
});
const expected = loadJsonFile(path.join(fixturePath, 'dereferenced.json'));

assert.deepEqual(toValue(actual), expected);
} finally {
httpServer?.terminate();
}
});
});
});

context('given $ref field with invalid JSON Pointer', function () {
const fixturePath = path.join(rootFixturePath, 'invalid-pointer');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"openapi": "3.1.0",
"components": {
"parameters": {
"externalRef": {
"name": "externalParameter",
"in": "query",
"description": "external ref",
"required": true,
"$$ref": "http://localhost:8123/ex3.json#/externalParameter"
}
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"indirection": {
"$ref": "./ex2.json#/indirection"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"indirection": {
"$ref": "./ex3.json#/externalParameter"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"externalParameter": {
"name": "externalParameter",
"in": "query",
"description": "this is parameter stored in external file",
"required": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"openapi": "3.1.0",
"components": {
"parameters": {
"externalRef": {
"$ref": "./ex1.json#/indirection",
"description": "external ref"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[
{
"openapi": "3.1.0",
"components": {
"parameters": {
"param1": {
"name": "offset",
"in": "query",
"required": true,
"$$ref": "http://localhost:8123/root.json#/components/parameters/param3"
},
"param2": {
"name": "offset",
"in": "query",
"required": true,
"$$ref": "http://localhost:8123/root.json#/components/parameters/param3"
},
"param3": {
"name": "offset",
"in": "query",
"required": true
}
}
}
}

]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"openapi": "3.1.0",
"components": {
"parameters": {
"param1": {
"$ref": "#/components/parameters/param2"
},
"param2": {
"$ref": "#/components/parameters/param3"
},
"param3": {
"name": "offset",
"in": "query",
"required": true
}
}
}
}
Loading