From 2b1338e6d9d996f12f1242a495fc552b4bbcfc34 Mon Sep 17 00:00:00 2001 From: Kanad Gupta <8854718+kanadgupta@users.noreply.github.com> Date: Tue, 23 May 2023 17:19:41 -0500 Subject: [PATCH] fix: ensure summaries + descriptions are strings (#764) * fix: ensure summaries + descriptions are strings * fix: also extending this check to common fields feedback: https://github.com/readmeio/oas/pull/764#discussion_r1203069976 --- .../callbacks-weird-summary-description.json | 205 ++++++++++++++++++ __tests__/operation.test.ts | 41 ++++ src/operation.ts | 24 +- 3 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 __tests__/__datasets__/callbacks-weird-summary-description.json diff --git a/__tests__/__datasets__/callbacks-weird-summary-description.json b/__tests__/__datasets__/callbacks-weird-summary-description.json new file mode 100644 index 000000000..c2c73be6c --- /dev/null +++ b/__tests__/__datasets__/callbacks-weird-summary-description.json @@ -0,0 +1,205 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Support for callbacks", + "description": "https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operationObject\n\nhttps://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object\n\nhttps://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://httpbin.org" + } + ], + "paths": { + "/callbacks": { + "summary": { + "$ref": "foo-summary.md" + }, + "description": { + "$ref": "foo-desc.md" + }, + "get": { + "summary": { + "$ref": "foo-summary.md" + }, + "description": { + "$ref": "foo-desc.md" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "callbacks": { + "myCallback": { + "{$request.query.queryUrl}": { + "post": { + "summary": { + "$ref": "foo-summary.md" + }, + "description": { + "$ref": "foo-desc.md" + }, + "requestBody": { + "description": "Callback payload", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dog" + } + } + } + }, + "responses": { + "200": { + "description": "callback successfully processed", + "content": { + "application/json": { + "example": { + "id": 1, + "name": "Pug", + "is_a_good_dog": true + } + } + } + } + } + } + } + }, + "multipleCallback": { + "{$request.multipleExpression.queryUrl}": { + "post": { + "requestBody": { + "description": "Callback payload", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dog" + } + } + } + }, + "responses": { + "200": { + "description": "callback successfully processed", + "content": { + "application/json": { + "example": { + "id": 1, + "name": "Pug", + "is_a_good_dog": true + } + } + } + } + } + } + }, + "{$request.multipleMethod.queryUrl}": { + "summary": { + "$ref": "foo-summary.md" + }, + "description": { + "$ref": "foo-desc.md" + }, + "parameters": [ + { + "in": "query", + "name": "queryParam", + "schema": { + "type": "string" + }, + "required": true + } + ], + "post": { + "requestBody": { + "description": "Callback payload", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dog" + } + } + } + }, + "responses": { + "200": { + "description": "callback successfully processed" + } + } + }, + "get": { + "summary": "[get] callback summary", + "description": "[get] callback description", + "parameters": [ + { + "in": "query", + "name": "queryParam", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "anotherQueryParam", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "callback successfully processed", + "content": { + "application/json": { + "example": { + "id": 1, + "name": "Pug", + "is_a_good_dog": true + } + } + } + } + } + } + } + } + } + }, + "post": { + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "dog": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "is_a_good_dog": { + "type": "boolean" + } + }, + "example": { + "id": 1, + "name": "Pug", + "is_a_good_dog": true + } + } + } + } +} diff --git a/__tests__/operation.test.ts b/__tests__/operation.test.ts index f6ebb4ea0..b96afeec1 100644 --- a/__tests__/operation.test.ts +++ b/__tests__/operation.test.ts @@ -15,6 +15,7 @@ let parametersCommon: Oas; let petstoreNondereferenced: Oas; let oas31NoResponses: Oas; let readme: Oas; +let callbacksWeirdSummaryDescription: Oas; beforeAll(async () => { petstore = await import('@readme/oas-examples/3.0/json/petstore.json').then(r => r.default).then(Oas.init); @@ -35,6 +36,11 @@ beforeAll(async () => { deprecatedSchema = await import('./__datasets__/schema-deprecated.json').then(r => r.default).then(Oas.init); await deprecatedSchema.dereference(); + callbacksWeirdSummaryDescription = await import('./__datasets__/callbacks-weird-summary-description.json') + .then(r => r.default) + .then(Oas.init); + await callbacksWeirdSummaryDescription.dereference(); + parametersCommon = await import('@readme/oas-examples/3.0/json/parameters-common.json') .then(r => r.default) .then(Oas.init); @@ -83,6 +89,20 @@ describe('#getSummary() + #getDescription()', () => { expect(operation.getDescription()).toBe('[get] Description'); }); + it('should account for non-string summaries and descriptions', () => { + const operation = callbacksWeirdSummaryDescription.operation('/callbacks', 'get'); + + expect(operation.getSummary()).toBeUndefined(); + expect(operation.getDescription()).toBeUndefined(); + }); + + it('should account for non-string common summaries and descriptions', () => { + const operation = callbacksWeirdSummaryDescription.operation('/callbacks', 'post'); + + expect(operation.getSummary()).toBeUndefined(); + expect(operation.getDescription()).toBeUndefined(); + }); + describe('callbacks', () => { it('should return a summary if present', () => { const operation = callbackSchema.operation('/callbacks', 'get'); @@ -115,6 +135,27 @@ describe('#getSummary() + #getDescription()', () => { expect(callback.getSummary()).toBe('[post] callback summary'); expect(callback.getDescription()).toBe('[post] callback description'); }); + + it('should account for non-string summaries and descriptions', () => { + const operation = callbacksWeirdSummaryDescription.operation('/callbacks', 'get'); + + const callback = operation.getCallback('myCallback', '{$request.query.queryUrl}', 'post') as Callback; + + expect(callback.getSummary()).toBeUndefined(); + expect(callback.getDescription()).toBeUndefined(); + }); + + it('should account for non-string common callback summary + descriptions', () => { + const operation = callbacksWeirdSummaryDescription.operation('/callbacks', 'get'); + const callback = operation.getCallback( + 'multipleCallback', + '{$request.multipleMethod.queryUrl}', + 'post' + ) as Callback; + + expect(callback.getSummary()).toBeUndefined(); + expect(callback.getDescription()).toBeUndefined(); + }); }); }); diff --git a/src/operation.ts b/src/operation.ts index 48f5efceb..9f99281c0 100644 --- a/src/operation.ts +++ b/src/operation.ts @@ -78,9 +78,9 @@ export default class Operation { } getSummary(): string { - if (this.schema?.summary) { - return this.schema.summary.trim(); - } else if (this.api.paths[this.path].summary) { + if (this.schema?.summary && typeof this.schema.summary === 'string') { + return this.schema.summary; + } else if (this.api.paths[this.path].summary && typeof this.api.paths[this.path].summary === 'string') { return this.api.paths[this.path].summary; } @@ -88,9 +88,9 @@ export default class Operation { } getDescription(): string { - if (this.schema?.description) { - return this.schema.description.trim(); - } else if (this.api.paths[this.path].description) { + if (this.schema?.description && typeof this.schema.description === 'string') { + return this.schema.description; + } else if (this.api.paths[this.path].description && typeof this.api.paths[this.path].description === 'string') { return this.api.paths[this.path].description; } @@ -854,9 +854,9 @@ export class Callback extends Operation { } getSummary(): string { - if (this.schema?.summary) { - return this.schema.summary.trim(); - } else if (this.parentSchema.summary) { + if (this.schema?.summary && typeof this.schema.summary === 'string') { + return this.schema.summary; + } else if (this.parentSchema.summary && typeof this.parentSchema.summary === 'string') { return this.parentSchema.summary; } @@ -864,9 +864,9 @@ export class Callback extends Operation { } getDescription(): string { - if (this.schema?.description) { - return this.schema.description.trim(); - } else if (this.parentSchema.description) { + if (this.schema?.description && typeof this.schema.description === 'string') { + return this.schema.description; + } else if (this.parentSchema.description && typeof this.parentSchema.description === 'string') { return this.parentSchema.description; }