Skip to content

Commit

Permalink
feat(reference): add support for allowMetaPatches option (#2337)
Browse files Browse the repository at this point in the history
This affects OpenAPI 3.1 swagger-client dereference strategy.

Refs #2336
  • Loading branch information
char0n authored Dec 6, 2022
1 parent d60d217 commit 22c5c7c
Show file tree
Hide file tree
Showing 26 changed files with 460 additions and 3 deletions.
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

0 comments on commit 22c5c7c

Please sign in to comment.