Skip to content

Commit 95380ff

Browse files
authored
Merge pull request #15 from josstn/non-ascii-services
Support non-ascii characters in service name, operation name and parameter name.
2 parents 09ff143 + 7588831 commit 95380ff

13 files changed

+172
-29
lines changed

src/openApi/v2/parser/getOperationName.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeOperationName from '../../../utils/sanitizeOperationName';
4+
35
/**
46
* Convert the input value to a correct operation (method) classname.
57
* This will use the operation ID - if available - and otherwise fallback
68
* on a generated name from the URL
79
*/
810
export const getOperationName = (url: string, method: string, operationId?: string): string => {
911
if (operationId) {
10-
return camelCase(
11-
operationId
12-
.replace(/^[^a-zA-Z]+/g, '')
13-
.replace(/[^\w\-]+/g, '-')
14-
.trim()
15-
);
12+
return camelCase(sanitizeOperationName(operationId).trim());
1613
}
1714

1815
const urlWithoutPlaceholders = url
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import camelCase from 'camelcase';
22

33
import { reservedWords } from '../../../utils/reservedWords';
4+
import sanitizeOperationParameterName from '../../../utils/sanitizeOperationParameterName';
45

56
/**
67
* Replaces any invalid characters from a parameter name.
78
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
89
*/
910
export const getOperationParameterName = (value: string): string => {
10-
const clean = value
11-
.replace(/^[^a-zA-Z]+/g, '')
12-
.replace(/[^\w\-]+/g, '-')
13-
.trim();
11+
const clean = sanitizeOperationParameterName(value).trim();
1412
return camelCase(clean).replace(reservedWords, '_$1');
1513
};
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeServiceName from '../../../utils/sanitizeServiceName';
4+
35
/**
46
* Convert the input value to a correct service name. This converts
57
* the input string to PascalCase.
68
*/
79
export const getServiceName = (value: string): string => {
8-
const clean = value
9-
.replace(/^[^a-zA-Z]+/g, '')
10-
.replace(/[^\w\-]+/g, '-')
11-
.trim();
10+
const clean = sanitizeServiceName(value).trim();
1211
return camelCase(clean, { pascalCase: true });
1312
};

src/openApi/v3/parser/getOperationName.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeOperationName from '../../../utils/sanitizeOperationName';
4+
35
/**
46
* Convert the input value to a correct operation (method) classname.
57
* This will use the operation ID - if available - and otherwise fallback
68
* on a generated name from the URL
79
*/
810
export const getOperationName = (url: string, method: string, operationId?: string): string => {
911
if (operationId) {
10-
return camelCase(
11-
operationId
12-
.replace(/^[^a-zA-Z]+/g, '')
13-
.replace(/[^\w\-]+/g, '-')
14-
.trim()
15-
);
12+
return camelCase(sanitizeOperationName(operationId).trim());
1613
}
1714

1815
const urlWithoutPlaceholders = url
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import camelCase from 'camelcase';
22

33
import { reservedWords } from '../../../utils/reservedWords';
4+
import sanitizeOperationParameterName from '../../../utils/sanitizeOperationParameterName';
45

56
/**
67
* Replaces any invalid characters from a parameter name.
78
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
89
*/
910
export const getOperationParameterName = (value: string): string => {
10-
const clean = value
11-
.replace(/^[^a-zA-Z]+/g, '')
12-
.replace('[]', 'Array')
13-
.replace(/[^\w\-]+/g, '-')
14-
.trim();
11+
const clean = sanitizeOperationParameterName(value).trim();
1512
return camelCase(clean).replace(reservedWords, '_$1');
1613
};

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ describe('getServiceName', () => {
99
expect(getServiceName('@fooBar')).toEqual('FooBar');
1010
expect(getServiceName('$fooBar')).toEqual('FooBar');
1111
expect(getServiceName('123fooBar')).toEqual('FooBar');
12+
expect(getServiceName('non-ascii-æøåÆØÅöôêÊ字符串')).toEqual('NonAsciiÆøåÆøÅöôêÊ字符串');
1213
});
1314
});
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeServiceName from '../../../utils/sanitizeServiceName';
4+
35
/**
46
* Convert the input value to a correct service name. This converts
57
* the input string to PascalCase.
68
*/
79
export const getServiceName = (value: string): string => {
8-
const clean = value
9-
.replace(/^[^a-zA-Z]+/g, '')
10-
.replace(/[^\w\-]+/g, '-')
11-
.trim();
10+
const clean = sanitizeServiceName(value).trim();
1211
return camelCase(clean, { pascalCase: true });
1312
};

src/utils/sanitizeOperationName.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import sanitizeServiceName from './sanitizeServiceName';
2+
3+
/**
4+
* sanitizeOperationName does the same as sanitizeServiceName.
5+
*/
6+
const sanitizeOperationName = sanitizeServiceName;
7+
export default sanitizeOperationName;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import sanitizeOperationName from './sanitizeOperationName';
2+
3+
const sanitizeOperationParameterName = (name: string): string => {
4+
const withoutBrackets = name.replace('[]', 'Array');
5+
return sanitizeOperationName(withoutBrackets);
6+
};
7+
export default sanitizeOperationParameterName;

src/utils/sanitizeServiceName.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Sanitizes service names, so they are valid typescript identifiers of a certain form.
3+
*
4+
* 1: Remove any leading characters that are illegal as starting character of a typescript identifier.
5+
* 2: Replace illegal characters in remaining part of type name with underscore (-).
6+
*
7+
* Step 1 should perhaps instead also replace illegal characters with underscore, or prefix with it, like sanitizeEnumName
8+
* does. The way this is now one could perhaps end up removing all characters, if all are illegal start characters. It
9+
* would be sort of a breaking change to do so, though, previously generated code might change then.
10+
*
11+
* Javascript identifier regexp pattern retrieved from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers
12+
*
13+
* The output of this is expected to be converted to PascalCase
14+
*/
15+
const sanitizeServiceName = (name: string) =>
16+
name.replace(/^[^\p{ID_Start}]+/u, '').replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '-');
17+
18+
export default sanitizeServiceName;

test/__snapshots__/index.spec.ts.snap

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,7 @@ export { MultipleTags1Service } from './services/MultipleTags1Service';
684684
export { MultipleTags2Service } from './services/MultipleTags2Service';
685685
export { MultipleTags3Service } from './services/MultipleTags3Service';
686686
export { NoContentService } from './services/NoContentService';
687+
export { NonAsciiÆøåÆøÅöôêÊService } from './services/NonAsciiÆøåÆøÅöôêÊService';
687688
export { ParametersService } from './services/ParametersService';
688689
export { ResponseService } from './services/ResponseService';
689690
export { SimpleService } from './services/SimpleService';
@@ -2852,6 +2853,36 @@ export class NoContentService {
28522853
"
28532854
`;
28542855

2856+
exports[`v2 should generate: test/generated/v2/services/NonAsciiÆøåÆøÅöôêÊService.ts 1`] = `
2857+
"/* generated using openapi-typescript-codegen -- do no edit */
2858+
/* istanbul ignore file */
2859+
/* tslint:disable */
2860+
/* eslint-disable */
2861+
import type { NonAsciiStringæøåÆØÅöôêÊ字符串 } from '../models/NonAsciiStringæøåÆØÅöôêÊ字符串';
2862+
import type { CancelablePromise } from '../core/CancelablePromise';
2863+
import { OpenAPI } from '../core/OpenAPI';
2864+
import { request as __request } from '../core/request';
2865+
export class NonAsciiÆøåÆøÅöôêÊService {
2866+
/**
2867+
* @param nonAsciiParamæøåÆøÅöôêÊ Dummy input param
2868+
* @returns NonAsciiStringæøåÆØÅöôêÊ字符串 Successful response
2869+
* @throws ApiError
2870+
*/
2871+
public static nonAsciiæøåÆøÅöôêÊ字符串(
2872+
nonAsciiParamæøåÆøÅöôêÊ: number,
2873+
): CancelablePromise<NonAsciiStringæøåÆØÅöôêÊ字符串> {
2874+
return __request(OpenAPI, {
2875+
method: 'POST',
2876+
url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串',
2877+
query: {
2878+
'nonAsciiParamæøåÆØÅöôêÊ': nonAsciiParamæøåÆøÅöôêÊ,
2879+
},
2880+
});
2881+
}
2882+
}
2883+
"
2884+
`;
2885+
28552886
exports[`v2 should generate: test/generated/v2/services/ParametersService.ts 1`] = `
28562887
"/* generated using openapi-typescript-codegen -- do no edit */
28572888
/* istanbul ignore file */
@@ -3894,6 +3925,7 @@ export { MultipleTags1Service } from './services/MultipleTags1Service';
38943925
export { MultipleTags2Service } from './services/MultipleTags2Service';
38953926
export { MultipleTags3Service } from './services/MultipleTags3Service';
38963927
export { NoContentService } from './services/NoContentService';
3928+
export { NonAsciiÆøåÆøÅöôêÊService } from './services/NonAsciiÆøåÆøÅöôêÊService';
38973929
export { ParametersService } from './services/ParametersService';
38983930
export { RequestBodyService } from './services/RequestBodyService';
38993931
export { ResponseService } from './services/ResponseService';
@@ -7487,6 +7519,36 @@ export class NoContentService {
74877519
"
74887520
`;
74897521

7522+
exports[`v3 should generate: test/generated/v3/services/NonAsciiÆøåÆøÅöôêÊService.ts 1`] = `
7523+
"/* generated using openapi-typescript-codegen -- do no edit */
7524+
/* istanbul ignore file */
7525+
/* tslint:disable */
7526+
/* eslint-disable */
7527+
import type { NonAsciiStringæøåÆØÅöôêÊ字符串 } from '../models/NonAsciiStringæøåÆØÅöôêÊ字符串';
7528+
import type { CancelablePromise } from '../core/CancelablePromise';
7529+
import { OpenAPI } from '../core/OpenAPI';
7530+
import { request as __request } from '../core/request';
7531+
export class NonAsciiÆøåÆøÅöôêÊService {
7532+
/**
7533+
* @param nonAsciiParamæøåÆøÅöôêÊ Dummy input param
7534+
* @returns NonAsciiStringæøåÆØÅöôêÊ字符串 Successful response
7535+
* @throws ApiError
7536+
*/
7537+
public static nonAsciiæøåÆøÅöôêÊ字符串(
7538+
nonAsciiParamæøåÆøÅöôêÊ: number,
7539+
): CancelablePromise<Array<NonAsciiStringæøåÆØÅöôêÊ字符串>> {
7540+
return __request(OpenAPI, {
7541+
method: 'POST',
7542+
url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串',
7543+
query: {
7544+
'nonAsciiParamæøåÆØÅöôêÊ': nonAsciiParamæøåÆøÅöôêÊ,
7545+
},
7546+
});
7547+
}
7548+
}
7549+
"
7550+
`;
7551+
74907552
exports[`v3 should generate: test/generated/v3/services/ParametersService.ts 1`] = `
74917553
"/* generated using openapi-typescript-codegen -- do no edit */
74927554
/* istanbul ignore file */

test/spec/v2.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,33 @@
929929
}
930930
}
931931
}
932+
},
933+
"/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串": {
934+
"post": {
935+
"tags": [
936+
"Non-Ascii-æøåÆØÅöôêÊ"
937+
],
938+
"operationId": "nonAsciiæøåÆØÅöôêÊ字符串",
939+
"parameters": [
940+
{
941+
"description": "Dummy input param",
942+
"name": "nonAsciiParamæøåÆØÅöôêÊ",
943+
"in": "query",
944+
"required": true,
945+
"schema": {
946+
"type": "integer"
947+
}
948+
}
949+
],
950+
"responses": {
951+
"200": {
952+
"description": "Successful response",
953+
"schema": {
954+
"$ref": "#/definitions/NonAsciiStringæøåÆØÅöôêÊ字符串"
955+
}
956+
}
957+
}
958+
}
932959
}
933960
},
934961
"definitions": {

test/spec/v3.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,40 @@
14881488
}
14891489
}
14901490
}
1491+
},
1492+
"/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串": {
1493+
"post": {
1494+
"tags": [
1495+
"Non-Ascii-æøåÆØÅöôêÊ"
1496+
],
1497+
"operationId": "nonAsciiæøåÆØÅöôêÊ字符串",
1498+
"parameters": [
1499+
{
1500+
"description": "Dummy input param",
1501+
"name": "nonAsciiParamæøåÆØÅöôêÊ",
1502+
"in": "query",
1503+
"required": true,
1504+
"schema": {
1505+
"type": "integer"
1506+
}
1507+
}
1508+
],
1509+
"responses": {
1510+
"200": {
1511+
"description": "Successful response",
1512+
"content": {
1513+
"application/json": {
1514+
"schema": {
1515+
"type": "array",
1516+
"items": {
1517+
"$ref": "#/components/schemas/NonAsciiStringæøåÆØÅöôêÊ字符串"
1518+
}
1519+
}
1520+
}
1521+
}
1522+
}
1523+
}
1524+
}
14911525
}
14921526
},
14931527
"components": {

0 commit comments

Comments
 (0)