From 8ce3621accc5826463520c91e921c62d9c939d82 Mon Sep 17 00:00:00 2001 From: Andrea Carraro Date: Sun, 3 Mar 2024 16:04:42 +0100 Subject: [PATCH] feat: merge operation and path level parameters (#184) * refactor: minor internal improvements * feat: merge operation and path level path params --- .changeset/sweet-hats-enjoy.md | 5 +++ README.md | 5 ++- src/openapiToTsJsonSchema.ts | 4 +-- ...rs.ts => convertOpenApiPathsParameters.ts} | 31 +++++++++++++------ src/utils/convertOpenApiToJsonSchema.ts | 2 +- src/utils/index.ts | 2 +- .../specs.yaml | 26 ++++++++++++---- ...eters.test.ts => paths-parameters.test.ts} | 27 +++++++++++----- 8 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 .changeset/sweet-hats-enjoy.md rename src/utils/{convertOpenApiParameters.ts => convertOpenApiPathsParameters.ts} (67%) rename test/fixtures/{parameters => paths-parameters}/specs.yaml (62%) rename test/{parameters.test.ts => paths-parameters.test.ts} (66%) diff --git a/.changeset/sweet-hats-enjoy.md b/.changeset/sweet-hats-enjoy.md new file mode 100644 index 0000000..e31d202 --- /dev/null +++ b/.changeset/sweet-hats-enjoy.md @@ -0,0 +1,5 @@ +--- +"openapi-ts-json-schema": minor +--- + +Merge operation and path level path parameters diff --git a/README.md b/README.md index 7ef5049..8a31b3f 100644 --- a/README.md +++ b/README.md @@ -145,11 +145,10 @@ Read [plugins documentation 📖](./docs/plugins.md). ## Todo -- Consider merging "operation" and "path" parameters definition - Consider removing required `definitionPathsToGenerateFrom` option in favour of exporting the whole OpenAPI definitions based on the structure defined in specs -- Consider adding a way to customize the values of the generated JSON schema ids. This could be beneficial even in case of multiple schemas being merged with plugins +- Improve external `#ref`s handling (currently being inlined and duplicated) - Find a way to merge multiple different OpenApi definitions consistently -- Improve external `#ref`s handling +- Consider adding a way to customize the values of the generated JSON schema ids. This could be beneficial even in case of multiple schemas being merged with plugins - Consider implementing an option to inline circular $refs with a configurable nesting level [ci-badge]: https://github.com/toomuchdesign/openapi-ts-json-schema/actions/workflows/ci.yml/badge.svg diff --git a/src/openapiToTsJsonSchema.ts b/src/openapiToTsJsonSchema.ts index 77e0b69..f6840ab 100644 --- a/src/openapiToTsJsonSchema.ts +++ b/src/openapiToTsJsonSchema.ts @@ -7,7 +7,7 @@ import { makeTsJsonSchemaFiles, REF_SYMBOL, convertOpenApiToJsonSchema, - convertOpenApiParameters, + convertOpenApiPathsParameters, addSchemaToMetaData, pathToRef, formatTypeScript, @@ -108,7 +108,7 @@ export async function openapiToTsJsonSchema({ }, ); - const jsonSchema = convertOpenApiParameters(dereferencedJsonSchema); + const jsonSchema = convertOpenApiPathsParameters(dereferencedJsonSchema); const schemaMetaDataMap: SchemaMetaDataMap = new Map(); /** diff --git a/src/utils/convertOpenApiParameters.ts b/src/utils/convertOpenApiPathsParameters.ts similarity index 67% rename from src/utils/convertOpenApiParameters.ts rename to src/utils/convertOpenApiPathsParameters.ts index e7b524f..7b73ab6 100644 --- a/src/utils/convertOpenApiParameters.ts +++ b/src/utils/convertOpenApiPathsParameters.ts @@ -5,6 +5,7 @@ import { import type { JSONSchema } from '../types'; type OpenAPIParameters = Parameters[0]; + function convertParametersToJSONSchema( openApiParameters: OpenAPIParameters, ): OpenAPIParametersAsJSONSchema { @@ -22,35 +23,45 @@ function convertParametersToJSONSchema( } /** - * Parameters field can only be found in: + * Paths parameters field can only be found in: * - paths[path].parameters * - paths[path][operation].parameters * * https://swagger.io/docs/specification/describing-parameters/ + * + * @NOTE The schema must be dereferenced since openapi-jsonschema-parameters + * doesn't handle $refs */ -export function convertOpenApiParameters(schema: JSONSchema): JSONSchema { +export function convertOpenApiPathsParameters(schema: JSONSchema): JSONSchema { if ('paths' in schema) { for (const path in schema.paths) { const pathSchema = schema.paths[path]; /** - * Path level parameters - * These could be merged with operation params: - * https://swagger.io/docs/specification/describing-parameters/#common + * Common path parameters + * https://swagger.io/docs/specification/describing-parameters/#common-for-path */ - if ('parameters' in pathSchema) { + const pathParameters = + 'parameters' in pathSchema ? pathSchema.parameters : []; + + if (pathParameters.length) { pathSchema.parameters = convertParametersToJSONSchema( pathSchema.parameters, ); } - // Operation level parameters + /** + * Operation path parameters + * https://swagger.io/docs/specification/describing-parameters/#path-parameters + */ for (const operation in pathSchema) { const operationSchema = pathSchema[operation]; if ('parameters' in operationSchema) { - operationSchema.parameters = convertParametersToJSONSchema( - operationSchema.parameters, - ); + // Merge operation and common path parameters + operationSchema.parameters = convertParametersToJSONSchema([ + ...pathParameters, + ...operationSchema.parameters, + ]); } } } diff --git a/src/utils/convertOpenApiToJsonSchema.ts b/src/utils/convertOpenApiToJsonSchema.ts index 7802bfa..865fe0b 100644 --- a/src/utils/convertOpenApiToJsonSchema.ts +++ b/src/utils/convertOpenApiToJsonSchema.ts @@ -4,7 +4,7 @@ import type { OpenApiSchema } from '../types'; export function convertOpenApiToJsonSchema(schema: OpenApiSchema) { /** * @openapi-contrib/openapi-schema-to-json-schema doesn't convert definitions by default, - * Here we convert all direct children of components + * Here we convert all direct children of components object: * https://swagger.io/specification/#components-object * https://github.com/openapi-contrib/openapi-schema-to-json-schema#definitionkeywords-array */ diff --git a/src/utils/index.ts b/src/utils/index.ts index 4a60a94..0bb44c4 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,6 @@ export { patchJsonSchema } from './makeTsJsonSchema/patchJsonSchema'; export { makeTsJsonSchema } from './makeTsJsonSchema'; -export { convertOpenApiParameters } from './convertOpenApiParameters'; +export { convertOpenApiPathsParameters } from './convertOpenApiPathsParameters'; export { convertOpenApiToJsonSchema } from './convertOpenApiToJsonSchema'; export { makeTsJsonSchemaFiles } from './makeTsJsonSchemaFiles'; export { refToPath } from './refToPath'; diff --git a/test/fixtures/parameters/specs.yaml b/test/fixtures/paths-parameters/specs.yaml similarity index 62% rename from test/fixtures/parameters/specs.yaml rename to test/fixtures/paths-parameters/specs.yaml index e666542..2abea82 100644 --- a/test/fixtures/parameters/specs.yaml +++ b/test/fixtures/paths-parameters/specs.yaml @@ -17,23 +17,35 @@ paths: - schema: type: string in: header - name: path-headers-param-1 + name: path-header-param required: true - description: path-headers-param-1 description + description: path-header-param description + - schema: + type: string + in: header + name: path-header-param-overridden-at-operation-level + description: path-header-param-overridden-at-operation-level description get: parameters: + # Header - schema: type: string in: header - name: headers-param-1 + name: header-param-1 required: true - description: headers-param-1 description + description: header-param-1 description - schema: $ref: '#/components/schemas/Answer' in: header - name: headers-param-2 + name: header-param-2 required: true - description: headers-param-2 description + description: header-param-2 description + - schema: + type: number + in: header + name: path-header-param-overridden-at-operation-level + description: path-header-param-overridden-at-operation-level description + # Body - schema: type: string enum: @@ -42,12 +54,14 @@ paths: in: body required: true description: Body param description + # Path - schema: type: string in: path name: path-param-1 required: true description: Path param description + # Query - schema: type: string in: query diff --git a/test/parameters.test.ts b/test/paths-parameters.test.ts similarity index 66% rename from test/parameters.test.ts rename to test/paths-parameters.test.ts index ba96302..ba4edd6 100644 --- a/test/parameters.test.ts +++ b/test/paths-parameters.test.ts @@ -3,10 +3,10 @@ import { describe, it, expect } from 'vitest'; import { fixtures, makeTestOutputPath } from './test-utils'; import { openapiToTsJsonSchema } from '../src'; -describe('OpenAPI parameters', () => { - it('Transforms parameters array into a JSON schema record', async () => { +describe('OpenAPI paths parameters', () => { + it('Transforms parameters array into valid JSON schema', async () => { const { outputPath } = await openapiToTsJsonSchema({ - openApiSchema: path.resolve(fixtures, 'parameters/specs.yaml'), + openApiSchema: path.resolve(fixtures, 'paths-parameters/specs.yaml'), outputPath: makeTestOutputPath('parameters'), definitionPathsToGenerateFrom: ['paths'], silent: true, @@ -20,9 +20,12 @@ describe('OpenAPI parameters', () => { parameters: { headers: { type: 'object', - required: ['path-headers-param-1'], + required: ['path-header-param'], properties: { - 'path-headers-param-1': { + 'path-header-param': { + type: 'string', + }, + 'path-header-param-overridden-at-operation-level': { type: 'string', }, }, @@ -33,15 +36,23 @@ describe('OpenAPI parameters', () => { headers: { type: 'object', properties: { - 'headers-param-1': { + 'header-param-1': { type: 'string', }, - 'headers-param-2': { + 'header-param-2': { type: 'string', enum: ['yes', 'no'], }, + // Merges path level parameters + 'path-header-param': { + type: 'string', + }, + // Overrides path level parameters + 'path-header-param-overridden-at-operation-level': { + type: 'number', + }, }, - required: ['headers-param-1', 'headers-param-2'], + required: ['path-header-param', 'header-param-1', 'header-param-2'], }, body: { type: 'string', enum: ['foo', 'bar'] }, path: {