[typescript] add abort signal to requestcontext (#21323)

* add abort signal to requestcontext

* regenerate

* rebuild and regenerate

* abort signal import

* refresh samples

* clean csharp samples

* csharp sample cleanup

* add missing cs samples from master

* abort testing
This commit is contained in:
David Gamero
2025-06-17 04:45:43 -04:00
committed by GitHub
parent dbc5d09da6
commit d2b8a1eeac
34 changed files with 366 additions and 104 deletions

View File

@@ -5,6 +5,7 @@ import {{^supportsES6}}* as{{/supportsES6}} FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
{{/node}}
{{/platforms}}
import { Observable, from } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub{{importFileExtension}}'{{/useRxJS}};
@@ -86,6 +87,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
{{#platforms}}
{{#node}}
private agent: http.Agent | https.Agent | undefined = undefined;
@@ -167,6 +169,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
{{#platforms}}
{{#node}}

View File

@@ -19,6 +19,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
{{#platforms}}
{{#node}}
agent: request.getAgent(),

View File

@@ -3,6 +3,7 @@ import * as FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
import { Observable, from } from '../rxjsStub';
export * from './isomorphic-fetch';
@@ -57,6 +58,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
private agent: http.Agent | https.Agent | undefined = undefined;
/**
@@ -135,6 +137,15 @@ export class RequestContext {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
public setAgent(agent: http.Agent | https.Agent) {
this.agent = agent;
}

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
agent: request.getAgent(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -3,6 +3,7 @@ import * as FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
import { Observable, from } from '../rxjsStub';
export * from './isomorphic-fetch';
@@ -57,6 +58,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
private agent: http.Agent | https.Agent | undefined = undefined;
/**
@@ -135,6 +137,15 @@ export class RequestContext {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
public setAgent(agent: http.Agent | https.Agent) {
this.agent = agent;
}

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
agent: request.getAgent(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -3,6 +3,7 @@ import * as FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
import { Observable, from } from '../rxjsStub';
export * from './isomorphic-fetch';
@@ -57,6 +58,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
private agent: http.Agent | https.Agent | undefined = undefined;
/**
@@ -135,6 +137,15 @@ export class RequestContext {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
public setAgent(agent: http.Agent | https.Agent) {
this.agent = agent;
}

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
agent: request.getAgent(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -48,6 +48,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -124,6 +125,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -11,6 +11,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};
resp.headers.forEach((value: string, name: string) => {

View File

@@ -48,6 +48,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -124,6 +125,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -11,6 +11,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};
resp.headers.forEach((value: string, name: string) => {

View File

@@ -3,6 +3,7 @@ import * as FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
import { Observable, from } from '../rxjsStub';
export * from './isomorphic-fetch';
@@ -57,6 +58,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
private agent: http.Agent | https.Agent | undefined = undefined;
/**
@@ -135,6 +137,15 @@ export class RequestContext {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
public setAgent(agent: http.Agent | https.Agent) {
this.agent = agent;
}

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
agent: request.getAgent(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -3,6 +3,7 @@ import FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
import { Observable, from } from '../rxjsStub';
export * from './isomorphic-fetch';
@@ -57,6 +58,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
private agent: http.Agent | https.Agent | undefined = undefined;
/**
@@ -135,6 +137,15 @@ export class RequestContext {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
public setAgent(agent: http.Agent | https.Agent) {
this.agent = agent;
}

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
agent: request.getAgent(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -49,6 +49,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
/**
* Creates the request context using a http method and request resource url
@@ -125,6 +126,15 @@ export class RequestContext {
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
}
export interface ResponseBody {

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
credentials: "same-origin"
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -3,6 +3,7 @@ import * as FormData from "form-data";
import { URL, URLSearchParams } from 'url';
import * as http from 'http';
import * as https from 'https';
import { AbortSignal } from "node-fetch/externals";
import { Observable, from } from '../rxjsStub';
export * from './isomorphic-fetch';
@@ -57,6 +58,7 @@ export class RequestContext {
private headers: Headers = {};
private body: RequestBody = undefined;
private url: URL;
private signal: AbortSignal | undefined = undefined;
private agent: http.Agent | https.Agent | undefined = undefined;
/**
@@ -135,6 +137,15 @@ export class RequestContext {
this.headers[key] = value;
}
public setSignal(signal: AbortSignal): void {
this.signal = signal;
}
public getSignal(): AbortSignal | undefined {
return this.signal;
}
public setAgent(agent: http.Agent | https.Agent) {
this.agent = agent;
}

View File

@@ -12,6 +12,7 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
method: method,
body: body as any,
headers: request.getHeaders(),
signal: request.getSignal(),
agent: request.getAgent(),
}).then((resp: any) => {
const headers: { [name: string]: string } = {};

View File

@@ -14,36 +14,66 @@ tag.id = Math.floor(Math.random() * 100000)
let pet: petstore.Pet;
function overridePetIDMiddleware(id: number): Middleware {
return {
pre: (c: RequestContext) => {
return new Promise((resolve) => {
pre: async (c: RequestContext) => {
const segments = c.getUrl().split('/')
segments[segments.length - 1] = id.toString()
const newURL = segments.join('/')
c.setUrl(newURL)
resolve(c)
})
return c
},
post: (c: ResponseContext) => {
return new Promise<ResponseContext>((resolve) => {
resolve(c)
})
post: async (c: ResponseContext) => {
return c
},
}
}
function NoopMiddleware(onPre: () => void, onPost: () => void): Middleware {
function noopMiddleware(onPre: () => void, onPost: () => void): Middleware {
return {
pre: (c: RequestContext) => {
return new Promise((resolve) => {
pre: async (c: RequestContext) => {
onPre()
resolve(c)
})
return c
},
post: (c: ResponseContext) => {
return new Promise<ResponseContext>((resolve) => {
post: async (c: ResponseContext) => {
onPost()
resolve(c)
})
return c
},
}
}
/**
* Middleware that adds an abort signal to the request context.
* This can be used to abort requests using an AbortController.
* @param signal AbortSignal to use for the request
* @returns Middleware that sets the signal in the request context
*/
function abortSignalMiddleware(signal: AbortSignal): Middleware {
return {
pre: async (c: RequestContext) => {
c.setSignal(signal)
return c
},
post: async (c: ResponseContext) => {
return c
},
}
}
/**
* Middleware that delays the request/response by a specified amount of time.
* @param delay in milliseconds
* @returns Middleware that delays the request/response
*/
function delayMiddleware(delay: number): Middleware {
return {
pre: async (c: RequestContext) => {
return new Promise<RequestContext>((resolve) => {
setTimeout(() => resolve(c), delay);
});
},
post: async (c: ResponseContext) => {
return new Promise<ResponseContext>((resolve) => {
setTimeout(() => resolve(c), delay);
});
},
}
}
@@ -52,8 +82,8 @@ function MiddlewareCallTracker() {
let CallOrder = [] as string[]
return {
CallOrder,
BaseMiddleware: NoopMiddleware(() => CallOrder.push('base-pre'), () => CallOrder.push('base-post')),
CalltimeMiddleware: NoopMiddleware(() => CallOrder.push('call-pre'), () => CallOrder.push('call-post'))
BaseMiddleware: noopMiddleware(() => CallOrder.push('base-pre'), () => CallOrder.push('base-post')),
CalltimeMiddleware: noopMiddleware(() => CallOrder.push('call-pre'), () => CallOrder.push('call-post'))
}
}
@@ -74,6 +104,8 @@ describe("PetApi", () => {
expect(createdPet).to.deep.equal(pet);
})
describe("Middeware", () => {
it("addPetViaMiddleware", async () => {
const wrongId = pet.id + 1
const createdPet = await petApi.getPetById(wrongId, { middleware: [overridePetIDMiddleware(pet.id)] })
@@ -157,6 +189,43 @@ describe("PetApi", () => {
const callTimeAppendedRightPet = await petApi.getPetById(wrongId, { middleware: [overridePetIDMiddleware(wrongId)], middlewareMergeStrategy: 'prepend' })
expect(callTimeAppendedRightPet).to.deep.equal(pet);
})
})
describe("AbortController", () => {
it("fails on invalid requests", async () => {
const controller = new AbortController();
const abortMiddleware = abortSignalMiddleware(controller.signal);
const wrongId = pet.id + 1
try {
await petApi.getPetById(wrongId, { middleware: [abortMiddleware] })
} catch (err) {
expect(err.code).to.equal(404);
}
})
it("succeeds on valid requests", async () => {
const controller = new AbortController();
const abortMiddleware = abortSignalMiddleware(controller.signal);
const createdPet = await petApi.getPetById(pet.id, { middleware: [abortMiddleware] })
expect(createdPet).to.deep.equal(pet);
})
it("aborts request", async () => {
const signal = AbortSignal.timeout(10); // Set a timeout to ensure the request is aborted
const abortMiddleware = abortSignalMiddleware(signal);
try {
await petApi.getPetById(pet.id, { middleware: [abortMiddleware, delayMiddleware(20)] })
} catch (err) {
expect(err.name).to.equal("AbortError");
return;
}
throw new Error("Request was not aborted!");
})
it("ignores abort after response", async () => {
const signal = AbortSignal.timeout(20); // Set a timeout to ensure the request is aborted
const abortMiddleware = abortSignalMiddleware(signal);
const createdPet = await petApi.getPetById(pet.id, { middleware: [abortMiddleware, delayMiddleware(10)] })
expect(createdPet).to.deep.equal(pet);
})
})
it("should override http api from option", async () => {
const configuration = petstore.createConfiguration();