diff --git a/.prettierrc.json b/.prettierrc.json index d1580670d..d7d68f56b 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -3,6 +3,6 @@ "singleQuote": true, "trailingComma": "es5", "arrowParens": "avoid", - "printWidth": 200, + "printWidth": 120, "tabWidth": 4 } diff --git a/src/client/interfaces/Schema.d.ts b/src/client/interfaces/Schema.d.ts index b45158d9f..26a116dbf 100644 --- a/src/client/interfaces/Schema.d.ts +++ b/src/client/interfaces/Schema.d.ts @@ -3,7 +3,18 @@ export interface Schema { isReadOnly: boolean; isRequired: boolean; isNullable: boolean; - format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password'; + format?: + | 'int32' + | 'int64' + | 'float' + | 'double' + | 'string' + | 'boolean' + | 'byte' + | 'binary' + | 'date' + | 'date-time' + | 'password'; maximum?: number; exclusiveMaximum?: boolean; minimum?: number; diff --git a/src/index.ts b/src/index.ts index 8eedb3765..01366416f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -66,7 +66,19 @@ export async function generate({ const client = parseV2(openApi); const clientFinal = postProcessClient(client); if (!write) break; - await writeClient(clientFinal, templates, output, httpClient, useOptions, useUnionTypes, exportCore, exportServices, exportModels, exportSchemas, request); + await writeClient( + clientFinal, + templates, + output, + httpClient, + useOptions, + useUnionTypes, + exportCore, + exportServices, + exportModels, + exportSchemas, + request + ); break; } @@ -74,7 +86,19 @@ export async function generate({ const client = parseV3(openApi); const clientFinal = postProcessClient(client); if (!write) break; - await writeClient(clientFinal, templates, output, httpClient, useOptions, useUnionTypes, exportCore, exportServices, exportModels, exportSchemas, request); + await writeClient( + clientFinal, + templates, + output, + httpClient, + useOptions, + useUnionTypes, + exportCore, + exportServices, + exportModels, + exportSchemas, + request + ); break; } } diff --git a/src/openApi/v2/interfaces/OpenApiHeader.d.ts b/src/openApi/v2/interfaces/OpenApiHeader.d.ts index ddb9c7d5b..81d2a74c9 100644 --- a/src/openApi/v2/interfaces/OpenApiHeader.d.ts +++ b/src/openApi/v2/interfaces/OpenApiHeader.d.ts @@ -7,7 +7,18 @@ import type { OpenApiItems } from './OpenApiItems'; export interface OpenApiHeader { description?: string; type: 'string' | 'number' | 'integer' | 'boolean' | 'array'; - format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password'; + format?: + | 'int32' + | 'int64' + | 'float' + | 'double' + | 'string' + | 'boolean' + | 'byte' + | 'binary' + | 'date' + | 'date-time' + | 'password'; items?: Dictionary; collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes'; default?: any; diff --git a/src/openApi/v2/interfaces/OpenApiItems.d.ts b/src/openApi/v2/interfaces/OpenApiItems.d.ts index de269d65e..fa1d0b944 100644 --- a/src/openApi/v2/interfaces/OpenApiItems.d.ts +++ b/src/openApi/v2/interfaces/OpenApiItems.d.ts @@ -5,7 +5,18 @@ import type { WithEnumExtension } from './Extensions/WithEnumExtension'; */ export interface OpenApiItems extends WithEnumExtension { type?: string; - format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password'; + format?: + | 'int32' + | 'int64' + | 'float' + | 'double' + | 'string' + | 'boolean' + | 'byte' + | 'binary' + | 'date' + | 'date-time' + | 'password'; items?: OpenApiItems; collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes'; default?: any; diff --git a/src/openApi/v2/interfaces/OpenApiParameter.d.ts b/src/openApi/v2/interfaces/OpenApiParameter.d.ts index 07f895ad2..bdff0bb6f 100644 --- a/src/openApi/v2/interfaces/OpenApiParameter.d.ts +++ b/src/openApi/v2/interfaces/OpenApiParameter.d.ts @@ -14,7 +14,18 @@ export interface OpenApiParameter extends OpenApiReference, WithEnumExtension, W required?: boolean; schema?: OpenApiSchema; type?: string; - format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password'; + format?: + | 'int32' + | 'int64' + | 'float' + | 'double' + | 'string' + | 'boolean' + | 'byte' + | 'binary' + | 'date' + | 'date-time' + | 'password'; allowEmptyValue?: boolean; items?: OpenApiItems; collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi'; diff --git a/src/openApi/v2/interfaces/OpenApiSchema.d.ts b/src/openApi/v2/interfaces/OpenApiSchema.d.ts index 4ac0f559f..09970dbb6 100644 --- a/src/openApi/v2/interfaces/OpenApiSchema.d.ts +++ b/src/openApi/v2/interfaces/OpenApiSchema.d.ts @@ -28,7 +28,18 @@ export interface OpenApiSchema extends OpenApiReference, WithEnumExtension, With required?: string[]; enum?: (string | number)[]; type?: string; - format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password'; + format?: + | 'int32' + | 'int64' + | 'float' + | 'double' + | 'string' + | 'boolean' + | 'byte' + | 'binary' + | 'date' + | 'date-time' + | 'password'; items?: OpenApiSchema; allOf?: OpenApiSchema[]; properties?: Dictionary; diff --git a/src/openApi/v2/parser/getComment.spec.ts b/src/openApi/v2/parser/getComment.spec.ts index 208a7d233..c3bde3836 100644 --- a/src/openApi/v2/parser/getComment.spec.ts +++ b/src/openApi/v2/parser/getComment.spec.ts @@ -4,12 +4,25 @@ import { getComment } from './getComment'; describe('getComment', () => { it('should parse comments', () => { - const multiline = 'Testing multiline comments.' + EOL + ' * This must go to the next line.' + EOL + ' * ' + EOL + ' * This will contain a break.'; + const multiline = + 'Testing multiline comments.' + + EOL + + ' * This must go to the next line.' + + EOL + + ' * ' + + EOL + + ' * This will contain a break.'; expect(getComment('')).toEqual(null); expect(getComment('Hello')).toEqual('Hello'); expect(getComment('Hello World!')).toEqual('Hello World!'); expect(getComment('Testing */escape/*')).toEqual('Testing *_/escape/*'); - expect(getComment('Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.')).toEqual(multiline); - expect(getComment('Testing multiline comments.\r\nThis must go to the next line.\r\n\r\nThis will contain a break.')).toEqual(multiline); + expect( + getComment('Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.') + ).toEqual(multiline); + expect( + getComment( + 'Testing multiline comments.\r\nThis must go to the next line.\r\n\r\nThis will contain a break.' + ) + ).toEqual(multiline); }); }); diff --git a/src/openApi/v2/parser/getModel.ts b/src/openApi/v2/parser/getModel.ts index dd341e383..a81f5e766 100644 --- a/src/openApi/v2/parser/getModel.ts +++ b/src/openApi/v2/parser/getModel.ts @@ -10,7 +10,12 @@ import { getModelComposition } from './getModelComposition'; import { getModelProperties } from './getModelProperties'; import { getType } from './getType'; -export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefinition: boolean = false, name: string = ''): Model { +export function getModel( + openApi: OpenApi, + definition: OpenApiSchema, + isDefinition: boolean = false, + name: string = '' +): Model { const model: Model = { name, export: 'interface', diff --git a/src/openApi/v2/parser/getModelComposition.ts b/src/openApi/v2/parser/getModelComposition.ts index 3c0f497fd..1e9619c3a 100644 --- a/src/openApi/v2/parser/getModelComposition.ts +++ b/src/openApi/v2/parser/getModelComposition.ts @@ -9,7 +9,13 @@ import { getRequiredPropertiesFromComposition } from './getRequiredPropertiesFro // Fix for circular dependency export type GetModelFn = typeof getModel; -export function getModelComposition(openApi: OpenApi, definition: OpenApiSchema, definitions: OpenApiSchema[], type: 'one-of' | 'any-of' | 'all-of', getModel: GetModelFn): ModelComposition { +export function getModelComposition( + openApi: OpenApi, + definition: OpenApiSchema, + definitions: OpenApiSchema[], + type: 'one-of' | 'any-of' | 'all-of', + getModel: GetModelFn +): ModelComposition { const composition: ModelComposition = { type, imports: [], @@ -35,7 +41,12 @@ export function getModelComposition(openApi: OpenApi, definition: OpenApiSchema, }); if (definition.required) { - const requiredProperties = getRequiredPropertiesFromComposition(openApi, definition.required, definitions, getModel); + const requiredProperties = getRequiredPropertiesFromComposition( + openApi, + definition.required, + definitions, + getModel + ); requiredProperties.forEach(requiredProperty => { composition.imports.push(...requiredProperty.imports); composition.enums.push(...requiredProperty.enums); diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index 51d12406b..7c6dc834b 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -12,7 +12,13 @@ import { getOperationResponses } from './getOperationResponses'; import { getOperationResults } from './getOperationResults'; import { getServiceClassName } from './getServiceClassName'; -export function getOperation(openApi: OpenApi, url: string, method: string, op: OpenApiOperation, pathParams: OperationParameters): Operation { +export function getOperation( + openApi: OpenApi, + url: string, + method: string, + op: OpenApiOperation, + pathParams: OperationParameters +): Operation { const serviceName = op.tags?.[0] || 'Service'; const serviceClassName = getServiceClassName(serviceName); const operationNameFallback = `${method}${serviceClassName}`; diff --git a/src/openApi/v2/parser/getOperationParameterDefault.ts b/src/openApi/v2/parser/getOperationParameterDefault.ts index a196d5dcc..c7f97e149 100644 --- a/src/openApi/v2/parser/getOperationParameterDefault.ts +++ b/src/openApi/v2/parser/getOperationParameterDefault.ts @@ -1,7 +1,10 @@ import type { OperationParameter } from '../../../client/interfaces/OperationParameter'; import type { OpenApiParameter } from '../interfaces/OpenApiParameter'; -export function getOperationParameterDefault(parameter: OpenApiParameter, operationParameter: OperationParameter): string | undefined { +export function getOperationParameterDefault( + parameter: OpenApiParameter, + operationParameter: OperationParameter +): string | undefined { if (parameter.default === undefined) { return; } diff --git a/src/openApi/v2/parser/getOperationPath.spec.ts b/src/openApi/v2/parser/getOperationPath.spec.ts index 41df428d1..d69ac8999 100644 --- a/src/openApi/v2/parser/getOperationPath.spec.ts +++ b/src/openApi/v2/parser/getOperationPath.spec.ts @@ -2,7 +2,9 @@ import { getOperationPath } from './getOperationPath'; describe('getOperationPath', () => { it('should produce correct result', () => { - 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}/{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/{foobar}')).toEqual('/api/${foobar}'); diff --git a/src/openApi/v2/parser/getOperationResponse.ts b/src/openApi/v2/parser/getOperationResponse.ts index 1e0e9ab42..86174f6b6 100644 --- a/src/openApi/v2/parser/getOperationResponse.ts +++ b/src/openApi/v2/parser/getOperationResponse.ts @@ -6,7 +6,11 @@ import { getComment } from './getComment'; import { getModel } from './getModel'; import { getType } from './getType'; -export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse, responseCode: number): OperationResponse { +export function getOperationResponse( + openApi: OpenApi, + response: OpenApiResponse, + responseCode: number +): OperationResponse { const operationResponse: OperationResponse = { in: 'response', name: '', diff --git a/src/openApi/v2/parser/getRef.ts b/src/openApi/v2/parser/getRef.ts index c2390a508..6e67d2089 100644 --- a/src/openApi/v2/parser/getRef.ts +++ b/src/openApi/v2/parser/getRef.ts @@ -17,7 +17,9 @@ export function getRef(openApi: OpenApi, item: T & OpenApiReference): T { // if we cannot find it, then we throw an error. let result: any = openApi; paths.forEach(path => { - const decodedPath = decodeURIComponent(path.replace(ESCAPED_REF_SLASH, '/').replace(ESCAPED_REF_TILDE, '~')); + const decodedPath = decodeURIComponent( + path.replace(ESCAPED_REF_SLASH, '/').replace(ESCAPED_REF_TILDE, '~') + ); if (result.hasOwnProperty(decodedPath)) { result = result[decodedPath]; } else { diff --git a/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts b/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts index 25672cc5d..0794d6162 100644 --- a/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts +++ b/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts @@ -7,7 +7,12 @@ import { getRef } from './getRef'; // Fix for circular dependency export type GetModelFn = typeof getModel; -export function getRequiredPropertiesFromComposition(openApi: OpenApi, required: string[], definitions: OpenApiSchema[], getModel: GetModelFn): Model[] { +export function getRequiredPropertiesFromComposition( + openApi: OpenApi, + required: string[], + definitions: OpenApiSchema[], + getModel: GetModelFn +): Model[] { return definitions .reduce((properties, definition) => { if (definition.$ref) { diff --git a/src/openApi/v3/interfaces/OpenApiSchema.d.ts b/src/openApi/v3/interfaces/OpenApiSchema.d.ts index 1e00fb031..a51456f3b 100644 --- a/src/openApi/v3/interfaces/OpenApiSchema.d.ts +++ b/src/openApi/v3/interfaces/OpenApiSchema.d.ts @@ -34,7 +34,18 @@ export interface OpenApiSchema extends OpenApiReference, WithEnumExtension { properties?: Dictionary; additionalProperties?: boolean | OpenApiSchema; description?: string; - format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password'; + format?: + | 'int32' + | 'int64' + | 'float' + | 'double' + | 'string' + | 'boolean' + | 'byte' + | 'binary' + | 'date' + | 'date-time' + | 'password'; default?: any; nullable?: boolean; discriminator?: OpenApiDiscriminator; diff --git a/src/openApi/v3/parser/getComment.spec.ts b/src/openApi/v3/parser/getComment.spec.ts index 208a7d233..c3bde3836 100644 --- a/src/openApi/v3/parser/getComment.spec.ts +++ b/src/openApi/v3/parser/getComment.spec.ts @@ -4,12 +4,25 @@ import { getComment } from './getComment'; describe('getComment', () => { it('should parse comments', () => { - const multiline = 'Testing multiline comments.' + EOL + ' * This must go to the next line.' + EOL + ' * ' + EOL + ' * This will contain a break.'; + const multiline = + 'Testing multiline comments.' + + EOL + + ' * This must go to the next line.' + + EOL + + ' * ' + + EOL + + ' * This will contain a break.'; expect(getComment('')).toEqual(null); expect(getComment('Hello')).toEqual('Hello'); expect(getComment('Hello World!')).toEqual('Hello World!'); expect(getComment('Testing */escape/*')).toEqual('Testing *_/escape/*'); - expect(getComment('Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.')).toEqual(multiline); - expect(getComment('Testing multiline comments.\r\nThis must go to the next line.\r\n\r\nThis will contain a break.')).toEqual(multiline); + expect( + getComment('Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.') + ).toEqual(multiline); + expect( + getComment( + 'Testing multiline comments.\r\nThis must go to the next line.\r\n\r\nThis will contain a break.' + ) + ).toEqual(multiline); }); }); diff --git a/src/openApi/v3/parser/getContent.ts b/src/openApi/v3/parser/getContent.ts index 7559cf8d1..46da5ef7f 100644 --- a/src/openApi/v3/parser/getContent.ts +++ b/src/openApi/v3/parser/getContent.ts @@ -4,21 +4,38 @@ import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiMediaType } from '../interfaces/OpenApiMediaType'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; -export function getContent(openApi: OpenApi, content: Dictionary): OpenApiSchema | null { - const basicMediaTypeSchema = - content['application/json-patch+json']?.schema || - content['application/json']?.schema || - content['text/json']?.schema || - content['text/plain']?.schema || - content['multipart/mixed']?.schema || - content['multipart/related']?.schema || - content['multipart/batch']?.schema; +export interface Content { + mediaType: string; + schema: OpenApiSchema; +} + +const BASIC_MEDIA_TYPES = [ + 'application/json-patch+json', + 'application/json', + 'text/json', + 'text/plain', + 'multipart/form-data', + 'multipart/mixed', + 'multipart/related', + 'multipart/batch', +]; - if (basicMediaTypeSchema) { - return basicMediaTypeSchema; +export function getContent(openApi: OpenApi, content: Dictionary): Content | null { + const basicMedia = BASIC_MEDIA_TYPES.find(mediaType => isDefined(content[mediaType]?.schema)); + if (basicMedia) { + return { + mediaType: basicMedia, + schema: content[basicMedia], + }; } - const mediaTypes = Object.values(content); - const mediaType = mediaTypes.find(mediaType => isDefined(mediaType.schema)); - return mediaType?.schema || null; + const otherMediaTypes = Object.keys(content); + const otherMediaType = otherMediaTypes.find(mediaType => isDefined(content[mediaType]?.schema)); + if (otherMediaType) { + return { + mediaType: otherMediaType, + schema: content[otherMediaType], + }; + } + return null; } diff --git a/src/openApi/v3/parser/getMediaType.ts b/src/openApi/v3/parser/getMediaType.ts index 98840ffce..273b16544 100644 --- a/src/openApi/v3/parser/getMediaType.ts +++ b/src/openApi/v3/parser/getMediaType.ts @@ -2,9 +2,17 @@ import type { Dictionary } from '../../../utils/types'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiMediaType } from '../interfaces/OpenApiMediaType'; +const supportedMediaTypes = [ + 'application/json-patch+json', + 'application/json', + 'text/json', + 'text/plain', + 'multipart/form-data', + 'multipart/mixed', + 'multipart/related', + 'multipart/batch', +]; + export function getMediaType(openApi: OpenApi, content: Dictionary): string | null { - return ( - Object.keys(content).find(key => ['application/json-patch+json', 'application/json', 'text/json', 'text/plain', 'multipart/mixed', 'multipart/related', 'multipart/batch'].includes(key)) || - null - ); + return Object.keys(content).find(key => supportedMediaTypes.includes(key)) || null; } diff --git a/src/openApi/v3/parser/getModel.ts b/src/openApi/v3/parser/getModel.ts index b5be173a3..23e151db7 100644 --- a/src/openApi/v3/parser/getModel.ts +++ b/src/openApi/v3/parser/getModel.ts @@ -11,7 +11,12 @@ import { getModelDefault } from './getModelDefault'; import { getModelProperties } from './getModelProperties'; import { getType } from './getType'; -export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefinition: boolean = false, name: string = ''): Model { +export function getModel( + openApi: OpenApi, + definition: OpenApiSchema, + isDefinition: boolean = false, + name: string = '' +): Model { const model: Model = { name, export: 'interface', diff --git a/src/openApi/v3/parser/getModelComposition.ts b/src/openApi/v3/parser/getModelComposition.ts index 3c0f497fd..1e9619c3a 100644 --- a/src/openApi/v3/parser/getModelComposition.ts +++ b/src/openApi/v3/parser/getModelComposition.ts @@ -9,7 +9,13 @@ import { getRequiredPropertiesFromComposition } from './getRequiredPropertiesFro // Fix for circular dependency export type GetModelFn = typeof getModel; -export function getModelComposition(openApi: OpenApi, definition: OpenApiSchema, definitions: OpenApiSchema[], type: 'one-of' | 'any-of' | 'all-of', getModel: GetModelFn): ModelComposition { +export function getModelComposition( + openApi: OpenApi, + definition: OpenApiSchema, + definitions: OpenApiSchema[], + type: 'one-of' | 'any-of' | 'all-of', + getModel: GetModelFn +): ModelComposition { const composition: ModelComposition = { type, imports: [], @@ -35,7 +41,12 @@ export function getModelComposition(openApi: OpenApi, definition: OpenApiSchema, }); if (definition.required) { - const requiredProperties = getRequiredPropertiesFromComposition(openApi, definition.required, definitions, getModel); + const requiredProperties = getRequiredPropertiesFromComposition( + openApi, + definition.required, + definitions, + getModel + ); requiredProperties.forEach(requiredProperty => { composition.imports.push(...requiredProperty.imports); composition.enums.push(...requiredProperty.enums); diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts index 347a38212..e13c90946 100644 --- a/src/openApi/v3/parser/getOperation.ts +++ b/src/openApi/v3/parser/getOperation.ts @@ -16,7 +16,13 @@ import { getRef } from './getRef'; import { getServiceClassName } from './getServiceClassName'; import { sortByRequired } from './sortByRequired'; -export function getOperation(openApi: OpenApi, url: string, method: string, op: OpenApiOperation, pathParams: OperationParameters): Operation { +export function getOperation( + openApi: OpenApi, + url: string, + method: string, + op: OpenApiOperation, + pathParams: OperationParameters +): Operation { const serviceName = op.tags?.[0] || 'Service'; const serviceClassName = getServiceClassName(serviceName); const operationNameFallback = `${method}${serviceClassName}`; diff --git a/src/openApi/v3/parser/getOperationPath.spec.ts b/src/openApi/v3/parser/getOperationPath.spec.ts index 41df428d1..d69ac8999 100644 --- a/src/openApi/v3/parser/getOperationPath.spec.ts +++ b/src/openApi/v3/parser/getOperationPath.spec.ts @@ -2,7 +2,9 @@ import { getOperationPath } from './getOperationPath'; describe('getOperationPath', () => { it('should produce correct result', () => { - 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}/{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/{foobar}')).toEqual('/api/${foobar}'); diff --git a/src/openApi/v3/parser/getOperationRequestBody.ts b/src/openApi/v3/parser/getOperationRequestBody.ts index 1f4ef3f85..04593d5cc 100644 --- a/src/openApi/v3/parser/getOperationRequestBody.ts +++ b/src/openApi/v3/parser/getOperationRequestBody.ts @@ -4,15 +4,14 @@ import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiRequestBody } from '../interfaces/OpenApiRequestBody'; import { getComment } from './getComment'; import { getContent } from './getContent'; -import { getMediaType } from './getMediaType'; import { getModel } from './getModel'; import { getType } from './getType'; export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequestBody): OperationParameter { const requestBody: OperationParameter = { in: 'body', - prop: 'body', export: 'interface', + prop: 'requestBody', name: 'requestBody', type: 'any', base: 'any', @@ -32,11 +31,15 @@ export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequ }; if (parameter.content) { - const schema = getContent(openApi, parameter.content); - if (schema) { - requestBody.mediaType = getMediaType(openApi, parameter.content); - if (schema?.$ref) { - const model = getType(schema.$ref); + const content = getContent(openApi, parameter.content); + if (content) { + if (content.mediaType === 'multipart/form-data') { + requestBody.in = 'formData'; + requestBody.name = 'formData'; + requestBody.prop = 'formData'; + } + if (content.schema.$ref) { + const model = getType(content.schema.$ref); requestBody.export = 'reference'; requestBody.type = model.type; requestBody.base = model.base; @@ -44,7 +47,7 @@ export function getOperationRequestBody(openApi: OpenApi, parameter: OpenApiRequ requestBody.imports.push(...model.imports); return requestBody; } else { - const model = getModel(openApi, schema); + const model = getModel(openApi, content.schema); requestBody.export = model.export; requestBody.type = model.type; requestBody.base = model.base; diff --git a/src/openApi/v3/parser/getOperationResponse.ts b/src/openApi/v3/parser/getOperationResponse.ts index 45abaa0a4..560a1aee7 100644 --- a/src/openApi/v3/parser/getOperationResponse.ts +++ b/src/openApi/v3/parser/getOperationResponse.ts @@ -7,7 +7,11 @@ import { getContent } from './getContent'; import { getModel } from './getModel'; import { getType } from './getType'; -export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse, responseCode: number): OperationResponse { +export function getOperationResponse( + openApi: OpenApi, + response: OpenApiResponse, + responseCode: number +): OperationResponse { const operationResponse: OperationResponse = { in: 'response', name: '', @@ -29,10 +33,10 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse }; if (response.content) { - const schema = getContent(openApi, response.content); - if (schema) { - if (schema?.$ref) { - const model = getType(schema.$ref); + const content = getContent(openApi, response.content); + if (content) { + if (content.schema.$ref) { + const model = getType(content.schema.$ref); operationResponse.export = 'reference'; operationResponse.type = model.type; operationResponse.base = model.base; @@ -40,7 +44,7 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse operationResponse.imports.push(...model.imports); return operationResponse; } else { - const model = getModel(openApi, schema); + const model = getModel(openApi, content.schema); operationResponse.export = model.export; operationResponse.type = model.type; operationResponse.base = model.base; diff --git a/src/openApi/v3/parser/getRef.ts b/src/openApi/v3/parser/getRef.ts index 9888e185c..d37373118 100644 --- a/src/openApi/v3/parser/getRef.ts +++ b/src/openApi/v3/parser/getRef.ts @@ -17,7 +17,9 @@ export function getRef(openApi: OpenApi, item: T & OpenApiReference): T { // if we cannot find it, then we throw an error. let result: any = openApi; paths.forEach(path => { - const decodedPath = decodeURIComponent(path.replace(ESCAPED_REF_SLASH, '/').replace(ESCAPED_REF_TILDE, '~')); + const decodedPath = decodeURIComponent( + path.replace(ESCAPED_REF_SLASH, '/').replace(ESCAPED_REF_TILDE, '~') + ); if (result.hasOwnProperty(decodedPath)) { result = result[decodedPath]; } else { diff --git a/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts b/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts index 25672cc5d..0794d6162 100644 --- a/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts +++ b/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts @@ -7,7 +7,12 @@ import { getRef } from './getRef'; // Fix for circular dependency export type GetModelFn = typeof getModel; -export function getRequiredPropertiesFromComposition(openApi: OpenApi, required: string[], definitions: OpenApiSchema[], getModel: GetModelFn): Model[] { +export function getRequiredPropertiesFromComposition( + openApi: OpenApi, + required: string[], + definitions: OpenApiSchema[], + getModel: GetModelFn +): Model[] { return definitions .reduce((properties, definition) => { if (definition.$ref) { diff --git a/src/templates/exportService.hbs b/src/templates/exportService.hbs index 01e665cdd..0a8807f14 100644 --- a/src/templates/exportService.hbs +++ b/src/templates/exportService.hbs @@ -69,7 +69,12 @@ export class {{{name}}} { }, {{/if}} {{#if parametersBody}} + {{#equals parametersBody.in 'formData'}} + formData: {{{parametersBody.name}}}, + {{/equals}} + {{#equals parametersBody.in 'body'}} body: {{{parametersBody.name}}}, + {{/equals}} {{#if parametersBody.mediaType}} mediaType: '{{{parametersBody.mediaType}}}', {{/if}} diff --git a/src/utils/registerHandlebarHelpers.ts b/src/utils/registerHandlebarHelpers.ts index 9c4da382f..a937d1cfe 100644 --- a/src/utils/registerHandlebarHelpers.ts +++ b/src/utils/registerHandlebarHelpers.ts @@ -5,50 +5,78 @@ import { Model } from '../client/interfaces/Model'; import { HttpClient } from '../HttpClient'; import { unique } from './unique'; -export function registerHandlebarHelpers(root: { httpClient: HttpClient; useOptions: boolean; useUnionTypes: boolean }): void { - Handlebars.registerHelper('equals', function (this: any, a: string, b: string, options: Handlebars.HelperOptions): string { - return a === b ? options.fn(this) : options.inverse(this); - }); +export function registerHandlebarHelpers(root: { + httpClient: HttpClient; + useOptions: boolean; + useUnionTypes: boolean; +}): void { + Handlebars.registerHelper( + 'equals', + function (this: any, a: string, b: string, options: Handlebars.HelperOptions): string { + return a === b ? options.fn(this) : options.inverse(this); + } + ); - Handlebars.registerHelper('notEquals', function (this: any, a: string, b: string, options: Handlebars.HelperOptions): string { - return a !== b ? options.fn(this) : options.inverse(this); - }); + Handlebars.registerHelper( + 'notEquals', + function (this: any, a: string, b: string, options: Handlebars.HelperOptions): string { + return a !== b ? options.fn(this) : options.inverse(this); + } + ); - Handlebars.registerHelper('containsSpaces', function (this: any, value: string, options: Handlebars.HelperOptions): string { - return /\s+/.test(value) ? options.fn(this) : options.inverse(this); - }); + Handlebars.registerHelper( + 'containsSpaces', + function (this: any, value: string, options: Handlebars.HelperOptions): string { + return /\s+/.test(value) ? options.fn(this) : options.inverse(this); + } + ); - Handlebars.registerHelper('union', function (this: any, properties: Model[], parent: string | undefined, options: Handlebars.HelperOptions) { - const type = Handlebars.partials['type']; - const types = properties.map(property => type({ ...root, ...property, parent })); - const uniqueTypes = types.filter(unique); - let uniqueTypesString = uniqueTypes.join(' | '); - if (uniqueTypes.length > 1) { - uniqueTypesString = `(${uniqueTypesString})`; + Handlebars.registerHelper( + 'union', + function (this: any, properties: Model[], parent: string | undefined, options: Handlebars.HelperOptions) { + const type = Handlebars.partials['type']; + const types = properties.map(property => type({ ...root, ...property, parent })); + const uniqueTypes = types.filter(unique); + let uniqueTypesString = uniqueTypes.join(' | '); + if (uniqueTypes.length > 1) { + uniqueTypesString = `(${uniqueTypesString})`; + } + return options.fn(uniqueTypesString); } - return options.fn(uniqueTypesString); - }); + ); - Handlebars.registerHelper('intersection', function (this: any, properties: Model[], parent: string | undefined, options: Handlebars.HelperOptions) { - const type = Handlebars.partials['type']; - const types = properties.map(property => type({ ...root, ...property, parent })); - const uniqueTypes = types.filter(unique); - let uniqueTypesString = uniqueTypes.join(' & '); - if (uniqueTypes.length > 1) { - uniqueTypesString = `(${uniqueTypesString})`; + Handlebars.registerHelper( + 'intersection', + function (this: any, properties: Model[], parent: string | undefined, options: Handlebars.HelperOptions) { + const type = Handlebars.partials['type']; + const types = properties.map(property => type({ ...root, ...property, parent })); + const uniqueTypes = types.filter(unique); + let uniqueTypesString = uniqueTypes.join(' & '); + if (uniqueTypes.length > 1) { + uniqueTypesString = `(${uniqueTypesString})`; + } + return options.fn(uniqueTypesString); } - return options.fn(uniqueTypesString); - }); + ); - Handlebars.registerHelper('enumerator', function (this: any, enumerators: Enum[], parent: string | undefined, name: string | undefined, options: Handlebars.HelperOptions) { - if (!root.useUnionTypes && parent && name) { - return `${parent}.${name}`; + Handlebars.registerHelper( + 'enumerator', + function ( + this: any, + enumerators: Enum[], + parent: string | undefined, + name: string | undefined, + options: Handlebars.HelperOptions + ) { + if (!root.useUnionTypes && parent && name) { + return `${parent}.${name}`; + } + return options.fn( + enumerators + .map(enumerator => enumerator.value) + .filter(unique) + .join(' | ') + ); } - return options.fn( - enumerators - .map(enumerator => enumerator.value) - .filter(unique) - .join(' | ') - ); - }); + ); } diff --git a/src/utils/registerHandlebarTemplates.ts b/src/utils/registerHandlebarTemplates.ts index fbea55132..a281b6455 100644 --- a/src/utils/registerHandlebarTemplates.ts +++ b/src/utils/registerHandlebarTemplates.ts @@ -97,7 +97,11 @@ export interface Templates { * Read all the Handlebar templates that we need and return on wrapper object * so we can easily access the templates in out generator / write functions. */ -export function registerHandlebarTemplates(root: { httpClient: HttpClient; useOptions: boolean; useUnionTypes: boolean }): Templates { +export function registerHandlebarTemplates(root: { + httpClient: HttpClient; + useOptions: boolean; + useUnionTypes: boolean; +}): Templates { registerHandlebarHelpers(root); // Main templates (entry points for the files we write to disk) diff --git a/src/utils/writeClient.ts b/src/utils/writeClient.ts index 94aecba50..8ccf5539a 100644 --- a/src/utils/writeClient.ts +++ b/src/utils/writeClient.ts @@ -57,7 +57,14 @@ export async function writeClient( if (exportServices) { await rmdir(outputPathServices); await mkdir(outputPathServices); - await writeClientServices(client.services, templates, outputPathServices, httpClient, useUnionTypes, useOptions); + await writeClientServices( + client.services, + templates, + outputPathServices, + httpClient, + useUnionTypes, + useOptions + ); } if (exportSchemas) { @@ -74,6 +81,15 @@ export async function writeClient( if (exportCore || exportServices || exportSchemas || exportModels) { await mkdir(outputPath); - await writeClientIndex(client, templates, outputPath, useUnionTypes, exportCore, exportServices, exportModels, exportSchemas); + await writeClientIndex( + client, + templates, + outputPath, + useUnionTypes, + exportCore, + exportServices, + exportModels, + exportSchemas + ); } } diff --git a/src/utils/writeClientCore.ts b/src/utils/writeClientCore.ts index 050dfbb90..557956af9 100644 --- a/src/utils/writeClientCore.ts +++ b/src/utils/writeClientCore.ts @@ -13,7 +13,13 @@ import { Templates } from './registerHandlebarTemplates'; * @param httpClient The selected httpClient (fetch, xhr, node or axios) * @param request: Path to custom request file */ -export async function writeClientCore(client: Client, templates: Templates, outputPath: string, httpClient: HttpClient, request?: string): Promise { +export async function writeClientCore( + client: Client, + templates: Templates, + outputPath: string, + httpClient: HttpClient, + request?: string +): Promise { const context = { httpClient, server: client.server, diff --git a/src/utils/writeClientModels.ts b/src/utils/writeClientModels.ts index acd508e1c..2a2b087d4 100644 --- a/src/utils/writeClientModels.ts +++ b/src/utils/writeClientModels.ts @@ -14,7 +14,13 @@ import { Templates } from './registerHandlebarTemplates'; * @param httpClient The selected httpClient (fetch, xhr, node or axios) * @param useUnionTypes Use union types instead of enums */ -export async function writeClientModels(models: Model[], templates: Templates, outputPath: string, httpClient: HttpClient, useUnionTypes: boolean): Promise { +export async function writeClientModels( + models: Model[], + templates: Templates, + outputPath: string, + httpClient: HttpClient, + useUnionTypes: boolean +): Promise { for (const model of models) { const file = resolve(outputPath, `${model.name}.ts`); const templateResult = templates.exports.model({ diff --git a/src/utils/writeClientSchemas.ts b/src/utils/writeClientSchemas.ts index bf181722f..f307b01a6 100644 --- a/src/utils/writeClientSchemas.ts +++ b/src/utils/writeClientSchemas.ts @@ -14,7 +14,13 @@ import { Templates } from './registerHandlebarTemplates'; * @param httpClient The selected httpClient (fetch, xhr, node or axios) * @param useUnionTypes Use union types instead of enums */ -export async function writeClientSchemas(models: Model[], templates: Templates, outputPath: string, httpClient: HttpClient, useUnionTypes: boolean): Promise { +export async function writeClientSchemas( + models: Model[], + templates: Templates, + outputPath: string, + httpClient: HttpClient, + useUnionTypes: boolean +): Promise { for (const model of models) { const file = resolve(outputPath, `$${model.name}.ts`); const templateResult = templates.exports.schema({ diff --git a/src/utils/writeClientServices.ts b/src/utils/writeClientServices.ts index 96905fcf6..f7793b346 100644 --- a/src/utils/writeClientServices.ts +++ b/src/utils/writeClientServices.ts @@ -17,7 +17,14 @@ const VERSION_TEMPLATE_STRING = 'OpenAPI.VERSION'; * @param useUnionTypes Use union types instead of enums * @param useOptions Use options or arguments functions */ -export async function writeClientServices(services: Service[], templates: Templates, outputPath: string, httpClient: HttpClient, useUnionTypes: boolean, useOptions: boolean): Promise { +export async function writeClientServices( + services: Service[], + templates: Templates, + outputPath: string, + httpClient: HttpClient, + useUnionTypes: boolean, + useOptions: boolean +): Promise { for (const service of services) { const file = resolve(outputPath, `${service.name}.ts`); const useVersion = service.operations.some(operation => operation.path.includes(VERSION_TEMPLATE_STRING)); diff --git a/test/index.js b/test/index.js index 9ff966a1f..fe244b259 100644 --- a/test/index.js +++ b/test/index.js @@ -19,7 +19,7 @@ async function generateV2() { async function generateV3() { await OpenAPI.generate({ - input: './test/spec/v3.json', + input: './test/spec/spec.json', output: './test/generated/v3/', httpClient: OpenAPI.HttpClient.FETCH, useOptions: false, @@ -33,7 +33,7 @@ async function generateV3() { } async function generate() { - await generateV2(); + // await generateV2(); await generateV3(); }