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 @@ -9,12 +9,14 @@ import {
keyMap,
ReferenceElement,
PathItemElement,
LinkElement,
SchemaElement,
isReferenceElementExternal,
isSchemaElementExternal,
isSchemaElement,
isPathItemElement,
isPathItemElementExternal,
isLinkElementExternal,
} from 'apidom-ns-openapi-3-1';

import { Reference as IReference } from '../../../types';
Expand Down Expand Up @@ -134,6 +136,34 @@ const OpenApi3_1ResolveVisitor = stampit({
return undefined;
},

LinkElement(linkElement: LinkElement) {
// ignore LinkElement without operationRef or operationId field
if (!isStringElement(linkElement.operationRef) && !isStringElement(linkElement.operationId)) {
return undefined;
}

// ignore resolving external Path Item Elements
if (!this.options.resolve.external && isLinkElementExternal(linkElement)) {
return undefined;
}

// operationRef and operationId are mutually exclusive
if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
throw new Error('LinkElement operationRef and operationId are mutually exclusive.');
}

if (isLinkElementExternal(linkElement)) {
const uri = linkElement.operationRef.toValue();
const baseURI = this.toBaseURI(uri);

if (!has(baseURI, this.crawlingMap)) {
this.crawlingMap[baseURI] = this.toReference(uri);
}
}

return undefined;
},

SchemaElement(schemaElement: SchemaElement) {
/**
* Skip traversal for already visited schemas and all their child schemas.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"operation": {
"description": "description of operation"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"openapi": "3.1.0",
"components": {
"links": {
"link1": {
"operationRef": "./ex.json#/operation"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"openapi": "3.1.0",
"components": {
"links": {
"link1": {
"operationRef": "./ex.json",
"operationId": "op1"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"operation": {
"description": "description of operation"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"openapi": "3.1.0",
"components": {
"links": {
"link1": {
"operationRef": "./ex.json#/operation"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"openapi": "3.1.0",
"components": {
"links": {
"link1": {
"operationRef": "./ex.json#invalid-pointer"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"openapi": "3.1.0",
"components": {
"links": {
"link1": {
"operationRef": "./ex.json"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import path from 'path';
import { assert } from 'chai';

import { resolve } from '../../../../../src';
import { ResolverError } from '../../../../../src/util/errors';

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

describe('resolve', function () {
context('strategies', function () {
context('openapi-3-1', function () {
context('Link Object', function () {
context('given operationRef field', function () {
context('and with external JSON Pointer', function () {
const fixturePath = path.join(rootFixturePath, 'operation-ref-external');
const rootFilePath = path.join(fixturePath, 'root.json');

specify('should resolve', async function () {
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
});

assert.strictEqual(refSet.size, 2);
});
});
});

context('with external resolution disabled', function () {
const fixturePath = path.join(rootFixturePath, 'operation-ref-ignore-external');

specify('should not resolve', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
resolve: { external: false },
});

assert.strictEqual(refSet.size, 1);
});
});

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

specify('should resolve', async function () {
// external resolution of Link Object is not concerned with validity of JSON Pointer (if defined)
const rootFilePath = path.join(fixturePath, 'root.json');
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
});

assert.strictEqual(refSet.size, 2);
});
});

context('and with unresolvable URI', function () {
const fixturePath = path.join(rootFixturePath, 'operation-ref-unresolvable');

specify('should throw error', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');

try {
await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
});
assert.fail('should throw ResolverError');
} catch (e) {
assert.instanceOf(e, ResolverError);
}
});
});

context('given both operationRef and operationId fields are defined', function () {
const fixturePath = path.join(rootFixturePath, 'operation-ref-id-both-defined');

specify('should throw error', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');

try {
await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
});
assert.fail('should throw ResolverError');
} catch (e) {
assert.strictEqual(
e.cause.cause.message,
'LinkElement operationRef and operationId are mutually exclusive.',
);
assert.instanceOf(e, ResolverError);
}
});
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('resolve', function () {
context('given $ref field pointing internally and externally', function () {
const fixturePath = path.join(rootFixturePath, 'internal-external');

specify('should dereference', async function () {
specify('should resolve', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
Expand All @@ -40,7 +40,7 @@ describe('resolve', function () {
context('given external resolution disabled', function () {
const fixturePath = path.join(rootFixturePath, 'ignore-external');

specify('should dereference', async function () {
specify('should resolve', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
Expand All @@ -54,7 +54,7 @@ describe('resolve', function () {
context('given $ref field pointing to external indirections', function () {
const fixturePath = path.join(rootFixturePath, 'external-indirections');

specify('should dereference', async function () {
specify('should resolve', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ describe('resolve', function () {
function () {
const fixturePath = path.join(rootFixturePath, '$anchor-external');

specify('should dereference', async function () {
specify('should resolve', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');
const refSet = await resolve(rootFilePath, {
parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' },
Expand Down