Skip to content

Commit

Permalink
Merge branch 'master' into allowIgnoringOperationId
Browse files Browse the repository at this point in the history
  • Loading branch information
mrlubos authored Feb 20, 2024
2 parents af4dfb7 + 8b7e5f6 commit e4c3846
Show file tree
Hide file tree
Showing 53 changed files with 1,423 additions and 293 deletions.
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/[email protected]

- name: Setup Node environment
uses: actions/[email protected]
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
36 changes: 6 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
# OpenAPI Typescript Codegen

[![NPM][npm-image]][npm-url]
[![License][license-image]][license-url]
[![Coverage][coverage-image]][coverage-url]
[![Coverage][coverage-image]][coverage-url]
[![Downloads][downloads-image]][downloads-url]
[![Build][build-image]][build-url]

> Node.js library that generates Typescript clients based on the OpenAPI specification.
This Repo is a fork of the original codebase that was created to support OpenAPI spec v3.1. The original motivation was to be able to support schema generated by [FastAPI](https://fastapi.tiangolo.com/) versions 0.100 and above.


## Why?
- Frontend ❤️ OpenAPI, but we do not want to use JAVA codegen in our builds
- Quick, lightweight, robust and framework-agnostic 🚀
- Supports generation of TypeScript clients
- Supports generations of Fetch, Node-Fetch, Axios, Angular and XHR http clients
- Supports OpenAPI specification v2.0 and v3.0
- Partial support of OpenAPI specification v3.1
- Supports JSON and YAML files for input
- Supports generation through CLI, Node.js and NPX
- Supports tsc and @babel/plugin-transform-typescript
Expand Down Expand Up @@ -43,8 +40,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 All @@ -62,24 +59,3 @@ Documentation

The main documentation can be found in the [openapi-typescript-codegen/wiki](https://github.com/ferdikoomen/openapi-typescript-codegen/wiki)

Sponsors
===

If you or your company use the OpenAPI Typescript Codegen, please consider supporting me. By sponsoring I can free up time to give this project some love! Details can be found here: https://github.com/sponsors/ferdikoomen

If you're from an enterprise looking for a fully managed SDK generation, please consider our sponsor:

<a href="https://speakeasyapi.dev/?utm_source=ferdi+repo&utm_medium=github+sponsorship">
<img alt="speakeasy" src="https://storage.googleapis.com/speakeasy-design-assets/ferdi-sponsorship.png" width="640"/>
</a>

[npm-url]: https://npmjs.org/package/openapi-typescript-codegen
[npm-image]: https://img.shields.io/npm/v/openapi-typescript-codegen.svg
[license-url]: LICENSE
[license-image]: http://img.shields.io/npm/l/openapi-typescript-codegen.svg
[coverage-url]: https://codecov.io/gh/ferdikoomen/openapi-typescript-codegen
[coverage-image]: https://img.shields.io/codecov/c/github/ferdikoomen/openapi-typescript-codegen.svg
[downloads-url]: http://npm-stat.com/charts.html?package=openapi-typescript-codegen
[downloads-image]: http://img.shields.io/npm/dm/openapi-typescript-codegen.svg
[build-url]: https://circleci.com/gh/ferdikoomen/openapi-typescript-codegen/tree/master
[build-image]: https://circleci.com/gh/ferdikoomen/openapi-typescript-codegen/tree/master.svg?style=svg
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 @@ -30,6 +31,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 @@ -38,9 +47,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,
useOperationId: JSON.parse(params.useOperationId) === true,
indent: params.indent,
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.8",
"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;
useOperationId?: boolean;
indent?: Indent;
Expand Down Expand Up @@ -59,6 +60,7 @@ export const generate = async ({
clientName,
useOptions = false,
useUnionTypes = false,
autoformat = false,
exportCore = true,
exportServices = true,
exportModels = true,
Expand All @@ -78,42 +80,32 @@ export const generate = async ({
useOptions,
});

let parser: typeof parseV2 | typeof parseV3;

switch (openApiVersion) {
case OpenApiVersion.V2: {
const client = parseV2(openApi, useOperationId);
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, useOperationId);
const clientFinal = postProcessClient(client);
if (!write) break;
parser = parseV3;
break;
}
}

if (parser) {
const client = parser(openApi, useOperationId);
const clientFinal = postProcessClient(client);
if (write) {
await writeClient(
clientFinal,
templates,
output,
httpClient,
useOptions,
useUnionTypes,
autoformat,
exportCore,
exportServices,
exportModels,
Expand All @@ -124,7 +116,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
9 changes: 3 additions & 6 deletions src/openApi/v2/parser/getOperationName.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import camelCase from 'camelcase';

import sanitizeOperationName from '../../../utils/sanitizeOperationName';

/**
* Convert the input value to a correct operation (method) classname.
* This will use the operation ID - if available - and otherwise fallback
Expand All @@ -12,12 +14,7 @@ export const getOperationName = (
operationId?: string
): string => {
if (useOperationId && operationId) {
return camelCase(
operationId
.replace(/^[^a-zA-Z]+/g, '')
.replace(/[^\w\-]+/g, '-')
.trim()
);
return camelCase(sanitizeOperationName(operationId).trim());
}

const urlWithoutPlaceholders = url
Expand Down
Loading

0 comments on commit e4c3846

Please sign in to comment.