Skip to content

Commit

Permalink
- Working on support for formData in v3
Browse files Browse the repository at this point in the history
  • Loading branch information
Ferdi Koomen committed Oct 28, 2021
1 parent fa2bab0 commit 433b4f1
Show file tree
Hide file tree
Showing 37 changed files with 405 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "avoid",
"printWidth": 200,
"printWidth": 120,
"tabWidth": 4
}
13 changes: 12 additions & 1 deletion src/client/interfaces/Schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 26 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,39 @@ 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;
}

case OpenApiVersion.V3: {
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;
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/openApi/v2/interfaces/OpenApiHeader.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenApiItems>;
collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes';
default?: any;
Expand Down
13 changes: 12 additions & 1 deletion src/openApi/v2/interfaces/OpenApiItems.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 12 additions & 1 deletion src/openApi/v2/interfaces/OpenApiParameter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
13 changes: 12 additions & 1 deletion src/openApi/v2/interfaces/OpenApiSchema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenApiSchema>;
Expand Down
19 changes: 16 additions & 3 deletions src/openApi/v2/parser/getComment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
7 changes: 6 additions & 1 deletion src/openApi/v2/parser/getModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
15 changes: 13 additions & 2 deletions src/openApi/v2/parser/getModelComposition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
Expand All @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion src/openApi/v2/parser/getOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand Down
5 changes: 4 additions & 1 deletion src/openApi/v2/parser/getOperationParameterDefault.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion src/openApi/v2/parser/getOperationPath.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}');
Expand Down
6 changes: 5 additions & 1 deletion src/openApi/v2/parser/getOperationResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
Expand Down
4 changes: 3 additions & 1 deletion src/openApi/v2/parser/getRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export function getRef<T>(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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
13 changes: 12 additions & 1 deletion src/openApi/v3/interfaces/OpenApiSchema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,18 @@ export interface OpenApiSchema extends OpenApiReference, WithEnumExtension {
properties?: Dictionary<OpenApiSchema>;
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;
Expand Down
19 changes: 16 additions & 3 deletions src/openApi/v3/parser/getComment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
45 changes: 31 additions & 14 deletions src/openApi/v3/parser/getContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenApiMediaType>): 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<OpenApiMediaType>): 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;
}
Loading

0 comments on commit 433b4f1

Please sign in to comment.