diff --git a/package.json b/package.json index 8fd8d72a..44b62eec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openapi-typescript-codegen", - "version": "0.1.14", + "version": "0.1.15", "description": "NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification.", "author": "Ferdi Koomen", "homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen", diff --git a/src/openApi/v2/parser/getOperationName.spec.ts b/src/openApi/v2/parser/getOperationName.spec.ts index ba8a5d3d..8773b5f0 100644 --- a/src/openApi/v2/parser/getOperationName.spec.ts +++ b/src/openApi/v2/parser/getOperationName.spec.ts @@ -6,5 +6,8 @@ describe('getOperationName', () => { expect(getOperationName('FooBar')).toEqual('fooBar'); expect(getOperationName('Foo Bar')).toEqual('fooBar'); expect(getOperationName('foo bar')).toEqual('fooBar'); + expect(getOperationName('foo-bar')).toEqual('fooBar'); + expect(getOperationName('foo_bar')).toEqual('fooBar'); + expect(getOperationName('foo.bar')).toEqual('fooBar'); }); }); diff --git a/src/openApi/v2/parser/getOperationName.ts b/src/openApi/v2/parser/getOperationName.ts index 83a9abcb..78a045ca 100644 --- a/src/openApi/v2/parser/getOperationName.ts +++ b/src/openApi/v2/parser/getOperationName.ts @@ -6,6 +6,6 @@ import camelCase from 'camelcase'; * the most popular Javascript and Typescript writing style. */ export function getOperationName(value: string): string { - const clean = value.replace(/[^\w\s\-]+/g, '_').trim(); + const clean = value.replace(/[^\w\s\-]+/g, '-').trim(); return camelCase(clean); } diff --git a/src/openApi/v2/parser/getOperationParameter.ts b/src/openApi/v2/parser/getOperationParameter.ts index cb34a305..1cc9a0fe 100644 --- a/src/openApi/v2/parser/getOperationParameter.ts +++ b/src/openApi/v2/parser/getOperationParameter.ts @@ -52,7 +52,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.template = definitionRef.template; operationParameter.imports.push(...definitionRef.imports); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } @@ -64,7 +64,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.base = PrimaryType.STRING; operationParameter.enum.push(...enumerators); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } } @@ -77,7 +77,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.base = PrimaryType.NUMBER; operationParameter.enum.push(...enumerators); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } } @@ -90,7 +90,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.template = items.template; operationParameter.imports.push(...items.imports); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } @@ -103,7 +103,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.imports.push(...items.imports); operationParameter.imports.push('Dictionary'); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } @@ -116,7 +116,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.template = model.template; operationParameter.imports.push(...model.imports); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } else { const model = getModel(openApi, parameter.schema); @@ -131,7 +131,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.enums.push(...model.enums); operationParameter.properties.push(...model.properties); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } } @@ -145,7 +145,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.template = definitionType.template; operationParameter.imports.push(...definitionType.imports); operationParameter.default = getOperationParameterDefault(parameter, operationParameter); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } diff --git a/src/openApi/v2/parser/getOperationParameterName.spec.ts b/src/openApi/v2/parser/getOperationParameterName.spec.ts new file mode 100644 index 00000000..ef7f2ddc --- /dev/null +++ b/src/openApi/v2/parser/getOperationParameterName.spec.ts @@ -0,0 +1,14 @@ +import { getOperationParameterName } from './getOperationParameterName'; + +describe('getOperationParameterName', () => { + it('should produce correct result', () => { + expect(getOperationParameterName('')).toEqual(''); + expect(getOperationParameterName('foobar')).toEqual('foobar'); + expect(getOperationParameterName('fooBar')).toEqual('fooBar'); + expect(getOperationParameterName('foo-bar')).toEqual('fooBar'); + expect(getOperationParameterName('foo_bar')).toEqual('fooBar'); + expect(getOperationParameterName('foo.bar')).toEqual('fooBar'); + expect(getOperationParameterName('Foo-Bar')).toEqual('fooBar'); + expect(getOperationParameterName('FOO-BAR')).toEqual('fooBar'); + }); +}); diff --git a/src/openApi/v2/parser/getOperationParameterName.ts b/src/openApi/v2/parser/getOperationParameterName.ts index 54d80d9f..5eb6c6a4 100644 --- a/src/openApi/v2/parser/getOperationParameterName.ts +++ b/src/openApi/v2/parser/getOperationParameterName.ts @@ -5,6 +5,6 @@ import camelCase from 'camelcase'; * For example: 'filter.someProperty' becomes 'filterSomeProperty'. */ export function getOperationParameterName(value: string): string { - const clean = value.replace(/[^\w\s\-]+/g, '_').trim(); + const clean = value.replace(/[^\w\s\-]+/g, '-').trim(); return camelCase(clean); } diff --git a/src/openApi/v2/parser/getOperationParameters.ts b/src/openApi/v2/parser/getOperationParameters.ts index 63346fba..d8e99d87 100644 --- a/src/openApi/v2/parser/getOperationParameters.ts +++ b/src/openApi/v2/parser/getOperationParameters.ts @@ -1,13 +1,9 @@ import { OpenApi } from '../interfaces/OpenApi'; import { OpenApiParameter } from '../interfaces/OpenApiParameter'; -import { OperationParameter } from '../../../client/interfaces/OperationParameter'; import { OperationParameters } from '../../../client/interfaces/OperationParameters'; import { getOperationParameter } from './getOperationParameter'; import { getRef } from './getRef'; - -function sortByRequired(a: OperationParameter, b: OperationParameter): number { - return a.isRequired && !b.isRequired ? -1 : !a.isRequired && b.isRequired ? 1 : 0; -} +import { sortByRequired } from './sortByRequired'; export function getOperationParameters(openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters { const operationParameters: OperationParameters = { diff --git a/src/openApi/v2/parser/getOperationPath.spec.ts b/src/openApi/v2/parser/getOperationPath.spec.ts index 4ede28f4..41df428d 100644 --- a/src/openApi/v2/parser/getOperationPath.spec.ts +++ b/src/openApi/v2/parser/getOperationPath.spec.ts @@ -5,6 +5,12 @@ describe('getOperationPath', () => { expect(getOperationPath('/api/v{api-version}/list/{id}/{type}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}/${type}'); expect(getOperationPath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}'); expect(getOperationPath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}'); - expect(getOperationPath('/api/v1/list')).toEqual('/api/v1/list'); + expect(getOperationPath('/api/{foobar}')).toEqual('/api/${foobar}'); + expect(getOperationPath('/api/{fooBar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{foo-bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{foo_bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{foo.bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{Foo-Bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{FOO-BAR}')).toEqual('/api/${fooBar}'); }); }); diff --git a/src/openApi/v2/parser/getOperationPath.ts b/src/openApi/v2/parser/getOperationPath.ts index 04b6fd4e..7d2a07cf 100644 --- a/src/openApi/v2/parser/getOperationPath.ts +++ b/src/openApi/v2/parser/getOperationPath.ts @@ -1,9 +1,16 @@ +import { getOperationParameterName } from './getOperationParameterName'; + /** * Get the final service path, this replaces the "{api-version}" placeholder * with a new template string placeholder so we can dynamically inject the * OpenAPI version without the need to hardcode this in the URL. + * Plus we return the correct parameter names to replace in the URL. * @param path */ export function getOperationPath(path: string): string { - return path.replace(/{api-version}/g, '{OpenAPI.VERSION}').replace(/\{(.*?)\}/g, '${$1}'); + return path + .replace(/\{(.*?)\}/g, (_, w: string) => { + return `\${${getOperationParameterName(w)}}`; + }) + .replace('${apiVersion}', '${OpenAPI.VERSION}'); } diff --git a/src/openApi/v2/parser/getOperationResponseCode.spec.ts b/src/openApi/v2/parser/getOperationResponseCode.spec.ts new file mode 100644 index 00000000..39390709 --- /dev/null +++ b/src/openApi/v2/parser/getOperationResponseCode.spec.ts @@ -0,0 +1,13 @@ +import { getOperationResponseCode } from './getOperationResponseCode'; + +describe('getOperationResponseCode', () => { + it('should produce correct result', () => { + expect(getOperationResponseCode('')).toEqual(null); + expect(getOperationResponseCode('default')).toEqual(200); + expect(getOperationResponseCode('200')).toEqual(200); + expect(getOperationResponseCode('300')).toEqual(300); + expect(getOperationResponseCode('400')).toEqual(400); + expect(getOperationResponseCode('abc')).toEqual(null); + expect(getOperationResponseCode('-100')).toEqual(100); + }); +}); diff --git a/src/openApi/v2/parser/getOperationResponseCode.ts b/src/openApi/v2/parser/getOperationResponseCode.ts index 080cdc02..c5609938 100644 --- a/src/openApi/v2/parser/getOperationResponseCode.ts +++ b/src/openApi/v2/parser/getOperationResponseCode.ts @@ -8,7 +8,7 @@ export function getOperationResponseCode(value: string | 'default'): number | nu if (/[0-9]+/g.test(value)) { const code = parseInt(value); if (Number.isInteger(code)) { - return code; + return Math.abs(code); } } diff --git a/src/openApi/v2/parser/getServiceClassName.ts b/src/openApi/v2/parser/getServiceClassName.ts index 94131a3d..4f116a5a 100644 --- a/src/openApi/v2/parser/getServiceClassName.ts +++ b/src/openApi/v2/parser/getServiceClassName.ts @@ -5,7 +5,7 @@ import camelCase from 'camelcase'; * the input string to PascalCase and appends the "Service" prefix if needed. */ export function getServiceClassName(value: string): string { - const clean = value.replace(/[^\w\s\-]+/g, '_').trim(); + const clean = value.replace(/[^\w\s\-]+/g, '-').trim(); const name = camelCase(clean, { pascalCase: true }); if (name && !name.endsWith('Service')) { return `${name}Service`; diff --git a/src/openApi/v2/parser/sortByRequired.ts b/src/openApi/v2/parser/sortByRequired.ts new file mode 100644 index 00000000..bfcd0c7b --- /dev/null +++ b/src/openApi/v2/parser/sortByRequired.ts @@ -0,0 +1,5 @@ +import { OperationParameter } from '../../../client/interfaces/OperationParameter'; + +export function sortByRequired(a: OperationParameter, b: OperationParameter): number { + return a.isRequired && !b.isRequired ? -1 : !a.isRequired && b.isRequired ? 1 : 0; +} diff --git a/src/openApi/v3/interfaces/OpenApiRequestBody.d.ts b/src/openApi/v3/interfaces/OpenApiRequestBody.d.ts index f4215ec4..e526bf61 100644 --- a/src/openApi/v3/interfaces/OpenApiRequestBody.d.ts +++ b/src/openApi/v3/interfaces/OpenApiRequestBody.d.ts @@ -9,4 +9,5 @@ export interface OpenApiRequestBody extends OpenApiReference { description?: string; content: Dictionary; required?: boolean; + nullable?: boolean; } diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts index 20ff34fa..e0cfa8b9 100644 --- a/src/openApi/v3/parser/getOperation.ts +++ b/src/openApi/v3/parser/getOperation.ts @@ -10,6 +10,7 @@ import { getOperationRequestBody } from './getOperationRequestBody'; import { getOperationResponses } from './getOperationResponses'; import { getOperationResults } from './getOperationResults'; import { getServiceClassName } from './getServiceClassName'; +import { sortByRequired } from './sortByRequired'; export function getOperation(openApi: OpenApi, url: string, method: string, op: OpenApiOperation): Operation { const serviceName = (op.tags && op.tags[0]) || 'Service'; @@ -56,6 +57,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op: const requestBody = getOperationRequestBody(openApi, op.requestBody); operation.imports.push(...requestBody.imports); operation.parameters.push(requestBody); + operation.parameters = operation.parameters.sort(sortByRequired); operation.parametersBody = requestBody; } diff --git a/src/openApi/v3/parser/getOperationName.spec.ts b/src/openApi/v3/parser/getOperationName.spec.ts index ba8a5d3d..8773b5f0 100644 --- a/src/openApi/v3/parser/getOperationName.spec.ts +++ b/src/openApi/v3/parser/getOperationName.spec.ts @@ -6,5 +6,8 @@ describe('getOperationName', () => { expect(getOperationName('FooBar')).toEqual('fooBar'); expect(getOperationName('Foo Bar')).toEqual('fooBar'); expect(getOperationName('foo bar')).toEqual('fooBar'); + expect(getOperationName('foo-bar')).toEqual('fooBar'); + expect(getOperationName('foo_bar')).toEqual('fooBar'); + expect(getOperationName('foo.bar')).toEqual('fooBar'); }); }); diff --git a/src/openApi/v3/parser/getOperationName.ts b/src/openApi/v3/parser/getOperationName.ts index 83a9abcb..78a045ca 100644 --- a/src/openApi/v3/parser/getOperationName.ts +++ b/src/openApi/v3/parser/getOperationName.ts @@ -6,6 +6,6 @@ import camelCase from 'camelcase'; * the most popular Javascript and Typescript writing style. */ export function getOperationName(value: string): string { - const clean = value.replace(/[^\w\s\-]+/g, '_').trim(); + const clean = value.replace(/[^\w\s\-]+/g, '-').trim(); return camelCase(clean); } diff --git a/src/openApi/v3/parser/getOperationParameter.ts b/src/openApi/v3/parser/getOperationParameter.ts index 40517eec..2fb16202 100644 --- a/src/openApi/v3/parser/getOperationParameter.ts +++ b/src/openApi/v3/parser/getOperationParameter.ts @@ -49,8 +49,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.template = model.template; operationParameter.imports.push(...model.imports); operationParameter.default = getModelDefault(parameter.schema); - operationParameter.isRequired = operationParameter.default || operationParameter.isRequired; - + operationParameter.isRequired = operationParameter.isRequired || operationParameter.default; return operationParameter; } else { const model = getModel(openApi, parameter.schema); @@ -60,8 +59,8 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.template = model.template; operationParameter.link = model.link; operationParameter.isReadOnly = model.isReadOnly; - operationParameter.isRequired = model.isRequired; - operationParameter.isNullable = model.isNullable; + operationParameter.isRequired = operationParameter.isRequired || model.isRequired || model.default; + operationParameter.isNullable = operationParameter.isNullable || model.isNullable; operationParameter.format = model.format; operationParameter.maximum = model.maximum; operationParameter.exclusiveMaximum = model.exclusiveMaximum; @@ -77,7 +76,6 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame operationParameter.maxProperties = model.maxProperties; operationParameter.minProperties = model.minProperties; operationParameter.default = model.default; - operationParameter.isRequired = model.default || model.isRequired; operationParameter.imports.push(...model.imports); operationParameter.extends.push(...model.extends); operationParameter.enum.push(...model.enum); diff --git a/src/openApi/v3/parser/getOperationParameterName.spec.ts b/src/openApi/v3/parser/getOperationParameterName.spec.ts new file mode 100644 index 00000000..ef7f2ddc --- /dev/null +++ b/src/openApi/v3/parser/getOperationParameterName.spec.ts @@ -0,0 +1,14 @@ +import { getOperationParameterName } from './getOperationParameterName'; + +describe('getOperationParameterName', () => { + it('should produce correct result', () => { + expect(getOperationParameterName('')).toEqual(''); + expect(getOperationParameterName('foobar')).toEqual('foobar'); + expect(getOperationParameterName('fooBar')).toEqual('fooBar'); + expect(getOperationParameterName('foo-bar')).toEqual('fooBar'); + expect(getOperationParameterName('foo_bar')).toEqual('fooBar'); + expect(getOperationParameterName('foo.bar')).toEqual('fooBar'); + expect(getOperationParameterName('Foo-Bar')).toEqual('fooBar'); + expect(getOperationParameterName('FOO-BAR')).toEqual('fooBar'); + }); +}); diff --git a/src/openApi/v3/parser/getOperationParameterName.ts b/src/openApi/v3/parser/getOperationParameterName.ts index 54d80d9f..5eb6c6a4 100644 --- a/src/openApi/v3/parser/getOperationParameterName.ts +++ b/src/openApi/v3/parser/getOperationParameterName.ts @@ -5,6 +5,6 @@ import camelCase from 'camelcase'; * For example: 'filter.someProperty' becomes 'filterSomeProperty'. */ export function getOperationParameterName(value: string): string { - const clean = value.replace(/[^\w\s\-]+/g, '_').trim(); + const clean = value.replace(/[^\w\s\-]+/g, '-').trim(); return camelCase(clean); } diff --git a/src/openApi/v3/parser/getOperationParameters.ts b/src/openApi/v3/parser/getOperationParameters.ts index 9ba08031..a5fe313a 100644 --- a/src/openApi/v3/parser/getOperationParameters.ts +++ b/src/openApi/v3/parser/getOperationParameters.ts @@ -1,13 +1,9 @@ import { OpenApi } from '../interfaces/OpenApi'; import { OpenApiParameter } from '../interfaces/OpenApiParameter'; -import { OperationParameter } from '../../../client/interfaces/OperationParameter'; import { OperationParameters } from '../../../client/interfaces/OperationParameters'; import { getOperationParameter } from './getOperationParameter'; import { getRef } from './getRef'; - -function sortByRequired(a: OperationParameter, b: OperationParameter): number { - return a.isRequired && !b.isRequired ? -1 : !a.isRequired && b.isRequired ? 1 : 0; -} +import { sortByRequired } from './sortByRequired'; export function getOperationParameters(openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters { const operationParameters: OperationParameters = { diff --git a/src/openApi/v3/parser/getOperationPath.spec.ts b/src/openApi/v3/parser/getOperationPath.spec.ts index 4ede28f4..41df428d 100644 --- a/src/openApi/v3/parser/getOperationPath.spec.ts +++ b/src/openApi/v3/parser/getOperationPath.spec.ts @@ -5,6 +5,12 @@ describe('getOperationPath', () => { expect(getOperationPath('/api/v{api-version}/list/{id}/{type}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}/${type}'); expect(getOperationPath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}'); expect(getOperationPath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}'); - expect(getOperationPath('/api/v1/list')).toEqual('/api/v1/list'); + expect(getOperationPath('/api/{foobar}')).toEqual('/api/${foobar}'); + expect(getOperationPath('/api/{fooBar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{foo-bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{foo_bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{foo.bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{Foo-Bar}')).toEqual('/api/${fooBar}'); + expect(getOperationPath('/api/{FOO-BAR}')).toEqual('/api/${fooBar}'); }); }); diff --git a/src/openApi/v3/parser/getOperationPath.ts b/src/openApi/v3/parser/getOperationPath.ts index 04b6fd4e..7d2a07cf 100644 --- a/src/openApi/v3/parser/getOperationPath.ts +++ b/src/openApi/v3/parser/getOperationPath.ts @@ -1,9 +1,16 @@ +import { getOperationParameterName } from './getOperationParameterName'; + /** * Get the final service path, this replaces the "{api-version}" placeholder * with a new template string placeholder so we can dynamically inject the * OpenAPI version without the need to hardcode this in the URL. + * Plus we return the correct parameter names to replace in the URL. * @param path */ export function getOperationPath(path: string): string { - return path.replace(/{api-version}/g, '{OpenAPI.VERSION}').replace(/\{(.*?)\}/g, '${$1}'); + return path + .replace(/\{(.*?)\}/g, (_, w: string) => { + return `\${${getOperationParameterName(w)}}`; + }) + .replace('${apiVersion}', '${OpenAPI.VERSION}'); } diff --git a/src/openApi/v3/parser/getOperationRequestBody.ts b/src/openApi/v3/parser/getOperationRequestBody.ts index 682b6651..da5a0a7b 100644 --- a/src/openApi/v3/parser/getOperationRequestBody.ts +++ b/src/openApi/v3/parser/getOperationRequestBody.ts @@ -22,7 +22,7 @@ export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequ isProperty: false, isReadOnly: false, isRequired: parameter.required === true, - isNullable: false, + isNullable: parameter.nullable === true, imports: [], extends: [], enum: [], @@ -48,8 +48,8 @@ export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequ requestBody.template = model.template; requestBody.link = model.link; requestBody.isReadOnly = model.isReadOnly; - requestBody.isRequired = model.isRequired; - requestBody.isNullable = model.isNullable; + requestBody.isRequired = requestBody.isRequired || model.isRequired; + requestBody.isNullable = requestBody.isNullable || model.isNullable; requestBody.format = model.format; requestBody.maximum = model.maximum; requestBody.exclusiveMaximum = model.exclusiveMaximum; diff --git a/src/openApi/v3/parser/getOperationResponseCode.spec.ts b/src/openApi/v3/parser/getOperationResponseCode.spec.ts new file mode 100644 index 00000000..39390709 --- /dev/null +++ b/src/openApi/v3/parser/getOperationResponseCode.spec.ts @@ -0,0 +1,13 @@ +import { getOperationResponseCode } from './getOperationResponseCode'; + +describe('getOperationResponseCode', () => { + it('should produce correct result', () => { + expect(getOperationResponseCode('')).toEqual(null); + expect(getOperationResponseCode('default')).toEqual(200); + expect(getOperationResponseCode('200')).toEqual(200); + expect(getOperationResponseCode('300')).toEqual(300); + expect(getOperationResponseCode('400')).toEqual(400); + expect(getOperationResponseCode('abc')).toEqual(null); + expect(getOperationResponseCode('-100')).toEqual(100); + }); +}); diff --git a/src/openApi/v3/parser/getOperationResponseCode.ts b/src/openApi/v3/parser/getOperationResponseCode.ts index 080cdc02..c5609938 100644 --- a/src/openApi/v3/parser/getOperationResponseCode.ts +++ b/src/openApi/v3/parser/getOperationResponseCode.ts @@ -8,7 +8,7 @@ export function getOperationResponseCode(value: string | 'default'): number | nu if (/[0-9]+/g.test(value)) { const code = parseInt(value); if (Number.isInteger(code)) { - return code; + return Math.abs(code); } } diff --git a/src/openApi/v3/parser/getServiceClassName.ts b/src/openApi/v3/parser/getServiceClassName.ts index 94131a3d..4f116a5a 100644 --- a/src/openApi/v3/parser/getServiceClassName.ts +++ b/src/openApi/v3/parser/getServiceClassName.ts @@ -5,7 +5,7 @@ import camelCase from 'camelcase'; * the input string to PascalCase and appends the "Service" prefix if needed. */ export function getServiceClassName(value: string): string { - const clean = value.replace(/[^\w\s\-]+/g, '_').trim(); + const clean = value.replace(/[^\w\s\-]+/g, '-').trim(); const name = camelCase(clean, { pascalCase: true }); if (name && !name.endsWith('Service')) { return `${name}Service`; diff --git a/src/openApi/v3/parser/sortByRequired.ts b/src/openApi/v3/parser/sortByRequired.ts new file mode 100644 index 00000000..bfcd0c7b --- /dev/null +++ b/src/openApi/v3/parser/sortByRequired.ts @@ -0,0 +1,5 @@ +import { OperationParameter } from '../../../client/interfaces/OperationParameter'; + +export function sortByRequired(a: OperationParameter, b: OperationParameter): number { + return a.isRequired && !b.isRequired ? -1 : !a.isRequired && b.isRequired ? 1 : 0; +} diff --git a/test/__snapshots__/index.spec.js.snap b/test/__snapshots__/index.spec.js.snap index c99a8924..c7c6fc93 100644 --- a/test/__snapshots__/index.spec.js.snap +++ b/test/__snapshots__/index.spec.js.snap @@ -1894,6 +1894,46 @@ export class ParametersService { return result.body; } + /** + * @param parameterHeader This is the parameter that goes into the request header + * @param parameterQuery This is the parameter that goes into the request query params + * @param parameterForm This is the parameter that goes into the request form data + * @param parameterBody This is the parameter that is send as request body + * @param parameterPath1 This is the parameter that goes into the path + * @param parameterPath2 This is the parameter that goes into the path + * @param parameterPath3 This is the parameter that goes into the path + * @throws ApiError + */ + public static async callWithWeirdParameterNames( + parameterHeader: string, + parameterQuery: string, + parameterForm: string, + parameterBody: string, + parameterPath1?: string, + parameterPath2?: string, + parameterPath3?: string, + ): Promise { + + const result = await __request({ + method: 'get', + path: \`/api/v\${OpenAPI.VERSION}/parameters/\${parameterPath1}/\${parameterPath2}/\${parameterPath3}\`, + headers: { + 'parameter.header': parameterHeader, + }, + query: { + 'parameter-query': parameterQuery, + }, + formData: { + 'parameter_form': parameterForm, + }, + body: parameterBody, + }); + + catchGenericError(result); + + return result.body; + } + }" `; @@ -4006,28 +4046,28 @@ import { OpenAPI } from '../core/OpenAPI'; export class ComplexService { /** - * @param parameterReference Parameter containing reference * @param parameterObject Parameter containing object + * @param parameterReference Parameter containing reference * @result ModelWithString Successful response * @throws ApiError */ public static async complexTypes( - parameterReference: ModelWithString, - parameterObject?: { + parameterObject: { first?: { second?: { third?: string, }, }, }, + parameterReference: ModelWithString, ): Promise> { const result = await __request({ method: 'get', path: \`/api/v\${OpenAPI.VERSION}/complex\`, query: { - 'parameterReference': parameterReference, 'parameterObject': parameterObject, + 'parameterReference': parameterReference, }, }); @@ -4050,7 +4090,7 @@ export class ComplexService { * @throws ApiError */ public static async complexParams( - id?: number, + id: number, requestBody?: { readonly key: string | null, name: string | null, @@ -4102,9 +4142,9 @@ export class DefaultsService { * @throws ApiError */ public static async callWithDefaultParameters( - parameterString: string = 'Hello World!', - parameterNumber: number = 123, - parameterBoolean: boolean = true, + parameterString: string | null = 'Hello World!', + parameterNumber: number | null = 123, + parameterBoolean: boolean | null = true, parameterEnum: ('Success' | 'Warning' | 'Error') = 'Success', parameterModel: ModelWithString | null = { \\"prop\\": \\"Hello World\\" @@ -4190,11 +4230,11 @@ export class ParametersService { * @throws ApiError */ public static async callWithParameters( - parameterHeader?: string, - parameterQuery?: string, - parameterForm?: string, - parameterCookie?: string, - requestBody?: ModelWithString, + parameterHeader: string | null, + parameterQuery: string | null, + parameterForm: string | null, + parameterCookie: string | null, + requestBody: ModelWithString | null, ): Promise { const result = await __request({ @@ -4220,6 +4260,99 @@ export class ParametersService { return result.body; } + /** + * @param parameterHeader This is the parameter that goes into the request header + * @param parameterQuery This is the parameter that goes into the request query params + * @param parameterForm This is the parameter that goes into the request form data + * @param parameterCookie This is the parameter that goes into the cookie + * @param requestBody This is the parameter that goes into the body + * @param parameterPath1 This is the parameter that goes into the path + * @param parameterPath2 This is the parameter that goes into the path + * @param parameterPath3 This is the parameter that goes into the path + * @throws ApiError + */ + public static async callWithWeirdParameterNames( + parameterHeader: string | null, + parameterQuery: string | null, + parameterForm: string | null, + parameterCookie: string | null, + requestBody: ModelWithString | null, + parameterPath1?: string, + parameterPath2?: string, + parameterPath3?: string, + ): Promise { + + const result = await __request({ + method: 'get', + path: \`/api/v\${OpenAPI.VERSION}/parameters/\${parameterPath1}/\${parameterPath2}/\${parameterPath3}\`, + cookies: { + 'PARAMETER-COOKIE': parameterCookie, + }, + headers: { + 'parameter.header': parameterHeader, + }, + query: { + 'parameter-query': parameterQuery, + }, + formData: { + 'parameter_form': parameterForm, + }, + body: requestBody, + }); + + catchGenericError(result); + + return result.body; + } + + /** + * @param requestBody This is a required parameter + * @param parameter This is an optional parameter + * @throws ApiError + */ + public static async getCallWithOptionalParam( + requestBody: ModelWithString, + parameter?: string, + ): Promise { + + const result = await __request({ + method: 'get', + path: \`/api/v\${OpenAPI.VERSION}/parameters/\`, + query: { + 'parameter': parameter, + }, + body: requestBody, + }); + + catchGenericError(result); + + return result.body; + } + + /** + * @param parameter This is a required parameter + * @param requestBody This is an optional parameter + * @throws ApiError + */ + public static async postCallWithOptionalParam( + parameter: string, + requestBody?: ModelWithString, + ): Promise { + + const result = await __request({ + method: 'post', + path: \`/api/v\${OpenAPI.VERSION}/parameters/\`, + query: { + 'parameter': parameter, + }, + body: requestBody, + }); + + catchGenericError(result); + + return result.body; + } + }" `; @@ -4456,12 +4589,12 @@ export class TypesService { */ public static async types( parameterNumber: number = 123, - parameterString: string = 'default', - parameterBoolean: boolean = true, + parameterString: string | null = 'default', + parameterBoolean: boolean | null = true, parameterObject: any = null, - parameterArray?: Array, - parameterDictionary?: any, - parameterEnum?: ('Success' | 'Warning' | 'Error'), + parameterArray: Array | null, + parameterDictionary: any, + parameterEnum: ('Success' | 'Warning' | 'Error') | null, id?: number, ): Promise { diff --git a/test/mock/v2/spec.json b/test/mock/v2/spec.json index 0499b244..83738bd0 100644 --- a/test/mock/v2/spec.json +++ b/test/mock/v2/spec.json @@ -98,6 +98,71 @@ ] } }, + "/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}": { + "get": { + "tags": [ + "Parameters" + ], + "operationId": "CallWithWeirdParameterNames", + "parameters": [ + { + "description": "This is the parameter that goes into the path", + "name": "parameter.path.1", + "in": "path", + "type": "string", + "required": false + }, + { + "description": "This is the parameter that goes into the path", + "name": "parameter-path-2", + "in": "path", + "type": "string", + "required": false + }, + { + "description": "This is the parameter that goes into the path", + "name": "PARAMETER-PATH-3", + "in": "path", + "type": "string", + "required": false + }, + { + "description": "This is the parameter that goes into the request header", + "name": "parameter.header", + "in": "header", + "type": "string", + "required": true + }, + { + "description": "This is the parameter that goes into the request query params", + "name": "parameter-query", + "in": "query", + "type": "string", + "required": true + }, + { + "description": "This is the parameter that goes into the request form data", + "name": "parameter_form", + "in": "formData", + "type": "string", + "required": true + }, + { + "description": "This is the parameter that is send as request body", + "name": "PARAMETER-BODY", + "in": "body", + "type": "string", + "required": true + }, + { + "name": "api-version", + "in": "path", + "type": "string", + "required": true + } + ] + } + }, "/api/v{api-version}/defaults": { "get": { "tags": [ diff --git a/test/mock/v3/spec.json b/test/mock/v3/spec.json index 3617acdc..8d02d419 100644 --- a/test/mock/v3/spec.json +++ b/test/mock/v3/spec.json @@ -113,6 +113,170 @@ ], "requestBody": { "description": "This is the parameter that goes into the body", + "required": true, + "nullable": true, + "content": { + "application/json": { + "description": "Message for default response", + "schema": { + "$ref": "#/components/schemas/ModelWithString" + } + } + } + } + } + }, + "/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}": { + "get": { + "tags": [ + "Parameters" + ], + "operationId": "CallWithWeirdParameterNames", + "parameters": [ + { + "description": "This is the parameter that goes into the path", + "name": "parameter.path.1", + "in": "path", + "required": false, + "nullable": false, + "schema": { + "type": "string" + } + }, + { + "description": "This is the parameter that goes into the path", + "name": "parameter-path-2", + "in": "path", + "required": false, + "nullable": false, + "schema": { + "type": "string" + } + }, + { + "description": "This is the parameter that goes into the path", + "name": "PARAMETER-PATH-3", + "in": "path", + "required": false, + "nullable": false, + "schema": { + "type": "string" + } + }, + { + "description": "This is the parameter that goes into the request header", + "name": "parameter.header", + "in": "header", + "required": true, + "nullable": true, + "schema": { + "type": "string" + } + }, + { + "description": "This is the parameter that goes into the request query params", + "name": "parameter-query", + "in": "query", + "required": true, + "nullable": true, + "schema": { + "type": "string" + } + }, + { + "description": "This is the parameter that goes into the request form data", + "name": "parameter_form", + "in": "formData", + "required": true, + "nullable": true, + "schema": { + "type": "string" + } + }, + { + "description": "This is the parameter that goes into the cookie", + "name": "PARAMETER-COOKIE", + "in": "cookie", + "required": true, + "nullable": true, + "schema": { + "type": "string" + } + }, + { + "name": "api-version", + "in": "path", + "required": true, + "nullable": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "This is the parameter that goes into the body", + "required": true, + "nullable": true, + "content": { + "application/json": { + "description": "Message for default response", + "schema": { + "$ref": "#/components/schemas/ModelWithString" + } + } + } + } + } + }, + "/api/v{api-version}/parameters/": { + "get": { + "tags": [ + "Parameters" + ], + "operationId": "getCallWithOptionalParam", + "parameters": [ + { + "description": "This is an optional parameter", + "name": "parameter", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "This is a required parameter", + "required": true, + "content": { + "application/json": { + "description": "Message for default response", + "schema": { + "$ref": "#/components/schemas/ModelWithString" + } + } + } + } + }, + "post": { + "tags": [ + "Parameters" + ], + "operationId": "postCallWithOptionalParam", + "parameters": [ + { + "description": "This is a required parameter", + "name": "parameter", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "This is an optional parameter", + "required": false, "content": { "application/json": { "description": "Message for default response",