From 07cd8ef17ac3c3bdd903689a7618fb0bb4ca5a75 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Thu, 18 Feb 2021 09:27:55 -0800 Subject: [PATCH] fix: edgecase where allOfs would receive a `type` when one wasn't present (#374) --- .../get-parameters-as-json-schema.test.js | 50 +++++++++++++++++++ .../get-parameters-as-json-schema.js | 28 +++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/__tests__/tooling/operation/get-parameters-as-json-schema.test.js b/__tests__/tooling/operation/get-parameters-as-json-schema.test.js index ca652b4d..ff63fe27 100644 --- a/__tests__/tooling/operation/get-parameters-as-json-schema.test.js +++ b/__tests__/tooling/operation/get-parameters-as-json-schema.test.js @@ -701,6 +701,56 @@ describe('request bodies', () => { }); describe('polymorphism / inheritance', () => { + describe('adding missing `type` properties', () => { + it("should not add a `type` to a shapeless-description that's part of an `allOf`", () => { + const oas = createOas({ + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + petIds: { + allOf: [{ type: 'array', items: { type: 'string' } }, { description: 'Parameter description' }], + }, + }, + }, + }, + }, + }, + }); + + const schema = oas.operation('/', 'get').getParametersAsJsonSchema(); + + expect(schema[0].schema.properties.petIds.allOf[1].type).toBeUndefined(); + }); + + it.each([['anyOf'], ['oneOf']])("should add a `type` to a shapeless-description that's part of an `%s`", prop => { + const oas = createOas({ + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + petIds: { + [prop]: [{ type: 'array', items: { type: 'string' } }, { description: 'Parameter description' }], + }, + }, + }, + }, + }, + }, + }); + + const schema = oas.operation('/', 'get').getParametersAsJsonSchema(); + expect(schema[0].schema.properties.petIds[prop][1]).toStrictEqual({ + description: 'Parameter description', + type: 'string', + }); + }); + }); + it.each([['allOf'], ['anyOf'], ['oneOf']])('should support nested %s', prop => { const oas = createOas({ requestBody: { diff --git a/tooling/operation/get-parameters-as-json-schema.js b/tooling/operation/get-parameters-as-json-schema.js index e45d09d1..4e563f51 100644 --- a/tooling/operation/get-parameters-as-json-schema.js +++ b/tooling/operation/get-parameters-as-json-schema.js @@ -178,10 +178,17 @@ function searchForExampleByPointer(pointer, examples = []) { * @link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md * @param {Object} data * @param {Object[]} prevSchemas - * @param {string} currentLocation + * @param {String} currentLocation * @param {Object} globalDefaults + * @param {Boolean} isPolymorphicAllOfChild */ -function constructSchema(data, prevSchemas = [], currentLocation = '', globalDefaults) { +function constructSchema( + data, + prevSchemas = [], + currentLocation = '', + globalDefaults, + isPolymorphicAllOfChild = false +) { const schema = { ...data }; // If this schema contains a `$ref`, it's circular and we shouldn't try to resolve it. Just return and move along. @@ -197,6 +204,15 @@ function constructSchema(data, prevSchemas = [], currentLocation = '', globalDef schema.type = 'object'; } else if ('items' in schema) { schema.type = 'array'; + } else if (isPolymorphicAllOfChild) { + // If this schema is immediate child of a polymorphic schema and is neither an array or an object, we should + // leave it alone. Cases like this are common where somebody might use `allOf` in order to dynamically add a + // `description` onto another schema, like such: + // + // allOf: [ + // { type: 'array', items: { type: 'string' }, + // { description: 'This is the description for the `array`.' } + // ] } else { // If we're processing a schema that has no types, no refs, and is just a lone schema, we should treat it at the // bare minimum as a simple string so we make an attempt to generate valid JSON Schema. @@ -330,7 +346,13 @@ function constructSchema(data, prevSchemas = [], currentLocation = '', globalDef ['allOf', 'anyOf', 'oneOf'].forEach(polyType => { if (polyType in schema && Array.isArray(schema[polyType])) { schema[polyType].forEach((item, idx) => { - schema[polyType][idx] = constructSchema(item, prevSchemas, `${currentLocation}/${idx}`, globalDefaults); + schema[polyType][idx] = constructSchema( + item, + prevSchemas, + `${currentLocation}/${idx}`, + globalDefaults, + polyType === 'allOf' + ); }); } });