Skip to content

Commit b32c85e

Browse files
committed
- Fixed quotes in enums
- Fixed better default operation name - Fixed unittest
1 parent ec2c712 commit b32c85e

File tree

14 files changed

+254
-51
lines changed

14 files changed

+254
-51
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4+
## [0.20.0] - 2022-02-25
5+
### Fixed
6+
- Support enums with single quotes in names
7+
- Generating better names when `operationId` is not given (breaking change)
8+
49
## [0.19.0] - 2022-02-02
510
### Added
611
- Support for Angular client with `--name` option

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021 Ferdi Koomen
3+
Copyright (c) Ferdi Koomen
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

src/openApi/v2/parser/escapeName.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { escapeName } from './escapeName';
22

33
describe('escapeName', () => {
44
it('should escape', () => {
5-
expect(escapeName('')).toEqual('');
5+
expect(escapeName('')).toEqual("''");
66
expect(escapeName('fooBar')).toEqual('fooBar');
77
expect(escapeName('Foo Bar')).toEqual(`'Foo Bar'`);
88
expect(escapeName('foo bar')).toEqual(`'foo bar'`);

src/openApi/v2/parser/getOperation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const getOperation = (
2020
pathParams: OperationParameters
2121
): Operation => {
2222
const serviceName = getServiceName(tag);
23-
const operationName = getOperationName(op.operationId || `${method}`);
23+
const operationName = getOperationName(url, method, op.operationId);
2424

2525
// Create a new operation object for this method.
2626
const operation: Operation = {

src/openApi/v2/parser/getOperationName.spec.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@ import { getOperationName } from './getOperationName';
22

33
describe('getOperationName', () => {
44
it('should produce correct result', () => {
5-
expect(getOperationName('')).toEqual('');
6-
expect(getOperationName('FooBar')).toEqual('fooBar');
7-
expect(getOperationName('Foo Bar')).toEqual('fooBar');
8-
expect(getOperationName('foo bar')).toEqual('fooBar');
9-
expect(getOperationName('foo-bar')).toEqual('fooBar');
10-
expect(getOperationName('foo_bar')).toEqual('fooBar');
11-
expect(getOperationName('foo.bar')).toEqual('fooBar');
12-
expect(getOperationName('@foo.bar')).toEqual('fooBar');
13-
expect(getOperationName('$foo.bar')).toEqual('fooBar');
14-
expect(getOperationName('_foo.bar')).toEqual('fooBar');
15-
expect(getOperationName('-foo.bar')).toEqual('fooBar');
16-
expect(getOperationName('123.foo.bar')).toEqual('fooBar');
5+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
6+
expect(getOperationName('/api/v{api-version}/users', 'GET', undefined)).toEqual('getApiUsers');
7+
expect(getOperationName('/api/v{api-version}/users', 'POST', undefined)).toEqual('postApiUsers');
8+
expect(getOperationName('/api/v1/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
9+
expect(getOperationName('/api/v1/users', 'GET', undefined)).toEqual('getApiV1Users');
10+
expect(getOperationName('/api/v1/users', 'POST', undefined)).toEqual('postApiV1Users');
11+
expect(getOperationName('/api/v1/users/{id}', 'GET', undefined)).toEqual('getApiV1Users');
12+
expect(getOperationName('/api/v1/users/{id}', 'POST', undefined)).toEqual('postApiV1Users');
13+
14+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'fooBar')).toEqual('fooBar');
15+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'FooBar')).toEqual('fooBar');
16+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'Foo Bar')).toEqual('fooBar');
17+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo bar')).toEqual('fooBar');
18+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo-bar')).toEqual('fooBar');
19+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo_bar')).toEqual('fooBar');
20+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo.bar')).toEqual('fooBar');
21+
expect(getOperationName('/api/v{api-version}/users', 'GET', '@foo.bar')).toEqual('fooBar');
22+
expect(getOperationName('/api/v{api-version}/users', 'GET', '$foo.bar')).toEqual('fooBar');
23+
expect(getOperationName('/api/v{api-version}/users', 'GET', '_foo.bar')).toEqual('fooBar');
24+
expect(getOperationName('/api/v{api-version}/users', 'GET', '-foo.bar')).toEqual('fooBar');
25+
expect(getOperationName('/api/v{api-version}/users', 'GET', '123.foo.bar')).toEqual('fooBar');
1726
});
1827
});

src/openApi/v2/parser/getOperationName.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@ import camelCase from 'camelcase';
22

33
/**
44
* Convert the input value to a correct operation (method) classname.
5-
* This converts the input string to camelCase, so the method name follows
6-
* the most popular Javascript and Typescript writing style.
5+
* This will use the operation ID - if available - and otherwise fallback
6+
* on a generated name from the URL
77
*/
8-
export const getOperationName = (value: string): string => {
9-
const clean = value
10-
.replace(/^[^a-zA-Z]+/g, '')
11-
.replace(/[^\w\-]+/g, '-')
12-
.trim();
13-
return camelCase(clean);
8+
export const getOperationName = (url: string, method: string, operationId?: string): string => {
9+
if (operationId) {
10+
return camelCase(
11+
operationId
12+
.replace(/^[^a-zA-Z]+/g, '')
13+
.replace(/[^\w\-]+/g, '-')
14+
.trim()
15+
);
16+
}
17+
18+
const urlWithoutPlaceholders = url
19+
.replace(/[^/]*?{api-version}.*?\//g, '')
20+
.replace(/{(.*?)}/g, '')
21+
.replace(/\//g, '-');
22+
23+
return camelCase(`${method}-${urlWithoutPlaceholders}`);
1424
};

src/openApi/v3/parser/getEnum.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
2323
.replace(/^(\d+)/g, '_$1')
2424
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
2525
.toUpperCase(),
26-
value: `'${value}'`,
26+
value: `'${value.replace(/'/g, "\\'")}'`,
2727
type: 'string',
2828
description: null,
2929
};

src/openApi/v3/parser/getOperation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const getOperation = (
2323
pathParams: OperationParameters
2424
): Operation => {
2525
const serviceName = getServiceName(tag);
26-
const operationName = getOperationName(op.operationId || `${method}`);
26+
const operationName = getOperationName(url, method, op.operationId);
2727

2828
// Create a new operation object for this method.
2929
const operation: Operation = {

src/openApi/v3/parser/getOperationName.spec.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@ import { getOperationName } from './getOperationName';
22

33
describe('getOperationName', () => {
44
it('should produce correct result', () => {
5-
expect(getOperationName('')).toEqual('');
6-
expect(getOperationName('FooBar')).toEqual('fooBar');
7-
expect(getOperationName('Foo Bar')).toEqual('fooBar');
8-
expect(getOperationName('foo bar')).toEqual('fooBar');
9-
expect(getOperationName('foo-bar')).toEqual('fooBar');
10-
expect(getOperationName('foo_bar')).toEqual('fooBar');
11-
expect(getOperationName('foo.bar')).toEqual('fooBar');
12-
expect(getOperationName('@foo.bar')).toEqual('fooBar');
13-
expect(getOperationName('$foo.bar')).toEqual('fooBar');
14-
expect(getOperationName('_foo.bar')).toEqual('fooBar');
15-
expect(getOperationName('-foo.bar')).toEqual('fooBar');
16-
expect(getOperationName('123.foo.bar')).toEqual('fooBar');
5+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
6+
expect(getOperationName('/api/v{api-version}/users', 'GET', undefined)).toEqual('getApiUsers');
7+
expect(getOperationName('/api/v{api-version}/users', 'POST', undefined)).toEqual('postApiUsers');
8+
expect(getOperationName('/api/v1/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
9+
expect(getOperationName('/api/v1/users', 'GET', undefined)).toEqual('getApiV1Users');
10+
expect(getOperationName('/api/v1/users', 'POST', undefined)).toEqual('postApiV1Users');
11+
expect(getOperationName('/api/v1/users/{id}', 'GET', undefined)).toEqual('getApiV1Users');
12+
expect(getOperationName('/api/v1/users/{id}', 'POST', undefined)).toEqual('postApiV1Users');
13+
14+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'fooBar')).toEqual('fooBar');
15+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'FooBar')).toEqual('fooBar');
16+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'Foo Bar')).toEqual('fooBar');
17+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo bar')).toEqual('fooBar');
18+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo-bar')).toEqual('fooBar');
19+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo_bar')).toEqual('fooBar');
20+
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo.bar')).toEqual('fooBar');
21+
expect(getOperationName('/api/v{api-version}/users', 'GET', '@foo.bar')).toEqual('fooBar');
22+
expect(getOperationName('/api/v{api-version}/users', 'GET', '$foo.bar')).toEqual('fooBar');
23+
expect(getOperationName('/api/v{api-version}/users', 'GET', '_foo.bar')).toEqual('fooBar');
24+
expect(getOperationName('/api/v{api-version}/users', 'GET', '-foo.bar')).toEqual('fooBar');
25+
expect(getOperationName('/api/v{api-version}/users', 'GET', '123.foo.bar')).toEqual('fooBar');
1726
});
1827
});

src/openApi/v3/parser/getOperationName.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@ import camelCase from 'camelcase';
22

33
/**
44
* Convert the input value to a correct operation (method) classname.
5-
* This converts the input string to camelCase, so the method name follows
6-
* the most popular Javascript and Typescript writing style.
5+
* This will use the operation ID - if available - and otherwise fallback
6+
* on a generated name from the URL
77
*/
8-
export const getOperationName = (value: string): string => {
9-
const clean = value
10-
.replace(/^[^a-zA-Z]+/g, '')
11-
.replace(/[^\w\-]+/g, '-')
12-
.trim();
13-
return camelCase(clean);
8+
export const getOperationName = (url: string, method: string, operationId?: string): string => {
9+
if (operationId) {
10+
return camelCase(
11+
operationId
12+
.replace(/^[^a-zA-Z]+/g, '')
13+
.replace(/[^\w\-]+/g, '-')
14+
.trim()
15+
);
16+
}
17+
18+
const urlWithoutPlaceholders = url
19+
.replace(/[^/]*?{api-version}.*?\//g, '')
20+
.replace(/{(.*?)}/g, '')
21+
.replace(/\//g, '-');
22+
23+
return camelCase(`${method}-${urlWithoutPlaceholders}`);
1424
};

test/__snapshots__/index.spec.ts.snap

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,77 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`v2 should generate: ./test/generated/v2/Demo.ts 1`] = `
4+
"/* istanbul ignore file */
5+
/* tslint:disable */
6+
/* eslint-disable */
7+
import { NgModule} from '@angular/core';
8+
import { HttpClientModule } from '@angular/common/http';
9+
10+
import { AngularHttpRequest } from './core/AngularHttpRequest';
11+
import { BaseHttpRequest } from './core/BaseHttpRequest';
12+
import type { OpenAPIConfig } from './core/OpenAPI';
13+
import { OpenAPI } from './core/OpenAPI';
14+
15+
import { CollectionFormatService } from './services/CollectionFormatService';
16+
import { ComplexService } from './services/ComplexService';
17+
import { DefaultService } from './services/DefaultService';
18+
import { DefaultsService } from './services/DefaultsService';
19+
import { DescriptionsService } from './services/DescriptionsService';
20+
import { DuplicateService } from './services/DuplicateService';
21+
import { ErrorService } from './services/ErrorService';
22+
import { HeaderService } from './services/HeaderService';
23+
import { MultipleTags1Service } from './services/MultipleTags1Service';
24+
import { MultipleTags2Service } from './services/MultipleTags2Service';
25+
import { MultipleTags3Service } from './services/MultipleTags3Service';
26+
import { NoContentService } from './services/NoContentService';
27+
import { ParametersService } from './services/ParametersService';
28+
import { ResponseService } from './services/ResponseService';
29+
import { SimpleService } from './services/SimpleService';
30+
import { TypesService } from './services/TypesService';
31+
32+
@NgModule({
33+
imports: [HttpClientModule],
34+
providers: [
35+
{
36+
provide: OpenAPI,
37+
useValue: {
38+
BASE: OpenAPI?.BASE ?? 'http://localhost:3000/base',
39+
VERSION: OpenAPI?.VERSION ?? '1.0',
40+
WITH_CREDENTIALS: OpenAPI?.WITH_CREDENTIALS ?? false,
41+
CREDENTIALS: OpenAPI?.CREDENTIALS ?? 'include',
42+
TOKEN: OpenAPI?.TOKEN,
43+
USERNAME: OpenAPI?.USERNAME,
44+
PASSWORD: OpenAPI?.PASSWORD,
45+
HEADERS: OpenAPI?.HEADERS,
46+
ENCODE_PATH: OpenAPI?.ENCODE_PATH,
47+
} as OpenAPIConfig,
48+
},
49+
{
50+
provide: BaseHttpRequest,
51+
useClass: AngularHttpRequest,
52+
},
53+
CollectionFormatService,
54+
ComplexService,
55+
DefaultService,
56+
DefaultsService,
57+
DescriptionsService,
58+
DuplicateService,
59+
ErrorService,
60+
HeaderService,
61+
MultipleTags1Service,
62+
MultipleTags2Service,
63+
MultipleTags3Service,
64+
NoContentService,
65+
ParametersService,
66+
ResponseService,
67+
SimpleService,
68+
TypesService,
69+
]
70+
})
71+
export class Demo {}
72+
"
73+
`;
74+
375
exports[`v2 should generate: ./test/generated/v2/core/ApiError.ts 1`] = `
476
"/* istanbul ignore file */
577
/* tslint:disable */
@@ -943,6 +1015,8 @@ export enum EnumWithStrings {
9431015
SUCCESS = 'Success',
9441016
WARNING = 'Warning',
9451017
ERROR = 'Error',
1018+
_SINGLE_QUOTE_ = ''Single Quote'',
1019+
_DOUBLE_QUOTES_ = '\\"Double Quotes\\"',
9461020
}"
9471021
`;
9481022

@@ -2949,6 +3023,86 @@ export class TypesService {
29493023
}"
29503024
`;
29513025

3026+
exports[`v3 should generate: ./test/generated/v3/Demo.ts 1`] = `
3027+
"/* istanbul ignore file */
3028+
/* tslint:disable */
3029+
/* eslint-disable */
3030+
import { NgModule} from '@angular/core';
3031+
import { HttpClientModule } from '@angular/common/http';
3032+
3033+
import { AngularHttpRequest } from './core/AngularHttpRequest';
3034+
import { BaseHttpRequest } from './core/BaseHttpRequest';
3035+
import type { OpenAPIConfig } from './core/OpenAPI';
3036+
import { OpenAPI } from './core/OpenAPI';
3037+
3038+
import { CollectionFormatService } from './services/CollectionFormatService';
3039+
import { ComplexService } from './services/ComplexService';
3040+
import { DefaultService } from './services/DefaultService';
3041+
import { DefaultsService } from './services/DefaultsService';
3042+
import { DescriptionsService } from './services/DescriptionsService';
3043+
import { DuplicateService } from './services/DuplicateService';
3044+
import { ErrorService } from './services/ErrorService';
3045+
import { FormDataService } from './services/FormDataService';
3046+
import { HeaderService } from './services/HeaderService';
3047+
import { MultipartService } from './services/MultipartService';
3048+
import { MultipleTags1Service } from './services/MultipleTags1Service';
3049+
import { MultipleTags2Service } from './services/MultipleTags2Service';
3050+
import { MultipleTags3Service } from './services/MultipleTags3Service';
3051+
import { NoContentService } from './services/NoContentService';
3052+
import { ParametersService } from './services/ParametersService';
3053+
import { RequestBodyService } from './services/RequestBodyService';
3054+
import { ResponseService } from './services/ResponseService';
3055+
import { SimpleService } from './services/SimpleService';
3056+
import { TypesService } from './services/TypesService';
3057+
import { UploadService } from './services/UploadService';
3058+
3059+
@NgModule({
3060+
imports: [HttpClientModule],
3061+
providers: [
3062+
{
3063+
provide: OpenAPI,
3064+
useValue: {
3065+
BASE: OpenAPI?.BASE ?? 'http://localhost:3000/base',
3066+
VERSION: OpenAPI?.VERSION ?? '1.0',
3067+
WITH_CREDENTIALS: OpenAPI?.WITH_CREDENTIALS ?? false,
3068+
CREDENTIALS: OpenAPI?.CREDENTIALS ?? 'include',
3069+
TOKEN: OpenAPI?.TOKEN,
3070+
USERNAME: OpenAPI?.USERNAME,
3071+
PASSWORD: OpenAPI?.PASSWORD,
3072+
HEADERS: OpenAPI?.HEADERS,
3073+
ENCODE_PATH: OpenAPI?.ENCODE_PATH,
3074+
} as OpenAPIConfig,
3075+
},
3076+
{
3077+
provide: BaseHttpRequest,
3078+
useClass: AngularHttpRequest,
3079+
},
3080+
CollectionFormatService,
3081+
ComplexService,
3082+
DefaultService,
3083+
DefaultsService,
3084+
DescriptionsService,
3085+
DuplicateService,
3086+
ErrorService,
3087+
FormDataService,
3088+
HeaderService,
3089+
MultipartService,
3090+
MultipleTags1Service,
3091+
MultipleTags2Service,
3092+
MultipleTags3Service,
3093+
NoContentService,
3094+
ParametersService,
3095+
RequestBodyService,
3096+
ResponseService,
3097+
SimpleService,
3098+
TypesService,
3099+
UploadService,
3100+
]
3101+
})
3102+
export class Demo {}
3103+
"
3104+
`;
3105+
29523106
exports[`v3 should generate: ./test/generated/v3/core/ApiError.ts 1`] = `
29533107
"/* istanbul ignore file */
29543108
/* tslint:disable */
@@ -4089,6 +4243,8 @@ export enum EnumWithStrings {
40894243
SUCCESS = 'Success',
40904244
WARNING = 'Warning',
40914245
ERROR = 'Error',
4246+
_SINGLE_QUOTE_ = '\\\\'Single Quote\\\\'',
4247+
_DOUBLE_QUOTES_ = '\\"Double Quotes\\"',
40924248
}"
40934249
`;
40944250

@@ -6125,7 +6281,7 @@ export class FormDataService {
61256281
* @param formData A reusable request body
61266282
* @throws ApiError
61276283
*/
6128-
public static post(
6284+
public static postApiFormData(
61296285
parameter?: string,
61306286
formData?: ModelWithString,
61316287
): CancelablePromise<void> {
@@ -6502,7 +6658,7 @@ export class RequestBodyService {
65026658
* @param requestBody A reusable request body
65036659
* @throws ApiError
65046660
*/
6505-
public static post(
6661+
public static postApiRequestBody(
65066662
parameter?: string,
65076663
requestBody?: ModelWithString,
65086664
): CancelablePromise<void> {

0 commit comments

Comments
 (0)