diff --git a/utils/mocks/schemaWithDeprecatedResponses.yaml b/utils/mocks/schemaWithDeprecatedResponses.yaml new file mode 100644 index 00000000..4d6a6ece --- /dev/null +++ b/utils/mocks/schemaWithDeprecatedResponses.yaml @@ -0,0 +1,53 @@ +openapi: 3.1.1 +info: + title: test + version: '1.0' +paths: + /v4/events: + parameters: + # Path-level parameters are not operations and should remain untouched + - name: x-region + in: header + required: false + schema: + type: string + get: + operationId: searchEvents + responses: + '200': + description: OK + '500': + description: Workspace error + # Deprecated response should be removed + '504': + description: Gateway Timeout + x-deprecated-response: true + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /v4/events/{event_id}: + get: + operationId: getEvent + responses: + '200': + description: OK + # Responses not explicitly marked as deprecated should remain + '500': + description: Workspace error + x-deprecated-response: false + '504': + description: Gateway Timeout + x-deprecated-response: true + patch: + operationId: updateEvent + responses: + '200': + description: OK +components: + schemas: + ErrorResponse: + type: object + properties: + error: + type: string diff --git a/utils/mocks/schemaWithDeprecatedResponsesRemoved.yaml b/utils/mocks/schemaWithDeprecatedResponsesRemoved.yaml new file mode 100644 index 00000000..5240744e --- /dev/null +++ b/utils/mocks/schemaWithDeprecatedResponsesRemoved.yaml @@ -0,0 +1,40 @@ +openapi: 3.1.1 +info: + title: test + version: '1.0' +paths: + /v4/events: + parameters: + - name: x-region + in: header + required: false + schema: + type: string + get: + operationId: searchEvents + responses: + '200': + description: OK + '500': + description: Workspace error + /v4/events/{event_id}: + get: + operationId: getEvent + responses: + '200': + description: OK + '500': + description: Workspace error + x-deprecated-response: false + patch: + operationId: updateEvent + responses: + '200': + description: OK +components: + schemas: + ErrorResponse: + type: object + properties: + error: + type: string diff --git a/utils/transformers/removeDeprecatedResponsesTransformer.spec.ts b/utils/transformers/removeDeprecatedResponsesTransformer.spec.ts new file mode 100644 index 00000000..c07e500a --- /dev/null +++ b/utils/transformers/removeDeprecatedResponsesTransformer.spec.ts @@ -0,0 +1,22 @@ +import fs from 'fs'; +import { removeDeprecatedResponsesTransformer } from './removeDeprecatedResponsesTransformer.ts'; +import { transformSchema } from './transformSchema.ts'; + +const simpleYaml = fs.readFileSync('./utils/mocks/simple.yaml'); +const schemaWithDeprecatedResponses = fs.readFileSync('./utils/mocks/schemaWithDeprecatedResponses.yaml'); +const schemaWithDeprecatedResponsesRemoved = fs.readFileSync('./utils/mocks/schemaWithDeprecatedResponsesRemoved.yaml'); + +const removeDeprecatedResponses = (yaml: string | Buffer) => + transformSchema(yaml, [removeDeprecatedResponsesTransformer]); + +describe('Test removeDeprecatedResponsesTransformer', () => { + it('does not modify schema without deprecated responses', () => { + const result = removeDeprecatedResponses(simpleYaml); + expect(result.toString()).toEqual(simpleYaml.toString()); + }); + + it('removes responses marked with x-deprecated-response: true', () => { + const result = removeDeprecatedResponses(schemaWithDeprecatedResponses); + expect(result.toString()).toEqual(schemaWithDeprecatedResponsesRemoved.toString()); + }); +}); diff --git a/utils/transformers/removeDeprecatedResponsesTransformer.ts b/utils/transformers/removeDeprecatedResponsesTransformer.ts new file mode 100644 index 00000000..3eebdaed --- /dev/null +++ b/utils/transformers/removeDeprecatedResponsesTransformer.ts @@ -0,0 +1,21 @@ +import type { OpenApiDocument } from '../openapi.ts'; + +const DEPRECATED_RESPONSE_EXTENSION = 'x-deprecated-response'; + +// Removes responses marked with `x-deprecated-response: true` from path operations. +export function removeDeprecatedResponsesTransformer(apiDefinition: OpenApiDocument): void { + Object.values(apiDefinition.paths ?? {}).forEach((pathItem: any) => { + Object.values(pathItem ?? {}).forEach((operation: any) => { + const responses = operation?.responses; + if (!responses || typeof responses !== 'object') { + return; + } + + Object.entries(responses).forEach(([statusCode, response]: [string, any]) => { + if (response?.[DEPRECATED_RESPONSE_EXTENSION] === true) { + delete responses[statusCode]; + } + }); + }); + }); +} diff --git a/utils/transformers/transformSchema.ts b/utils/transformers/transformSchema.ts index d2f7fad7..0d2ddad4 100644 --- a/utils/transformers/transformSchema.ts +++ b/utils/transformers/transformSchema.ts @@ -6,6 +6,7 @@ import { extractFirstParameterExampleTransformer } from './extractFirstParameter import { extractPathOperationInlineEnumsTransformer } from './extractPathOperationInlineEnumsTransformer.ts'; import { parseYaml } from './parseYaml.ts'; import { removeBigExamplesTransformer } from './removeBigExamplesTransformer.ts'; +import { removeDeprecatedResponsesTransformer } from './removeDeprecatedResponsesTransformer.ts'; import { removeEdgeTransformer } from './removeEdgeTransformer.ts'; import { removeFeedbackTransformer } from './removeFeedbackTransformer.ts'; import { removeWebhookTransformer } from './removeWebhookTransformer.ts'; @@ -39,12 +40,15 @@ export const v4CommonTransformers: Transformer[] = [ export const v4Transformers: Transformer[] = [ ...v4CommonTransformers, + // Hide deprecated responses from the docs. SDK schemas keep them to avoid breaking changes. + removeDeprecatedResponsesTransformer, // This transformer should run last to ensure all unused schemas are found removeUnusedSchemasTransformer, ]; export const v4SchemaForSdksCommonTransformers: Transformer[] = [ - ...v4Transformers, + ...v4CommonTransformers, + removeUnusedSchemasTransformer, extractFirstParameterExampleTransformer, removeEdgeTransformer, removeFeedbackTransformer,