From b9e93fa632941de68d610e02d1e658a4eb8be2b2 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Thu, 12 Aug 2021 16:44:56 -0700 Subject: [PATCH] fix: including refs when discriminator mappings are present (#480) --- __tests__/__datasets__/discriminators.json | 109 ++++++++++++++++++ ...get-parameters-as-json-schema.test.js.snap | 80 +++++++++++++ .../get-parameters-as-json-schema.test.js | 11 ++ src/lib/openapi-to-json-schema.js | 10 ++ 4 files changed, 210 insertions(+) create mode 100644 __tests__/__datasets__/discriminators.json diff --git a/__tests__/__datasets__/discriminators.json b/__tests__/__datasets__/discriminators.json new file mode 100644 index 00000000..11fb06eb --- /dev/null +++ b/__tests__/__datasets__/discriminators.json @@ -0,0 +1,109 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Discriminator support", + "description": "https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#discriminatorObject", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://httpbin.org" + } + ], + "paths": { + "/anything/discriminator-with-mapping": { + "patch": { + "operationId": "oneOfWithTopLevelDiscriminatorAndMapping", + "summary": "oneOf with discriminator and mapping", + "description": "Polymorphic `oneOf` schema with a top-level discriminator and a mapping definition.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/OptionOneNoDisc" + }, + { + "$ref": "#/components/schemas/OptionTwoNoDisc" + } + ], + "discriminator": { + "propertyName": "discrim", + "mapping": { + "Option One": "#/components/schemas/OptionOneNoDisc", + "Option Two": "#/components/schemas/OptionTwoNoDisc" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Updated" + } + } + } + }, + "/anything/discriminator-with-no-mapping": { + "patch": { + "operationId": "oneOfWithTopLevelDiscriminatorNoMapping", + "summary": "oneOf with top-level discriminator (no mapping)", + "description": "Polymorphic `oneOf` schema with a top-level discriminator and **no** mapping definition.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/OptionOneNoDisc" + }, + { + "$ref": "#/components/schemas/OptionTwoNoDisc" + } + ], + "discriminator": { + "propertyName": "discrim" + } + } + } + } + }, + "responses": { + "200": { + "description": "Updated" + } + } + } + } + }, + "components": { + "schemas": { + "OptionOneNoDisc": { + "type": "object", + "required": ["discrim"], + "properties": { + "discrim": { + "type": "string" + }, + "optionone": { + "type": "number" + } + } + }, + "OptionTwoNoDisc": { + "type": "object", + "required": ["discrim"], + "properties": { + "discrim": { + "type": "string" + }, + "optiontwo": { + "type": "string" + } + } + } + } + } +} diff --git a/__tests__/operation/__snapshots__/get-parameters-as-json-schema.test.js.snap b/__tests__/operation/__snapshots__/get-parameters-as-json-schema.test.js.snap index d4c8b425..7aa64efa 100644 --- a/__tests__/operation/__snapshots__/get-parameters-as-json-schema.test.js.snap +++ b/__tests__/operation/__snapshots__/get-parameters-as-json-schema.test.js.snap @@ -120,6 +120,86 @@ Array [ ] `; +exports[`polymorphism / discriminators should retain discriminator \`mapping\` refs when present 1`] = ` +Array [ + Object { + "label": "Body Params", + "schema": Object { + "components": Object { + "schemas": Object { + "OptionOneNoDisc": Object { + "properties": Object { + "discrim": Object { + "type": "string", + }, + "optionone": Object { + "type": "number", + }, + }, + "required": Array [ + "discrim", + ], + "type": "object", + }, + "OptionTwoNoDisc": Object { + "properties": Object { + "discrim": Object { + "type": "string", + }, + "optiontwo": Object { + "type": "string", + }, + }, + "required": Array [ + "discrim", + ], + "type": "object", + }, + }, + }, + "discriminator": Object { + "mapping": Object { + "Option One": "#/components/schemas/OptionOneNoDisc", + "Option Two": "#/components/schemas/OptionTwoNoDisc", + }, + "propertyName": "discrim", + }, + "oneOf": Array [ + Object { + "properties": Object { + "discrim": Object { + "type": "string", + }, + "optionone": Object { + "type": "number", + }, + }, + "required": Array [ + "discrim", + ], + "type": "object", + }, + Object { + "properties": Object { + "discrim": Object { + "type": "string", + }, + "optiontwo": Object { + "type": "string", + }, + }, + "required": Array [ + "discrim", + ], + "type": "object", + }, + ], + }, + "type": "body", + }, +] +`; + exports[`request bodies should convert request bodies to JSON schema (application/json) 1`] = ` Array [ Object { diff --git a/__tests__/operation/get-parameters-as-json-schema.test.js b/__tests__/operation/get-parameters-as-json-schema.test.js index 9469a0a7..fa9f1737 100644 --- a/__tests__/operation/get-parameters-as-json-schema.test.js +++ b/__tests__/operation/get-parameters-as-json-schema.test.js @@ -2,6 +2,7 @@ const Oas = require('../../src'); const createOas = require('../__fixtures__/create-oas'); const circular = require('../__datasets__/circular.json'); +const discriminators = require('../__datasets__/discriminators.json'); const petstore = require('@readme/oas-examples/3.0/json/petstore.json'); const petstoreServerVars = require('../__datasets__/petstore-server-vars.json'); @@ -320,6 +321,16 @@ describe('$ref quirks', () => { }); }); +describe('polymorphism / discriminators', () => { + it('should retain discriminator `mapping` refs when present', async () => { + const oas = new Oas(discriminators); + await oas.dereference(); + + const operation = oas.operation('/anything/discriminator-with-mapping', 'patch'); + expect(operation.getParametersAsJsonSchema()).toMatchSnapshot(); + }); +}); + describe('type', () => { describe('request bodies', () => { describe('repair invalid schema that has no `type`', () => { diff --git a/src/lib/openapi-to-json-schema.js b/src/lib/openapi-to-json-schema.js index e7c501d6..e3bc7947 100644 --- a/src/lib/openapi-to-json-schema.js +++ b/src/lib/openapi-to-json-schema.js @@ -221,6 +221,16 @@ function toJSONSchema(data, opts = {}) { } }); + if ('discriminator' in schema) { + if ('mapping' in schema.discriminator && typeof schema.discriminator.mapping === 'object') { + // Discriminator mappings aren't written as traditional `$ref` pointers so in order to log them to the supplied + // `refLogger`. + Object.keys(schema.discriminator.mapping).forEach(k => { + refLogger(schema.discriminator.mapping[k]); + }); + } + } + // If this schema is malformed for some reason, let's do our best to repair it. if (!('type' in schema) && !isPolymorphicSchema(schema) && !isRequestBodySchema(schema)) { if ('properties' in schema) {