Skip to content
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

fix(api): prefer unknown instead of any #65

Merged
merged 1 commit into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading