diff --git a/package.json b/package.json index 9fd5addc..f04826b4 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "@types/qs": "6.9.6", "@typescript-eslint/eslint-plugin": "4.17.0", "@typescript-eslint/parser": "4.17.0", + "abort-controller": "3.0.0", "codecov": "3.8.1", "eslint": "7.21.0", "eslint-config-prettier": "8.1.0", diff --git a/src/templates/core/CancelablePromise.hbs b/src/templates/core/CancelablePromise.hbs new file mode 100644 index 00000000..2ce30856 --- /dev/null +++ b/src/templates/core/CancelablePromise.hbs @@ -0,0 +1,10 @@ +export class CancelablePromise extends Promise { + + constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void) { + super(executor); + } + + public cancel() { + // + } +} diff --git a/src/templates/core/fetch/request.hbs b/src/templates/core/fetch/request.hbs index c22330e5..21077340 100644 --- a/src/templates/core/fetch/request.hbs +++ b/src/templates/core/fetch/request.hbs @@ -3,6 +3,7 @@ import { ApiError } from './ApiError'; import type { ApiRequestOptions } from './ApiRequestOptions'; import type { ApiResult } from './ApiResult'; +import type { CancelablePromise } from './CancelablePromise'; import { OpenAPI } from './OpenAPI'; {{>functions/isDefined}} @@ -53,9 +54,10 @@ import { OpenAPI } from './OpenAPI'; * @returns ApiResult * @throws ApiError */ -export async function request(options: ApiRequestOptions): Promise { +export async function request(options: ApiRequestOptions): CancelablePromise { + const controller = new AbortController(); const url = getUrl(options); - const response = await sendRequest(options, url); + const response = await sendRequest(options, url, controller.signal); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/templates/core/fetch/sendRequest.hbs b/src/templates/core/fetch/sendRequest.hbs index 4afd0731..3607fd77 100644 --- a/src/templates/core/fetch/sendRequest.hbs +++ b/src/templates/core/fetch/sendRequest.hbs @@ -1,8 +1,9 @@ -async function sendRequest(options: ApiRequestOptions, url: string): Promise { +async function sendRequest(options: ApiRequestOptions, url: string, signal: AbortSignal): Promise { const request: RequestInit = { method: options.method, headers: await getHeaders(options), body: getRequestBody(options), + signal, }; if (OpenAPI.WITH_CREDENTIALS) { request.credentials = 'include'; diff --git a/src/templates/core/node/request.hbs b/src/templates/core/node/request.hbs index 1bf15050..3af6cc2b 100644 --- a/src/templates/core/node/request.hbs +++ b/src/templates/core/node/request.hbs @@ -1,5 +1,6 @@ {{>header}} +import { AbortController, AbortSignal } from 'abort-controller'; import FormData from 'form-data'; import fetch, { BodyInit, Headers, RequestInit, Response } from 'node-fetch'; import { types } from 'util'; @@ -7,6 +8,7 @@ import { types } from 'util'; import { ApiError } from './ApiError'; import type { ApiRequestOptions } from './ApiRequestOptions'; import type { ApiResult } from './ApiResult'; +import type { CancelablePromise } from './CancelablePromise'; import { OpenAPI } from './OpenAPI'; {{>functions/isDefined}} @@ -57,9 +59,10 @@ import { OpenAPI } from './OpenAPI'; * @returns ApiResult * @throws ApiError */ -export async function request(options: ApiRequestOptions): Promise { +export async function request(options: ApiRequestOptions): CancelablePromise { + const controller = new AbortController(); const url = getUrl(options); - const response = await sendRequest(options, url); + const response = await sendRequest(options, url, controller.signal); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/templates/core/node/sendRequest.hbs b/src/templates/core/node/sendRequest.hbs index e0a296e0..7a31a291 100644 --- a/src/templates/core/node/sendRequest.hbs +++ b/src/templates/core/node/sendRequest.hbs @@ -1,8 +1,9 @@ -async function sendRequest(options: ApiRequestOptions, url: string): Promise { +async function sendRequest(options: ApiRequestOptions, url: string, signal: AbortSignal): Promise { const request: RequestInit = { method: options.method, headers: await getHeaders(options), body: getRequestBody(options), + signal, }; return await fetch(url, request); } diff --git a/src/templates/core/xhr/request.hbs b/src/templates/core/xhr/request.hbs index 9faf192f..4aa9cbf4 100644 --- a/src/templates/core/xhr/request.hbs +++ b/src/templates/core/xhr/request.hbs @@ -3,6 +3,7 @@ import { ApiError } from './ApiError'; import type { ApiRequestOptions } from './ApiRequestOptions'; import type { ApiResult } from './ApiResult'; +import type { CancelablePromise } from './CancelablePromise'; import { OpenAPI } from './OpenAPI'; {{>functions/isDefined}} @@ -56,7 +57,7 @@ import { OpenAPI } from './OpenAPI'; * @returns ApiResult * @throws ApiError */ -export async function request(options: ApiRequestOptions): Promise { +export async function request(options: ApiRequestOptions): CancelablePromise { const url = getUrl(options); const response = await sendRequest(options, url); const responseBody = getResponseBody(response); diff --git a/src/templates/exportService.hbs b/src/templates/exportService.hbs index 315640d9..21325997 100644 --- a/src/templates/exportService.hbs +++ b/src/templates/exportService.hbs @@ -5,6 +5,7 @@ import type { {{{this}}} } from '../models/{{{this}}}'; {{/each}} {{/if}} +import type { CancelablePromise } from '../core/CancelablePromise'; import { request as __request } from '../core/request'; {{#if @root.useVersion}} import { OpenAPI } from '../core/OpenAPI'; @@ -35,7 +36,7 @@ export class {{{name}}} { {{/each}} * @throws ApiError */ - public static async {{{name}}}({{>parameters}}): Promise<{{>result}}> { + public static async {{{name}}}({{>parameters}}): CancelablePromise<{{>result}}> { const result = await __request({ method: '{{{method}}}', path: `{{{path}}}`, diff --git a/src/templates/index.hbs b/src/templates/index.hbs index e10436ba..1e19118a 100644 --- a/src/templates/index.hbs +++ b/src/templates/index.hbs @@ -2,6 +2,7 @@ {{#if @root.exportCore}} export { ApiError } from './core/ApiError'; +export type { CancelablePromise } from './core/CancelablePromise'; export { OpenAPI } from './core/OpenAPI'; {{/if}} {{#if @root.exportModels}} diff --git a/src/utils/registerHandlebarTemplates.ts b/src/utils/registerHandlebarTemplates.ts index 347b2b98..54e113a1 100644 --- a/src/utils/registerHandlebarTemplates.ts +++ b/src/utils/registerHandlebarTemplates.ts @@ -28,6 +28,7 @@ import nodeGetResponseHeader from '../templates/core/node/getResponseHeader.hbs' import nodeRequest from '../templates/core/node/request.hbs'; import nodeSendRequest from '../templates/core/node/sendRequest.hbs'; import templateCoreSettings from '../templates/core/OpenAPI.hbs'; +import templateCancelablePromise from '../templates/core/CancelablePromise.hbs'; import templateCoreRequest from '../templates/core/request.hbs'; import xhrGetHeaders from '../templates/core/xhr/getHeaders.hbs'; import xhrGetRequestBody from '../templates/core/xhr/getRequestBody.hbs'; @@ -80,6 +81,7 @@ export interface Templates { apiError: Handlebars.TemplateDelegate; apiRequestOptions: Handlebars.TemplateDelegate; apiResult: Handlebars.TemplateDelegate; + cancelablePromise: Handlebars.TemplateDelegate; request: Handlebars.TemplateDelegate; }; } @@ -104,6 +106,7 @@ export function registerHandlebarTemplates(root: { httpClient: HttpClient; useOp apiError: Handlebars.template(templateCoreApiError), apiRequestOptions: Handlebars.template(templateCoreApiRequestOptions), apiResult: Handlebars.template(templateCoreApiResult), + cancelablePromise: Handlebars.template(templateCancelablePromise), request: Handlebars.template(templateCoreRequest), }, }; diff --git a/src/utils/writeClientCore.ts b/src/utils/writeClientCore.ts index c7cac72f..b150687c 100644 --- a/src/utils/writeClientCore.ts +++ b/src/utils/writeClientCore.ts @@ -24,6 +24,7 @@ export async function writeClientCore(client: Client, templates: Templates, outp await writeFile(resolve(outputPath, 'ApiError.ts'), templates.core.apiError({})); await writeFile(resolve(outputPath, 'ApiRequestOptions.ts'), templates.core.apiRequestOptions({})); await writeFile(resolve(outputPath, 'ApiResult.ts'), templates.core.apiResult({})); + await writeFile(resolve(outputPath, 'CancelablePromise.ts'), templates.core.cancelablePromise({})); await writeFile(resolve(outputPath, 'request.ts'), templates.core.request(context)); if (request) { diff --git a/test/index.js b/test/index.js index 3f2fd866..12e13663 100644 --- a/test/index.js +++ b/test/index.js @@ -6,14 +6,14 @@ async function generateV2() { await OpenAPI.generate({ input: './test/spec/v2.json', output: './test/generated/v2/', - httpClient: OpenAPI.HttpClient.FETCH, + httpClient: OpenAPI.HttpClient.NODE, useOptions: false, useUnionTypes: false, exportCore: true, exportSchemas: true, exportModels: true, exportServices: true, - request: './test/custom/request.ts', + // request: './test/custom/request.ts', }); } @@ -21,14 +21,14 @@ async function generateV3() { await OpenAPI.generate({ input: './test/spec/v3.json', output: './test/generated/v3/', - httpClient: OpenAPI.HttpClient.FETCH, + httpClient: OpenAPI.HttpClient.NODE, useOptions: false, useUnionTypes: false, exportCore: true, exportSchemas: true, exportModels: true, exportServices: true, - request: './test/custom/request.ts', + // request: './test/custom/request.ts', }); } diff --git a/yarn.lock b/yarn.lock index cb52a81f..cb5d1d6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1488,6 +1488,13 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abort-controller@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2610,6 +2617,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + exec-sh@^0.3.2: version "0.3.4" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5"