Skip to content

Support non-ascii (unicode) enum and type names. #2038

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 47 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
94fceb2
Adding const support
nicolas-chaulet Jan 10, 2024
f06132d
Merge pull request #1 from CanoaPBC/support-const
nicolas-chaulet Jan 10, 2024
e3bc08c
Bump version
nicolas-chaulet Jan 10, 2024
5568964
Fix some more const
nicolas-chaulet Jan 10, 2024
4a01f7d
Fix nested any-of
nicolas-chaulet Jan 10, 2024
1f8316c
Merge branch 'ferdikoomen:master' into master
nicolas-chaulet Jan 11, 2024
927cb35
feat(parser): return optional success response on 204 status code
mrlubos Jan 27, 2024
827581d
Merge branch 'master' into feat/response-204
mrlubos Jan 28, 2024
feeb526
Merge pull request #2 from mrlubos/feat/response-204
mrlubos Jan 28, 2024
f80bcb0
test(snapshot): fix snapshot
mrlubos Jan 28, 2024
e11a373
Merge pull request #3 from nicolas-chaulet/fix/status-204-type
mrlubos Jan 28, 2024
69fbc94
feat(client): support regexp to select services to export
mrlubos Jan 28, 2024
859961e
Merge pull request #4 from nicolas-chaulet/feat/export-services-regexp
mrlubos Jan 29, 2024
f5d8e81
Adding a unittest action
nicolas-chaulet Jan 29, 2024
4354538
Fix yaml file
nicolas-chaulet Jan 29, 2024
df0ab80
Fix yaml file
nicolas-chaulet Jan 29, 2024
664b4fe
Adjust flow trigger
nicolas-chaulet Jan 29, 2024
d206b42
Merge pull request #5 from nicolas-chaulet/feat/unittest-action
nicolas-chaulet Jan 29, 2024
72094f6
Introduce a couple of broken schema parsing
nicolas-chaulet Jan 29, 2024
d12d999
test(ci): match steps from CircleCI
mrlubos Jan 29, 2024
4174eb7
chore(model): add another test case
mrlubos Jan 29, 2024
0b6c6bc
Merge pull request #7 from nicolas-chaulet/ci/unittest
nicolas-chaulet Jan 29, 2024
b0a2db3
Change package name
nicolas-chaulet Jan 29, 2024
8a4fad3
fix(client): support regexp to select models to export
mrlubos Jan 30, 2024
76e6851
Merge pull request #8 from nicolas-chaulet/fix/api-models-regexp
mrlubos Jan 30, 2024
41743e8
Merge branch 'master' of https://github.com/CanoaPBC/openapi-typescri…
mrlubos Jan 30, 2024
f35bb12
fix(any-of): handle more cases
mrlubos Feb 1, 2024
abaa6f7
Merge pull request #6 from nicolas-chaulet/union-bugs
nicolas-chaulet Feb 1, 2024
74adb1a
Merge branch 'ferdikoomen:master' into master
mrlubos Feb 1, 2024
059558e
fix(config): support autoformat option flag
mrlubos Feb 2, 2024
c8e7915
Merge pull request #10 from nicolas-chaulet/fix/autoformat-option
mrlubos Feb 2, 2024
c33a9c3
0.27.3
mrlubos Feb 2, 2024
8f75b6a
0.27.4
mrlubos Feb 2, 2024
2cce3cf
Merge branch 'ferdikoomen:master' into master
mrlubos Feb 2, 2024
20dac04
chore(deps): update package-lock.json
mrlubos Feb 2, 2024
c1d8979
fix(parser): parse array items only if parent definition has type
mrlubos Feb 4, 2024
22eef6c
Merge pull request #11 from nicolas-chaulet/fix/any-of-arrays
mrlubos Feb 4, 2024
fc55ad2
fix(parser): type additional properties with properties
mrlubos Feb 4, 2024
ecb60a0
Merge pull request #12 from nicolas-chaulet/fix/additional-properties
mrlubos Feb 4, 2024
80f0dbf
0.27.5
mrlubos Feb 4, 2024
d9daebd
fix(api): allow overriding request body name with x-body-name key
mrlubos Feb 4, 2024
89f3da6
Merge pull request #13 from nicolas-chaulet/fix/x-body-name
mrlubos Feb 4, 2024
c14c1c5
0.27.6
mrlubos Feb 4, 2024
a9c6e48
Merge branch 'ferdikoomen:master' into master
mrlubos Feb 11, 2024
a205111
Support non-ascii (unicode) enum and type names.
josstn Feb 14, 2024
308a180
Update test snapshot.
josstn Feb 14, 2024
b2c3fc7
Merge branch 'master' into non-ascii-identifiers
mrlubos Feb 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/unittest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: unittest

on: [pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.1

- name: Setup Node environment
uses: actions/setup-node@v4.0.1
with:
node-version: 20

- name: Cache Modules
uses: actions/cache@v4
with:
path: "**/node_modules"
key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}

- name: Install dependencies
run: npm install

- name: Build library
run: npm run release

- name: Run unit tests
run: npm run test

# - name: Run e2e tests
# run: npm run test:e2e

# - name: Submit to Codecov
# run: npm run codecov

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ test/e2e/generated
samples/generated
samples/swagger-codegen-cli-v2.jar
samples/swagger-codegen-cli-v3.jar
.env
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ $ openapi --help
--useOptions Use options instead of arguments
--useUnionTypes Use union types instead of enums
--exportCore <value> Write core files to disk (default: true)
--exportServices <value> Write services to disk (default: true)
--exportModels <value> Write models to disk (default: true)
--exportServices <value> Write services to disk [true, false, regexp] (default: true)
--exportModels <value> Write models to disk [true, false, regexp] (default: true)
--exportSchemas <value> Write schemas to disk (default: false)
--indent <value> Indentation options [4, 2, tab] (default: "4")
--postfixServices Service name postfix (default: "Service")
Expand Down
14 changes: 12 additions & 2 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const params = program
.option('--name <value>', 'Custom client class name')
.option('--useOptions', 'Use options instead of arguments')
.option('--useUnionTypes', 'Use union types instead of enums')
.option('--autoformat', 'Process generated files with autoformatter', false)
.option('--exportCore <value>', 'Write core files to disk', true)
.option('--exportServices <value>', 'Write services to disk', true)
.option('--exportModels <value>', 'Write models to disk', true)
Expand All @@ -29,6 +30,14 @@ const params = program

const OpenAPI = require(path.resolve(__dirname, '../dist/index.js'));

const parseBooleanOrString = value => {
try {
return JSON.parse(value) === true;
} catch (error) {
return value;
}
};

if (OpenAPI) {
OpenAPI.generate({
input: params.input,
Expand All @@ -37,9 +46,10 @@ if (OpenAPI) {
clientName: params.name,
useOptions: params.useOptions,
useUnionTypes: params.useUnionTypes,
autoformat: JSON.parse(params.autoformat) === true,
exportCore: JSON.parse(params.exportCore) === true,
exportServices: JSON.parse(params.exportServices) === true,
exportModels: JSON.parse(params.exportModels) === true,
exportServices: parseBooleanOrString(params.exportServices),
exportModels: parseBooleanOrString(params.exportModels),
exportSchemas: JSON.parse(params.exportSchemas) === true,
indent: params.indent,
postfixServices: params.postfixServices,
Expand Down
29 changes: 29 additions & 0 deletions bin/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,35 @@ describe('bin', () => {
expect(result.stderr.toString()).toBe('');
});

it('it should support regexp params', async () => {
const result = crossSpawn.sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--exportServices',
'^(Simple|Types)',
'--exportModels',
'^(Simple|Types)',
]);
expect(result.stdout.toString()).toBe('');
expect(result.stderr.toString()).toBe('');
});

it('should autoformat with Prettier', async () => {
const result = crossSpawn.sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--autoformat',
]);
expect(result.stdout.toString()).toBe('');
expect(result.stderr.toString()).toBe('');
});

it('it should throw error without params', async () => {
const result = crossSpawn.sync('node', ['./bin/index.js']);
expect(result.stdout.toString()).toBe('');
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "openapi-typescript-codegen",
"version": "0.27.0",
"name": "@nicolas-chaulet/openapi-typescript-codegen",
"version": "0.27.6",
"description": "Library that generates Typescript clients based on the OpenAPI specification.",
"author": "Ferdi Koomen",
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
"homepage": "https://github.com/CanoaPBC/openapi-typescript-codegen",
"repository": {
"type": "git",
"url": "git+https://github.com/ferdikoomen/openapi-typescript-codegen.git"
"url": "git+https://github.com/CanoaPBC/openapi-typescript-codegen.git"
},
"bugs": {
"url": "https://github.com/ferdikoomen/openapi-typescript-codegen/issues"
Expand Down
12 changes: 11 additions & 1 deletion src/client/interfaces/Model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ import type { Schema } from './Schema';

export interface Model extends Schema {
name: string;
export: 'reference' | 'generic' | 'enum' | 'array' | 'dictionary' | 'interface' | 'one-of' | 'any-of' | 'all-of';
export:
| 'reference'
| 'generic'
| 'enum'
| 'array'
| 'dictionary'
| 'interface'
| 'one-of'
| 'any-of'
| 'all-of'
| 'const';
type: string;
base: string;
template: string | null;
Expand Down
4 changes: 2 additions & 2 deletions src/client/interfaces/ModelComposition.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Model } from './Model';

export interface ModelComposition {
type: 'one-of' | 'any-of' | 'all-of';
imports: string[];
enums: Model[];
export: 'one-of' | 'any-of' | 'all-of';
imports: string[];
properties: Model[];
}
43 changes: 17 additions & 26 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ export type Options = {
clientName?: string;
useOptions?: boolean;
useUnionTypes?: boolean;
autoformat?: boolean;
exportCore?: boolean;
exportServices?: boolean;
exportModels?: boolean;
exportServices?: boolean | string;
exportModels?: boolean | string;
exportSchemas?: boolean;
indent?: Indent;
postfixServices?: string;
Expand Down Expand Up @@ -57,6 +58,7 @@ export const generate = async ({
clientName,
useOptions = false,
useUnionTypes = false,
autoformat = false,
exportCore = true,
exportServices = true,
exportModels = true,
Expand All @@ -75,42 +77,32 @@ export const generate = async ({
useOptions,
});

let parser: typeof parseV2 | typeof parseV3;

switch (openApiVersion) {
case OpenApiVersion.V2: {
const client = parseV2(openApi);
const clientFinal = postProcessClient(client);
if (!write) break;
await writeClient(
clientFinal,
templates,
output,
httpClient,
useOptions,
useUnionTypes,
exportCore,
exportServices,
exportModels,
exportSchemas,
indent,
postfixServices,
postfixModels,
clientName,
request
);
parser = parseV2;
break;
}

case OpenApiVersion.V3: {
const client = parseV3(openApi);
const clientFinal = postProcessClient(client);
if (!write) break;
parser = parseV3;
break;
}
}

if (parser) {
const client = parser(openApi);
const clientFinal = postProcessClient(client);
if (write) {
await writeClient(
clientFinal,
templates,
output,
httpClient,
useOptions,
useUnionTypes,
autoformat,
exportCore,
exportServices,
exportModels,
Expand All @@ -121,7 +113,6 @@ export const generate = async ({
clientName,
request
);
break;
}
}
};
Expand Down
4 changes: 3 additions & 1 deletion src/openApi/v2/parser/escapeName.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import validTypescriptIdentifierRegex from '../../../utils/validTypescriptIdentifierRegex';

export const escapeName = (value: string): string => {
if (value || value === '') {
const validName = /^[a-zA-Z_$][\w$]+$/g.test(value);
const validName = validTypescriptIdentifierRegex.test(value);
if (!validName) {
return `'${value}'`;
}
Expand Down
7 changes: 2 additions & 5 deletions src/openApi/v2/parser/getEnum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Enum } from '../../../client/interfaces/Enum';
import sanitizeEnumName from '../../../utils/sanitizeEnumName';

export const getEnum = (values?: (string | number)[]): Enum[] => {
if (Array.isArray(values)) {
Expand All @@ -19,11 +20,7 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
};
}
return {
name: String(value)
.replace(/\W+/g, '_')
.replace(/^(\d+)/g, '_$1')
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
.toUpperCase(),
name: sanitizeEnumName(String(value)),
value: `'${value.replace(/'/g, "\\'")}'`,
type: 'string',
description: null,
Expand Down
2 changes: 1 addition & 1 deletion src/openApi/v2/parser/getModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const getModel = (

if (definition.allOf?.length) {
const composition = getModelComposition(openApi, definition, definition.allOf, 'all-of', getModel);
model.export = composition.type;
model.export = composition.export;
model.imports.push(...composition.imports);
model.properties.push(...composition.properties);
model.enums.push(...composition.enums);
Expand Down
4 changes: 2 additions & 2 deletions src/openApi/v2/parser/getModelComposition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export const getModelComposition = (
getModel: GetModelFn
): ModelComposition => {
const composition: ModelComposition = {
type,
imports: [],
enums: [],
export: type,
imports: [],
properties: [],
};

Expand Down
4 changes: 2 additions & 2 deletions src/openApi/v2/parser/getOperationResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export const getOperationResponse = (
code: responseCode,
description: response.description || null,
export: 'generic',
type: 'any',
base: 'any',
type: responseCode !== 204 ? 'any' : 'void',
base: responseCode !== 204 ? 'any' : 'void',
template: null,
link: null,
isDefinition: false,
Expand Down
26 changes: 2 additions & 24 deletions src/openApi/v2/parser/getOperationResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,14 @@ const areEqual = (a: Model, b: Model): boolean => {
export const getOperationResults = (operationResponses: OperationResponse[]): OperationResponse[] => {
const operationResults: OperationResponse[] = [];

// Filter out success response codes, but skip "204 No Content"
// Filter out success response codes
operationResponses.forEach(operationResponse => {
const { code } = operationResponse;
if (code && code !== 204 && code >= 200 && code < 300) {
if (code && code >= 200 && code < 300) {
operationResults.push(operationResponse);
}
});

if (!operationResults.length) {
operationResults.push({
in: 'response',
name: '',
code: 200,
description: '',
export: 'generic',
type: 'void',
base: 'void',
template: null,
link: null,
isDefinition: false,
isReadOnly: false,
isRequired: false,
isNullable: false,
imports: [],
enum: [],
enums: [],
properties: [],
});
}

return operationResults.filter((operationResult, index, arr) => {
return (
arr.findIndex(item => {
Expand Down
5 changes: 2 additions & 3 deletions src/openApi/v2/parser/getType.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { Type } from '../../../client/interfaces/Type';
import sanitizeTypeName from '../../../utils/sanitizeTypeName';
import { getMappedType } from './getMappedType';
import { stripNamespace } from './stripNamespace';

const encode = (value: string): string => {
return value.replace(/^[^a-zA-Z_$]+/g, '').replace(/[^\w$]+/g, '_');
};
const encode = (value: string): string => sanitizeTypeName(value);

/**
* Parse any string value into a type object.
Expand Down
5 changes: 3 additions & 2 deletions src/openApi/v3/interfaces/OpenApiRequestBody.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import type { OpenApiReference } from './OpenApiReference';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#requestBodyObject
*/
export interface OpenApiRequestBody extends OpenApiReference {
description?: string;
'x-body-name'?: string;
content: Dictionary<OpenApiMediaType>;
required?: boolean;
description?: string;
nullable?: boolean;
required?: boolean;
}
Loading