diff --git a/package.json b/package.json index d9e6f259..a00c6e17 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openapi-typescript-codegen", - "version": "0.12.0-alpha", + "version": "0.12.0-beta", "description": "Library that generates Typescript clients based on the OpenAPI specification.", "author": "Ferdi Koomen", "homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen", diff --git a/test/__snapshots__/index.spec.js.snap b/test/__snapshots__/index.spec.js.snap index 0bc3c58b..72c2313c 100644 --- a/test/__snapshots__/index.spec.js.snap +++ b/test/__snapshots__/index.spec.js.snap @@ -571,6 +571,7 @@ export { CollectionFormatService } from './services/CollectionFormatService'; export { ComplexService } from './services/ComplexService'; export { DefaultsService } from './services/DefaultsService'; export { DuplicateService } from './services/DuplicateService'; +export { ErrorService } from './services/ErrorService'; export { HeaderService } from './services/HeaderService'; export { MultipleTags1Service } from './services/MultipleTags1Service'; export { MultipleTags2Service } from './services/MultipleTags2Service'; @@ -2179,6 +2180,42 @@ export class DuplicateService { }" `; +exports[`v2 should generate: ./test/generated/v2/services/ErrorService.ts 1`] = ` +"/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CancelablePromise } from '../core/CancelablePromise'; +import { request as __request } from '../core/request'; +import { OpenAPI } from '../core/OpenAPI'; + +export class ErrorService { + + /** + * @param status Status code to return + * @returns any Custom message: Successful response + * @throws ApiError + */ + public static testErrorCode( + status: string, + ): CancelablePromise { + return __request({ + method: 'POST', + path: \`/api/v\${OpenAPI.VERSION}/error\`, + query: { + 'status': status, + }, + errors: { + 500: \`Custom message: Internal Server Error\`, + 501: \`Custom message: Not Implemented\`, + 502: \`Custom message: Bad Gateway\`, + 503: \`Custom message: Service Unavailable\`, + }, + }); + } + +}" +`; + exports[`v2 should generate: ./test/generated/v2/services/HeaderService.ts 1`] = ` "/* istanbul ignore file */ /* tslint:disable */ @@ -3197,6 +3234,7 @@ export { CollectionFormatService } from './services/CollectionFormatService'; export { ComplexService } from './services/ComplexService'; export { DefaultsService } from './services/DefaultsService'; export { DuplicateService } from './services/DuplicateService'; +export { ErrorService } from './services/ErrorService'; export { FormDataService } from './services/FormDataService'; export { HeaderService } from './services/HeaderService'; export { MultipartService } from './services/MultipartService'; @@ -5227,6 +5265,42 @@ export class DuplicateService { }" `; +exports[`v3 should generate: ./test/generated/v3/services/ErrorService.ts 1`] = ` +"/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CancelablePromise } from '../core/CancelablePromise'; +import { request as __request } from '../core/request'; +import { OpenAPI } from '../core/OpenAPI'; + +export class ErrorService { + + /** + * @param status Status code to return + * @returns any Custom message: Successful response + * @throws ApiError + */ + public static testErrorCode( + status: number, + ): CancelablePromise { + return __request({ + method: 'POST', + path: \`/api/v\${OpenAPI.VERSION}/error\`, + query: { + 'status': status, + }, + errors: { + 500: \`Custom message: Internal Server Error\`, + 501: \`Custom message: Not Implemented\`, + 502: \`Custom message: Bad Gateway\`, + 503: \`Custom message: Service Unavailable\`, + }, + }); + } + +}" +`; + exports[`v3 should generate: ./test/generated/v3/services/FormDataService.ts 1`] = ` "/* istanbul ignore file */ /* tslint:disable */ diff --git a/test/e2e/scripts/browser.js b/test/e2e/scripts/browser.js index 369fd6ac..c5998318 100644 --- a/test/e2e/scripts/browser.js +++ b/test/e2e/scripts/browser.js @@ -13,7 +13,7 @@ async function start() { args: ['--no-sandbox', '--disable-setuid-sandbox'], }); page = await browser.newPage(); - page.on('console', msg => console.log(msg.text())); + // page.on('console', msg => console.log(msg.text())); await page.goto(`http://localhost:3000/`, { waitUntil: 'networkidle0', }); diff --git a/test/e2e/scripts/server.js b/test/e2e/scripts/server.js index 9d74f2c9..48803461 100644 --- a/test/e2e/scripts/server.js +++ b/test/e2e/scripts/server.js @@ -27,10 +27,18 @@ async function start(dir) { res.send(''); }); + // Register an 'echo' server for testing error codes. This will just grab the + // status code from the query and return the default response (and text) from Express. + // See the spec files for more information. + app.all('/base/api/v1.0/error', (req, res) => { + const status = parseInt(req.query.status); + res.sendStatus(status); + }); + // Register an 'echo' server that just returns all data from the API calls. // Although this might not be a 'correct' response, we can use this to test // the majority of API calls. - app.all('/base/api/*', (req, res) => { + app.all('/base/api/v1.0/*', (req, res) => { setTimeout(() => { res.json({ method: req.method, @@ -45,6 +53,7 @@ async function start(dir) { }, 100); }); + server = app.listen(3000, resolve); }); } diff --git a/test/e2e/v2.axios.spec.js b/test/e2e/v2.axios.spec.js index 7aae313c..ce6ca66e 100644 --- a/test/e2e/v2.axios.spec.js +++ b/test/e2e/v2.axios.spec.js @@ -37,6 +37,7 @@ describe('v2.node', () => { }); it('can abort the request', async () => { + let error; try { const { SimpleService } = require('./generated/v2/axios/index.js'); const promise = SimpleService.getCallWithoutParametersAndResponse(); @@ -45,7 +46,8 @@ describe('v2.node', () => { }, 10); await promise; } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); }); }); diff --git a/test/e2e/v2.fetch.spec.js b/test/e2e/v2.fetch.spec.js index 12691f55..75e560a9 100644 --- a/test/e2e/v2.fetch.spec.js +++ b/test/e2e/v2.fetch.spec.js @@ -45,6 +45,7 @@ describe('v2.fetch', () => { }); it('can abort the request', async () => { + let error; try { await browser.evaluate(async () => { const { SimpleService } = window.api; @@ -55,7 +56,65 @@ describe('v2.fetch', () => { await promise; }); } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); + }); + + it('should throw known error (500)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(500); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Custom message: Internal Server Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=500', + status: 500, + statusText: 'Internal Server Error', + body: 'Internal Server Error', + }) + ); + }); + + it('should throw unknown error (409)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(409); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Generic Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=409', + status: 409, + statusText: 'Conflict', + body: 'Conflict', + }) + ); }); }); diff --git a/test/e2e/v2.node.spec.js b/test/e2e/v2.node.spec.js index 1609d656..c8b3920d 100644 --- a/test/e2e/v2.node.spec.js +++ b/test/e2e/v2.node.spec.js @@ -37,6 +37,7 @@ describe('v2.node', () => { }); it('can abort the request', async () => { + let error; try { const { SimpleService } = require('./generated/v2/node/index.js'); const promise = SimpleService.getCallWithoutParametersAndResponse(); @@ -45,7 +46,62 @@ describe('v2.node', () => { }, 10); await promise; } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); + }); + + it('should throw known error (500)', async () => { + let error; + try { + const { ErrorService } = require('./generated/v2/node/index.js'); + await ErrorService.testErrorCode(500); + } catch (e) { + error = JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Custom message: Internal Server Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=500', + status: 500, + statusText: 'Internal Server Error', + body: 'Internal Server Error', + }) + ); + }); + + it('should throw unknown error (409)', async () => { + let error; + try { + const { ErrorService } = require('./generated/v2/node/index.js'); + await ErrorService.testErrorCode(409); + } catch (e) { + error = JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Generic Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=409', + status: 409, + statusText: 'Conflict', + body: 'Conflict', + }) + ); }); }); diff --git a/test/e2e/v2.xhr.spec.js b/test/e2e/v2.xhr.spec.js index 9c1bf5d7..b7f2c09e 100644 --- a/test/e2e/v2.xhr.spec.js +++ b/test/e2e/v2.xhr.spec.js @@ -5,6 +5,7 @@ const copy = require('./scripts/copy'); const compileWithTypescript = require('./scripts/compileWithTypescript'); const server = require('./scripts/server'); const browser = require('./scripts/browser'); +const {ErrorService} = require("./generated/v3/node/index.js"); describe('v2.xhr', () => { beforeAll(async () => { @@ -45,6 +46,7 @@ describe('v2.xhr', () => { }); it('can abort the request', async () => { + let error; try { await browser.evaluate(async () => { const { SimpleService } = window.api; @@ -55,7 +57,65 @@ describe('v2.xhr', () => { await promise; }); } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); + }); + + it('should throw known error (500)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(500); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Custom message: Internal Server Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=500', + status: 500, + statusText: 'Internal Server Error', + body: 'Internal Server Error', + }) + ); + }); + + it('should throw unknown error (409)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(409); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Generic Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=409', + status: 409, + statusText: 'Conflict', + body: 'Conflict', + }) + ); }); }); diff --git a/test/e2e/v3.axios.spec.js b/test/e2e/v3.axios.spec.js index 94069405..3814891f 100644 --- a/test/e2e/v3.axios.spec.js +++ b/test/e2e/v3.axios.spec.js @@ -49,13 +49,21 @@ describe('v3.node', () => { it('formData', async () => { const { ParametersService } = require('./generated/v3/axios/index.js'); - const result = await ParametersService.callWithParameters('valueHeader', 'valueQuery', 'valueForm', 'valueCookie', 'valuePath', { - prop: 'valueBody', - }); + const result = await ParametersService.callWithParameters( + 'valueHeader', + 'valueQuery', + 'valueForm', + 'valueCookie', + 'valuePath', + { + prop: 'valueBody', + } + ); expect(result).toBeDefined(); }); it('can abort the request', async () => { + let error; try { const { SimpleService } = require('./generated/v3/axios/index.js'); const promise = SimpleService.getCallWithoutParametersAndResponse(); @@ -64,7 +72,8 @@ describe('v3.node', () => { }, 10); await promise; } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); }); }); diff --git a/test/e2e/v3.fetch.spec.js b/test/e2e/v3.fetch.spec.js index 7c8ca1d3..d0fc1342 100644 --- a/test/e2e/v3.fetch.spec.js +++ b/test/e2e/v3.fetch.spec.js @@ -60,14 +60,22 @@ describe('v3.fetch', () => { it('formData', async () => { const result = await browser.evaluate(async () => { const { ParametersService } = window.api; - return await ParametersService.callWithParameters('valueHeader', 'valueQuery', 'valueForm', 'valueCookie', 'valuePath', { - prop: 'valueBody', - }); + return await ParametersService.callWithParameters( + 'valueHeader', + 'valueQuery', + 'valueForm', + 'valueCookie', + 'valuePath', + { + prop: 'valueBody', + } + ); }); expect(result).toBeDefined(); }); it('can abort the request', async () => { + let error; try { await browser.evaluate(async () => { const { SimpleService } = window.api; @@ -78,7 +86,65 @@ describe('v3.fetch', () => { await promise; }); } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); + }); + + it('should throw known error (500)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(500); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Custom message: Internal Server Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=500', + status: 500, + statusText: 'Internal Server Error', + body: 'Internal Server Error', + }) + ); + }); + + it('should throw unknown error (409)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(409); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Generic Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=409', + status: 409, + statusText: 'Conflict', + body: 'Conflict', + }) + ); }); }); diff --git a/test/e2e/v3.node.spec.js b/test/e2e/v3.node.spec.js index 26494552..2e506582 100644 --- a/test/e2e/v3.node.spec.js +++ b/test/e2e/v3.node.spec.js @@ -49,13 +49,21 @@ describe('v3.node', () => { it('formData', async () => { const { ParametersService } = require('./generated/v3/node/index.js'); - const result = await ParametersService.callWithParameters('valueHeader', 'valueQuery', 'valueForm', 'valueCookie', 'valuePath', { - prop: 'valueBody', - }); + const result = await ParametersService.callWithParameters( + 'valueHeader', + 'valueQuery', + 'valueForm', + 'valueCookie', + 'valuePath', + { + prop: 'valueBody', + } + ); expect(result).toBeDefined(); }); it('can abort the request', async () => { + let error; try { const { SimpleService } = require('./generated/v3/node/index.js'); const promise = SimpleService.getCallWithoutParametersAndResponse(); @@ -64,7 +72,62 @@ describe('v3.node', () => { }, 10); await promise; } catch (e) { - expect(e.message).toContain('The user aborted a request.'); + error = e.message; } + expect(error).toContain('The user aborted a request.'); + }); + + it('should throw known error (500)', async () => { + let error; + try { + const { ErrorService } = require('./generated/v3/node/index.js'); + await ErrorService.testErrorCode(500); + } catch (e) { + error = JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Custom message: Internal Server Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=500', + status: 500, + statusText: 'Internal Server Error', + body: 'Internal Server Error', + }) + ); + }); + + it('should throw unknown error (409)', async () => { + let error; + try { + const { ErrorService } = require('./generated/v3/node/index.js'); + await ErrorService.testErrorCode(409); + } catch (e) { + error = JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Generic Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=409', + status: 409, + statusText: 'Conflict', + body: 'Conflict', + }) + ); }); }); diff --git a/test/e2e/v3.xhr.spec.js b/test/e2e/v3.xhr.spec.js index 72c56410..16621498 100644 --- a/test/e2e/v3.xhr.spec.js +++ b/test/e2e/v3.xhr.spec.js @@ -71,4 +71,60 @@ describe('v3.xhr', () => { expect(e.message).toContain('The user aborted a request.'); } }); + + it('should throw known error (500)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(500); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Custom message: Internal Server Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=500', + status: 500, + statusText: 'Internal Server Error', + body: 'Internal Server Error', + }) + ); + }); + + it('should throw unknown error (409)', async () => { + const error = await browser.evaluate(async () => { + try { + const { ErrorService } = window.api; + await ErrorService.testErrorCode(409); + } catch (e) { + return JSON.stringify({ + name: e.name, + message: e.message, + url: e.url, + status: e.status, + statusText: e.statusText, + body: e.body, + }); + } + }); + expect(error).toBe( + JSON.stringify({ + name: 'ApiError', + message: 'Generic Error', + url: 'http://localhost:3000/base/api/v1.0/error?status=409', + status: 409, + statusText: 'Conflict', + body: 'Conflict', + }) + ); + }); }); diff --git a/test/spec/v2.json b/test/spec/v2.json index ca4e26bc..7bb5d3d3 100644 --- a/test/spec/v2.json +++ b/test/spec/v2.json @@ -802,6 +802,40 @@ } } } + }, + "/api/v{api-version}/error": { + "post": { + "tags": [ + "Error" + ], + "operationId": "testErrorCode", + "parameters": [ + { + "description": "Status code to return", + "name": "status", + "in": "query", + "type": "string", + "required": true + } + ], + "responses": { + "200": { + "description": "Custom message: Successful response" + }, + "500": { + "description": "Custom message: Internal Server Error" + }, + "501": { + "description": "Custom message: Not Implemented" + }, + "502": { + "description": "Custom message: Bad Gateway" + }, + "503": { + "description": "Custom message: Service Unavailable" + } + } + } } }, "definitions": { diff --git a/test/spec/v3.json b/test/spec/v3.json index 83c9f819..26ff8ec3 100644 --- a/test/spec/v3.json +++ b/test/spec/v3.json @@ -1333,6 +1333,42 @@ } } } + }, + "/api/v{api-version}/error": { + "post": { + "tags": [ + "Error" + ], + "operationId": "testErrorCode", + "parameters": [ + { + "description": "Status code to return", + "name": "status", + "in": "query", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Custom message: Successful response" + }, + "500": { + "description": "Custom message: Internal Server Error" + }, + "501": { + "description": "Custom message: Not Implemented" + }, + "502": { + "description": "Custom message: Bad Gateway" + }, + "503": { + "description": "Custom message: Service Unavailable" + } + } + } } }, "components": {