Skip to content

Commit

Permalink
Merge pull request #65 from nicolas-chaulet/fix/unknown-types
Browse files Browse the repository at this point in the history
fix(api): prefer unknown instead of any
  • Loading branch information
mrlubos authored Mar 17, 2024
2 parents 45b695a + 792f1c0 commit f5513a0
Show file tree
Hide file tree
Showing 151 changed files with 1,114 additions and 1,082 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@typescript-eslint/ban-ts-ignore": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-inferrable-types": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-var-requires": 0,
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/unittest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- name: Build library
run: npm run release

- name: Run linter
run: npm run eslint

- name: Run unit tests
run: npm run test

Expand Down
3 changes: 1 addition & 2 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const params = program
.version(json.version)
.requiredOption('-i, --input <value>', 'OpenAPI specification, can be a path, url or string content (required)')
.requiredOption('-o, --output <value>', 'Output directory (required)')
.option('-c, --client <value>', 'HTTP client to generate [fetch, xhr, node, axios, angular]', 'fetch')
.option('-c, --client <value>', 'HTTP client to generate [fetch, xhr, node, axios, angular]')
.option('--name <value>', 'Custom client class name')
.option('--useOptions [value]', 'Use options instead of arguments', false)
.option('--no-autoformat', 'Disable processing generated files with formatter')
Expand Down Expand Up @@ -44,7 +44,6 @@ if (OpenAPI) {
exportModels: parseBooleanOrString(params.exportModels),
exportSchemas: JSON.parse(params.exportSchemas) === true,
exportServices: parseBooleanOrString(params.exportServices),
httpClient: params.client,
useDateType: JSON.parse(params.useDateType) === true,
useOptions: JSON.parse(params.useOptions) === true,
})
Expand Down
98 changes: 86 additions & 12 deletions bin/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { sync } = require('cross-spawn');

describe('bin', () => {
it('it should support minimal params', async () => {
it('supports required parameters', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
Expand All @@ -10,11 +10,86 @@ describe('bin', () => {
'./test/generated/bin',
'--no-write',
]);
expect(result.stdout.toString()).toBe('');
expect(result.stdout.toString()).not.toContain('Prettier');
expect(result.stdout.toString()).toContain('Done!');
expect(result.stderr.toString()).toBe('');
});

it('it should support all params', async () => {
it('generates angular client', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--client',
'angular',
'--no-write',
]);
expect(result.stdout.toString()).toContain('Angular');
expect(result.stderr.toString()).toBe('');
});

it('generates axios client', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--client',
'axios',
'--no-write',
]);
expect(result.stdout.toString()).toContain('Axios');
expect(result.stderr.toString()).toBe('');
});

it('generates fetch client', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--client',
'fetch',
'--no-write',
]);
expect(result.stdout.toString()).toContain('Fetch');
expect(result.stderr.toString()).toBe('');
});
it('generates node client', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--client',
'node',
'--no-write',
]);
expect(result.stdout.toString()).toContain('Node.js');
expect(result.stderr.toString()).toBe('');
});

it('generates xhr client', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
'./test/spec/v3.json',
'--output',
'./test/generated/bin',
'--client',
'xhr',
'--no-write',
]);
expect(result.stdout.toString()).toContain('XHR');
expect(result.stderr.toString()).toBe('');
});

it('supports all parameters', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
Expand All @@ -38,11 +113,11 @@ describe('bin', () => {
'Dto',
'--no-write',
]);
expect(result.stdout.toString()).toBe('');
expect(result.stdout.toString()).toContain('Done!');
expect(result.stderr.toString()).toBe('');
});

it('it should support regexp params', async () => {
it('supports regexp parameters', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
Expand All @@ -55,30 +130,29 @@ describe('bin', () => {
'^(Simple|Types)',
'--no-write',
]);
expect(result.stdout.toString()).toBe('');
expect(result.stdout.toString()).toContain('Done!');
expect(result.stderr.toString()).toBe('');
});

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

it('it should throw error without params', async () => {
it('throws error without parameters', async () => {
const result = sync('node', ['./bin/index.js', '--no-write']);
expect(result.stdout.toString()).toBe('');
expect(result.stderr.toString()).toContain(`error: required option '-i, --input <value>' not specified`);
});

it('it should throw error with wrong params', async () => {
it('throws error with wrong parameters', async () => {
const result = sync('node', [
'./bin/index.js',
'--input',
Expand All @@ -92,7 +166,7 @@ describe('bin', () => {
expect(result.stderr.toString()).toContain(`error: unknown option '--unknown'`);
});

it('it should display help', async () => {
it('displays help', async () => {
const result = sync('node', ['./bin/index.js', '--help', '--no-write']);
expect(result.stdout.toString()).toContain(`Usage: openapi-ts [options]`);
expect(result.stdout.toString()).toContain(`-i, --input <value>`);
Expand Down
7 changes: 0 additions & 7 deletions src/HttpClient.ts

This file was deleted.

13 changes: 7 additions & 6 deletions src/client/interfaces/Options.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HttpClient } from '../../HttpClient';
import type { OpenApi as OpenApiV2 } from '../../openApi/v2/interfaces/OpenApi';
import type { OpenApi as OpenApiV3 } from '../../openApi/v3/interfaces/OpenApi';

export type ServiceResponse = 'body' | 'generics' | 'response';

Expand All @@ -11,6 +12,10 @@ export interface Options {
* Manually set base in OpenAPI config instead of inferring from server value
*/
base?: string;
/**
* The selected HTTP client (fetch, xhr, node or axios)
*/
client?: 'angular' | 'axios' | 'fetch' | 'node' | 'xhr';
/**
* Custom client class name
*/
Expand All @@ -35,14 +40,10 @@ export interface Options {
* Generate services
*/
exportServices?: boolean | string;
/**
* The selected httpClient (fetch, xhr, node or axios)
*/
httpClient?: HttpClient;
/**
* The relative location of the OpenAPI spec
*/
input: string | Record<string, any>;
input: string | OpenApiV2 | OpenApiV3;
/**
* Use operation ID to generate operation names?
*/
Expand Down
107 changes: 102 additions & 5 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,132 @@
import OpenAPI from './index';
import { generate, parseOpenApiSpecification } from './index';
import * as parseV2 from './openApi/v2';
import * as parseV3 from './openApi/v3';

describe('index', () => {
it('parses v2 without issues', async () => {
await OpenAPI.generate({
await generate({
input: './test/spec/v2.json',
output: './generated/v2/',
write: false,
});
});

it('parses v3 without issues', async () => {
await OpenAPI.generate({
await generate({
input: './test/spec/v3.json',
output: './generated/v3/',
write: false,
});
});

it('downloads and parses v2 without issues', async () => {
await OpenAPI.generate({
await generate({
input: 'https://raw.githubusercontent.com/ferdikoomen/openapi-typescript-codegen/master/test/spec/v2.json',
output: './generated/v2-downloaded/',
write: false,
});
});

it('downloads and parses v3 without issues', async () => {
await OpenAPI.generate({
await generate({
input: 'https://raw.githubusercontent.com/ferdikoomen/openapi-typescript-codegen/master/test/spec/v3.json',
output: './generated/v3-downloaded/',
write: false,
});
});
});

describe('parseOpenApiSpecification', () => {
afterEach(() => {
jest.restoreAllMocks();
});

const options: Parameters<typeof parseOpenApiSpecification>[1] = {
autoformat: true,
client: 'fetch',
enums: true,
exportCore: true,
exportModels: true,
exportSchemas: true,
exportServices: true,
input: '',
operationId: true,
output: '',
postfixModels: '',
postfixServices: '',
serviceResponse: 'body',
useDateType: false,
useOptions: true,
write: false,
};

it('uses v2 parser', () => {
const spy = jest.spyOn(parseV2, 'parse');

const spec: Parameters<typeof parseOpenApiSpecification>[0] = {
info: {
title: 'dummy',
version: '1.0',
},
paths: {},
swagger: '2',
};
parseOpenApiSpecification(spec, options);
expect(spy).toHaveBeenCalledWith(spec, options);

const spec2: Parameters<typeof parseOpenApiSpecification>[0] = {
info: {
title: 'dummy',
version: '1.0',
},
paths: {},
swagger: '2.0',
};
parseOpenApiSpecification(spec2, options);
expect(spy).toHaveBeenCalledWith(spec2, options);
});

it('uses v3 parser', () => {
const spy = jest.spyOn(parseV3, 'parse');

const spec: Parameters<typeof parseOpenApiSpecification>[0] = {
info: {
title: 'dummy',
version: '1.0',
},
openapi: '3',
paths: {},
};
parseOpenApiSpecification(spec, options);
expect(spy).toHaveBeenCalledWith(spec, options);

const spec2: Parameters<typeof parseOpenApiSpecification>[0] = {
info: {
title: 'dummy',
version: '1.0',
},
openapi: '3.0',
paths: {},
};
parseOpenApiSpecification(spec2, options);
expect(spy).toHaveBeenCalledWith(spec2, options);

const spec3: Parameters<typeof parseOpenApiSpecification>[0] = {
info: {
title: 'dummy',
version: '1.0',
},
openapi: '3.1.0',
paths: {},
};
parseOpenApiSpecification(spec3, options);
expect(spy).toHaveBeenCalledWith(spec3, options);
});

it('throws on unknown version', () => {
// @ts-ignore
expect(() => parseOpenApiSpecification({ foo: 'bar' }, options)).toThrow(
`Unsupported Open API specification: ${JSON.stringify({ foo: 'bar' }, null, 2)}`
);
});
});
Loading

0 comments on commit f5513a0

Please sign in to comment.