diff --git a/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts b/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts index 80d682002b..aeabc39e65 100644 --- a/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts +++ b/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts @@ -895,6 +895,11 @@ class OpenAPI3_0DereferenceVisitor { return undefined; } + // ignore already dereferenced ExampleElement; value field was transcluded from externalValue field + if (exampleElement.value?.meta.hasKey('ref-origin')) { + return undefined; + } + // value and externalValue fields are mutually exclusive if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) { const error = new ApiDOMError( diff --git a/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts b/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts index 5120cc6283..9aec92112c 100644 --- a/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts +++ b/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts @@ -972,6 +972,11 @@ class OpenAPI3_1DereferenceVisitor { return undefined; } + // ignore already dereferenced ExampleElement; value field was transcluded from externalValue field + if (exampleElement.value?.meta.hasKey('ref-origin')) { + return undefined; + } + // value and externalValue fields are mutually exclusive if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) { const error = new ApiDOMError( diff --git a/packages/apidom-reference/src/dereference/strategies/openapi-3-2/visitor.ts b/packages/apidom-reference/src/dereference/strategies/openapi-3-2/visitor.ts index aea7e108a4..2ecc04659e 100644 --- a/packages/apidom-reference/src/dereference/strategies/openapi-3-2/visitor.ts +++ b/packages/apidom-reference/src/dereference/strategies/openapi-3-2/visitor.ts @@ -1095,6 +1095,11 @@ class OpenAPI3_2DereferenceVisitor { return undefined; } + // ignore already dereferenced ExampleElement; value field was transcluded from externalValue field + if (exampleElement.value?.meta.hasKey('ref-origin')) { + return undefined; + } + // value and externalValue fields are mutually exclusive if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) { const error = new ApiDOMError( diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/dereferenced.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/dereferenced.json new file mode 100644 index 0000000000..8de71d1f8e --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/dereferenced.json @@ -0,0 +1,30 @@ +[ + { + "openapi": "3.0.4", + "paths": { + "/path1": { + "post": { + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "examples": { + "example1": { + "description": "example1 description", + "externalValue": "./ex.json", + "value": { + "prop1": "value1", + "prop2": "value2" + } + } + } + } + } + } + } + } + } + } + } +] diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/entry.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/entry.json new file mode 100644 index 0000000000..818e5ac739 --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/entry.json @@ -0,0 +1,8 @@ +{ + "openapi": "3.0.4", + "paths": { + "/path1": { + "$ref": "./path-item.json" + } + } +} diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/ex.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/ex.json new file mode 100644 index 0000000000..6fb8855f8a --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/ex.json @@ -0,0 +1,4 @@ +{ + "prop1": "value1", + "prop2": "value2" +} diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/path-item.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/path-item.json new file mode 100644 index 0000000000..f738471ac3 --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/fixtures/external-value-path-item-ref/path-item.json @@ -0,0 +1,19 @@ +{ + "post": { + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "examples": { + "example1": { + "description": "example1 description", + "externalValue": "./ex.json" + } + } + } + } + } + } + } +} diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/index.ts b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/index.ts index 42a8a9b43c..dc98e49c76 100644 --- a/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/index.ts +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-0/example-object/index.ts @@ -160,6 +160,23 @@ describe('dereference', function () { }); }); + context( + 'given Example Object is inside externally referenced Path Item Object', + function () { + const fixturePath = path.join(entryFixturePath, 'external-value-path-item-ref'); + + specify('should dereference', async function () { + const entryFilePath = path.join(fixturePath, 'entry.json'); + const actual = await dereference(entryFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + const expected = loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + assert.deepEqual(toValue(actual), expected); + }); + }, + ); + context('given both value and externalValue fields are defined', function () { const fixturePath = path.join(entryFixturePath, 'external-value-value-both-defined'); diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/dereferenced.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/dereferenced.json new file mode 100644 index 0000000000..f67033bacb --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/dereferenced.json @@ -0,0 +1,30 @@ +[ + { + "openapi": "3.1.0", + "paths": { + "/path1": { + "post": { + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "examples": { + "example1": { + "description": "example1 description", + "externalValue": "./ex.json", + "value": { + "prop1": "value1", + "prop2": "value2" + } + } + } + } + } + } + } + } + } + } + } +] diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/ex.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/ex.json new file mode 100644 index 0000000000..6fb8855f8a --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/ex.json @@ -0,0 +1,4 @@ +{ + "prop1": "value1", + "prop2": "value2" +} diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/path-item.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/path-item.json new file mode 100644 index 0000000000..f738471ac3 --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/path-item.json @@ -0,0 +1,19 @@ +{ + "post": { + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "examples": { + "example1": { + "description": "example1 description", + "externalValue": "./ex.json" + } + } + } + } + } + } + } +} diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/root.json b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/root.json new file mode 100644 index 0000000000..9902b76e7b --- /dev/null +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/fixtures/external-value-path-item-ref/root.json @@ -0,0 +1,8 @@ +{ + "openapi": "3.1.0", + "paths": { + "/path1": { + "$ref": "./path-item.json" + } + } +} diff --git a/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/index.ts b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/index.ts index f4a619a532..85c389223d 100644 --- a/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/index.ts +++ b/packages/apidom-reference/test/dereference/strategies/openapi-3-1/example-object/index.ts @@ -160,6 +160,23 @@ describe('dereference', function () { }); }); + context( + 'given Example Object is inside externally referenced Path Item Object', + function () { + const fixturePath = path.join(rootFixturePath, 'external-value-path-item-ref'); + + specify('should dereference', async function () { + const rootFilePath = path.join(fixturePath, 'root.json'); + const actual = await dereference(rootFilePath, { + parse: { mediaType: mediaTypes.latest('json') }, + }); + const expected = loadJsonFile(path.join(fixturePath, 'dereferenced.json')); + + assert.deepEqual(toValue(actual), expected); + }); + }, + ); + context('given both value and externalValue fields are defined', function () { const fixturePath = path.join(rootFixturePath, 'external-value-value-both-defined');