mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Fixed just in time cancelation scenario
This commit is contained in:
parent
2553b2cf93
commit
63f59e6c39
@ -11,6 +11,7 @@ export class ApiError extends Error {
|
||||
constructor(response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.name = 'ApiError';
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
|
||||
@ -1,10 +1,29 @@
|
||||
{{>header}}
|
||||
|
||||
export class CancelError extends Error {
|
||||
|
||||
constructor(reason: string = 'Promise was canceled') {
|
||||
super(reason);
|
||||
this.name = 'CancelError';
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnCancel {
|
||||
readonly isPending: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
|
||||
(cancelHandler: () => void): void;
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
|
||||
#isPending: boolean;
|
||||
#isCanceled: boolean;
|
||||
#isCancelled: boolean;
|
||||
readonly #cancelHandlers: (() => void)[];
|
||||
readonly #promise: Promise<T>;
|
||||
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||
@ -14,18 +33,18 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: any) => void,
|
||||
onCancel: (cancelHandler: () => void) => void
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this.#isPending = true;
|
||||
this.#isCanceled = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (!this.#isCanceled) {
|
||||
if (!this.#isCancelled) {
|
||||
this.#isPending = false;
|
||||
this.#resolve?.(value);
|
||||
}
|
||||
@ -42,7 +61,15 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
};
|
||||
|
||||
return executor(onResolve, onReject, onCancel);
|
||||
Object.defineProperty(onCancel, 'isPending', {
|
||||
get: (): boolean => this.#isPending,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this.#isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
|
||||
@ -64,10 +91,10 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (!this.#isPending || this.#isCanceled) {
|
||||
if (!this.#isPending || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isCanceled = true;
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
@ -80,7 +107,7 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public get isCanceled(): boolean {
|
||||
return this.#isCanceled;
|
||||
public get isCancelled(): boolean {
|
||||
return this.#isCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
6
src/templates/core/axios/getRequestBody.hbs
Normal file
6
src/templates/core/axios/getRequestBody.hbs
Normal file
@ -0,0 +1,6 @@
|
||||
function getRequestBody(options: ApiRequestOptions): any {
|
||||
if (options.body) {
|
||||
return options.body;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -2,5 +2,5 @@ function getResponseBody(response: AxiosResponse<any>): any {
|
||||
if (response.status !== 204) {
|
||||
return response.data;
|
||||
}
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
function getResponseHeader(response: AxiosResponse<any>, responseHeader?: string): string | null {
|
||||
function getResponseHeader(response: AxiosResponse<any>, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = response.headers[responseHeader];
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
{{>functions/isDefined}}
|
||||
@ -39,6 +40,9 @@ import { OpenAPI } from './OpenAPI';
|
||||
{{>axios/getHeaders}}
|
||||
|
||||
|
||||
{{>axios/getRequestBody}}
|
||||
|
||||
|
||||
{{>axios/sendRequest}}
|
||||
|
||||
|
||||
@ -61,21 +65,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url, onCancel);
|
||||
const responseBody = getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options, formData);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: isSuccess(response.status),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
catchErrors(options, result);
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: isSuccess(response.status),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
resolve(result.body);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<AxiosResponse<any>> {
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: any,
|
||||
headers: Record<string, string>,
|
||||
onCancel: OnCancel
|
||||
): Promise<AxiosResponse<any>> {
|
||||
const source = axios.CancelToken.source();
|
||||
const formData = options.formData && getFormData(options.formData);
|
||||
const data = formData || options.body;
|
||||
|
||||
const config: AxiosRequestConfig = {
|
||||
url,
|
||||
data,
|
||||
headers,
|
||||
data: body || formData,
|
||||
method: options.method,
|
||||
headers: await getHeaders(options, formData),
|
||||
cancelToken: source.token,
|
||||
};
|
||||
|
||||
|
||||
@ -37,5 +37,6 @@ async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
|
||||
headers.append('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
if (options.formData) {
|
||||
return getFormData(options.formData);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
@ -12,6 +8,5 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -14,6 +14,5 @@ async function getResponseBody(response: Response): Promise<any> {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | null {
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
{{>functions/isDefined}}
|
||||
@ -61,21 +62,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
catchErrors(options, result);
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
resolve(result.body);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<Response> {
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: BodyInit | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
|
||||
|
||||
const request: RequestInit = {
|
||||
headers,
|
||||
body: body || formData,
|
||||
method: options.method,
|
||||
headers: await getHeaders(options),
|
||||
body: getRequestBody(options),
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
function getFormData(params: Record<string, any>): FormData {
|
||||
const formData = new FormData();
|
||||
function getFormData(options: ApiRequestOptions): FormData | undefined {
|
||||
if (options.formData) {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.keys(params).forEach(key => {
|
||||
const value = params[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
Object.keys(options.formData).forEach(key => {
|
||||
const value = options.formData?.[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return formData;
|
||||
return formData;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
if (options.formData) {
|
||||
return getFormData(options.formData);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
@ -12,6 +8,5 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -14,6 +14,5 @@ async function getResponseBody(response: Response): Promise<any> {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | null {
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
{{>functions/isDefined}}
|
||||
@ -69,21 +70,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
catchErrors(options, result);
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
resolve(result.body);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<Response> {
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: BodyInit | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
|
||||
const request: RequestInit = {
|
||||
headers,
|
||||
method: options.method,
|
||||
headers: await getHeaders(options),
|
||||
body: getRequestBody(options),
|
||||
body: body || formData,
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
function getRequestBody(options: ApiRequestOptions): any {
|
||||
if (options.formData) {
|
||||
return getFormData(options.formData);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
@ -13,5 +9,5 @@ function getRequestBody(options: ApiRequestOptions): any {
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -14,6 +14,5 @@ function getResponseBody(xhr: XMLHttpRequest): any {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
function getResponseHeader(xhr: XMLHttpRequest, responseHeader?: string): string | null {
|
||||
function getResponseHeader(xhr: XMLHttpRequest, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = xhr.getResponseHeader(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
{{>functions/isDefined}}
|
||||
@ -64,21 +65,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url, onCancel);
|
||||
const responseBody = getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: isSuccess(response.status),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
catchErrors(options, result);
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: isSuccess(response.status),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
resolve(result.body);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@ -1,17 +1,23 @@
|
||||
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<XMLHttpRequest> {
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: any,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<XMLHttpRequest> {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(options.method, url, true);
|
||||
xhr.withCredentials = OpenAPI.WITH_CREDENTIALS;
|
||||
|
||||
const headers = await getHeaders(options);
|
||||
headers.forEach((value: string, key: string) => {
|
||||
headers.forEach((value, key) => {
|
||||
xhr.setRequestHeader(key, value);
|
||||
});
|
||||
|
||||
return new Promise<XMLHttpRequest>((resolve, reject) => {
|
||||
xhr.onload = () => resolve(xhr);
|
||||
xhr.onabort = () => reject(new Error('The user aborted a request.'));
|
||||
xhr.send(getRequestBody(options));
|
||||
xhr.send(body || formData);
|
||||
|
||||
onCancel(() => xhr.abort());
|
||||
});
|
||||
|
||||
@ -5,6 +5,7 @@ import templateCoreApiError from '../templates/core/ApiError.hbs';
|
||||
import templateCoreApiRequestOptions from '../templates/core/ApiRequestOptions.hbs';
|
||||
import templateCoreApiResult from '../templates/core/ApiResult.hbs';
|
||||
import axiosGetHeaders from '../templates/core/axios/getHeaders.hbs';
|
||||
import axiosGetRequestBody from '../templates/core/axios/getRequestBody.hbs';
|
||||
import axiosGetResponseBody from '../templates/core/axios/getResponseBody.hbs';
|
||||
import axiosGetResponseHeader from '../templates/core/axios/getResponseHeader.hbs';
|
||||
import axiosRequest from '../templates/core/axios/request.hbs';
|
||||
@ -186,6 +187,7 @@ export function registerHandlebarTemplates(root: { httpClient: HttpClient; useOp
|
||||
|
||||
// Specific files for the axios client implementation
|
||||
Handlebars.registerPartial('axios/getHeaders', Handlebars.template(axiosGetHeaders));
|
||||
Handlebars.registerPartial('axios/getRequestBody', Handlebars.template(axiosGetRequestBody));
|
||||
Handlebars.registerPartial('axios/getResponseBody', Handlebars.template(axiosGetResponseBody));
|
||||
Handlebars.registerPartial('axios/getResponseHeader', Handlebars.template(axiosGetResponseHeader));
|
||||
Handlebars.registerPartial('axios/sendRequest', Handlebars.template(axiosSendRequest));
|
||||
|
||||
@ -15,6 +15,7 @@ export class ApiError extends Error {
|
||||
constructor(response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.name = 'ApiError';
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
@ -58,11 +59,30 @@ exports[`v2 should generate: ./test/generated/v2/core/CancelablePromise.ts 1`] =
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export class CancelError extends Error {
|
||||
|
||||
constructor(reason: string = 'Promise was canceled') {
|
||||
super(reason);
|
||||
this.name = 'CancelError';
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnCancel {
|
||||
readonly isPending: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
|
||||
(cancelHandler: () => void): void;
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
|
||||
#isPending: boolean;
|
||||
#isCanceled: boolean;
|
||||
#isCancelled: boolean;
|
||||
readonly #cancelHandlers: (() => void)[];
|
||||
readonly #promise: Promise<T>;
|
||||
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||
@ -72,18 +92,18 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: any) => void,
|
||||
onCancel: (cancelHandler: () => void) => void
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this.#isPending = true;
|
||||
this.#isCanceled = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (!this.#isCanceled) {
|
||||
if (!this.#isCancelled) {
|
||||
this.#isPending = false;
|
||||
this.#resolve?.(value);
|
||||
}
|
||||
@ -100,7 +120,15 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
};
|
||||
|
||||
return executor(onResolve, onReject, onCancel);
|
||||
Object.defineProperty(onCancel, 'isPending', {
|
||||
get: (): boolean => this.#isPending,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this.#isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
|
||||
@ -122,10 +150,10 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (!this.#isPending || this.#isCanceled) {
|
||||
if (!this.#isPending || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isCanceled = true;
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
@ -138,8 +166,8 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public get isCanceled(): boolean {
|
||||
return this.#isCanceled;
|
||||
public get isCancelled(): boolean {
|
||||
return this.#isCancelled;
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -184,6 +212,7 @@ import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
function isDefined<T>(value: T | null | undefined): value is Exclude<T, null | undefined> {
|
||||
@ -243,17 +272,20 @@ function getUrl(options: ApiRequestOptions): string {
|
||||
return url;
|
||||
}
|
||||
|
||||
function getFormData(params: Record<string, any>): FormData {
|
||||
const formData = new FormData();
|
||||
function getFormData(options: ApiRequestOptions): FormData | undefined {
|
||||
if (options.formData) {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.keys(params).forEach(key => {
|
||||
const value = params[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
Object.keys(options.formData).forEach(key => {
|
||||
const value = options.formData?.[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return formData;
|
||||
return formData;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||
@ -304,14 +336,11 @@ async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
|
||||
headers.append('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
if (options.formData) {
|
||||
return getFormData(options.formData);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
@ -321,17 +350,23 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<Response> {
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: BodyInit | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
|
||||
|
||||
const request: RequestInit = {
|
||||
headers,
|
||||
body: body || formData,
|
||||
method: options.method,
|
||||
headers: await getHeaders(options),
|
||||
body: getRequestBody(options),
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
@ -344,15 +379,14 @@ async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (c
|
||||
return await fetch(url, request);
|
||||
}
|
||||
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | null {
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
async function getResponseBody(response: Response): Promise<any> {
|
||||
@ -371,8 +405,7 @@ async function getResponseBody(response: Response): Promise<any> {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
|
||||
@ -407,21 +440,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
catchErrors(options, result);
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
resolve(result.body);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
@ -2488,6 +2527,7 @@ export class ApiError extends Error {
|
||||
constructor(response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.name = 'ApiError';
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
@ -2531,11 +2571,30 @@ exports[`v3 should generate: ./test/generated/v3/core/CancelablePromise.ts 1`] =
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export class CancelError extends Error {
|
||||
|
||||
constructor(reason: string = 'Promise was canceled') {
|
||||
super(reason);
|
||||
this.name = 'CancelError';
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnCancel {
|
||||
readonly isPending: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
|
||||
(cancelHandler: () => void): void;
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
|
||||
#isPending: boolean;
|
||||
#isCanceled: boolean;
|
||||
#isCancelled: boolean;
|
||||
readonly #cancelHandlers: (() => void)[];
|
||||
readonly #promise: Promise<T>;
|
||||
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||
@ -2545,18 +2604,18 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: any) => void,
|
||||
onCancel: (cancelHandler: () => void) => void
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this.#isPending = true;
|
||||
this.#isCanceled = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (!this.#isCanceled) {
|
||||
if (!this.#isCancelled) {
|
||||
this.#isPending = false;
|
||||
this.#resolve?.(value);
|
||||
}
|
||||
@ -2573,7 +2632,15 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
};
|
||||
|
||||
return executor(onResolve, onReject, onCancel);
|
||||
Object.defineProperty(onCancel, 'isPending', {
|
||||
get: (): boolean => this.#isPending,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this.#isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
|
||||
@ -2595,10 +2662,10 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (!this.#isPending || this.#isCanceled) {
|
||||
if (!this.#isPending || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isCanceled = true;
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
@ -2611,8 +2678,8 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public get isCanceled(): boolean {
|
||||
return this.#isCanceled;
|
||||
public get isCancelled(): boolean {
|
||||
return this.#isCancelled;
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -2657,6 +2724,7 @@ import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
function isDefined<T>(value: T | null | undefined): value is Exclude<T, null | undefined> {
|
||||
@ -2716,17 +2784,20 @@ function getUrl(options: ApiRequestOptions): string {
|
||||
return url;
|
||||
}
|
||||
|
||||
function getFormData(params: Record<string, any>): FormData {
|
||||
const formData = new FormData();
|
||||
function getFormData(options: ApiRequestOptions): FormData | undefined {
|
||||
if (options.formData) {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.keys(params).forEach(key => {
|
||||
const value = params[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
Object.keys(options.formData).forEach(key => {
|
||||
const value = options.formData?.[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return formData;
|
||||
return formData;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||
@ -2777,14 +2848,11 @@ async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
|
||||
headers.append('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
if (options.formData) {
|
||||
return getFormData(options.formData);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
@ -2794,17 +2862,23 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<Response> {
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: BodyInit | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
|
||||
|
||||
const request: RequestInit = {
|
||||
headers,
|
||||
body: body || formData,
|
||||
method: options.method,
|
||||
headers: await getHeaders(options),
|
||||
body: getRequestBody(options),
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
@ -2817,15 +2891,14 @@ async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (c
|
||||
return await fetch(url, request);
|
||||
}
|
||||
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | null {
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
async function getResponseBody(response: Response): Promise<any> {
|
||||
@ -2844,8 +2917,7 @@ async function getResponseBody(response: Response): Promise<any> {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
|
||||
@ -2880,21 +2952,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
catchErrors(options, result);
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
resolve(result.body);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ const compileWithBabel = require('./scripts/compileWithBabel');
|
||||
const server = require('./scripts/server');
|
||||
const browser = require('./scripts/browser');
|
||||
|
||||
describe('v2.fetch', () => {
|
||||
describe('v2.babel', () => {
|
||||
beforeAll(async () => {
|
||||
await generate('v2/babel', 'v2', 'fetch', true, true);
|
||||
await copy('v2/babel');
|
||||
|
||||
@ -38,7 +38,7 @@ describe('v2.node', () => {
|
||||
|
||||
it('can abort the request', async () => {
|
||||
try {
|
||||
const { SimpleService } = require('./generated/v3/node/index.js');
|
||||
const { SimpleService } = require('./generated/v2/node/index.js');
|
||||
const promise = SimpleService.getCallWithoutParametersAndResponse();
|
||||
setTimeout(() => {
|
||||
promise.cancel();
|
||||
|
||||
@ -6,7 +6,7 @@ const compileWithBabel = require('./scripts/compileWithBabel');
|
||||
const server = require('./scripts/server');
|
||||
const browser = require('./scripts/browser');
|
||||
|
||||
describe('v3.fetch', () => {
|
||||
describe('v3.babel', () => {
|
||||
beforeAll(async () => {
|
||||
await generate('v3/babel', 'v3', 'fetch', true, true);
|
||||
await copy('v3/babel');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user