@equinor/fusion-framework-module-http 8.1.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +6 -3
- package/CHANGELOG.md +0 -1299
- package/docs/client-configuration.md +0 -175
- package/docs/observable-patterns.md +0 -103
- package/docs/selectors-and-handlers.md +0 -112
- package/docs/server-sent-events.md +0 -125
- package/docs/testing.md +0 -78
- package/src/configurator.ts +0 -249
- package/src/errors/ClientNotFoundException.ts +0 -9
- package/src/errors/HttpJsonResponseError.ts +0 -32
- package/src/errors/HttpResponseError.ts +0 -21
- package/src/errors/ServerSentEventResponseError.ts +0 -30
- package/src/errors/index.ts +0 -4
- package/src/index.ts +0 -15
- package/src/lib/client/client-msal.ts +0 -80
- package/src/lib/client/client.ts +0 -496
- package/src/lib/client/index.ts +0 -4
- package/src/lib/client/types.ts +0 -244
- package/src/lib/index.ts +0 -3
- package/src/lib/operators/HttpMiddlewareHandler.ts +0 -58
- package/src/lib/operators/HttpRequestHandler.ts +0 -29
- package/src/lib/operators/HttpResponseHandler.ts +0 -11
- package/src/lib/operators/ProcessOperators.ts +0 -113
- package/src/lib/operators/capitalize-request-method-operator.ts +0 -26
- package/src/lib/operators/fetch-request.schemas.ts +0 -104
- package/src/lib/operators/index.ts +0 -9
- package/src/lib/operators/request-operator-header.ts +0 -19
- package/src/lib/operators/request-validation-operator.ts +0 -51
- package/src/lib/operators/sse-map.operator.ts +0 -45
- package/src/lib/operators/types.ts +0 -174
- package/src/lib/selectors/blob-selector.ts +0 -42
- package/src/lib/selectors/create-sse-selector.ts +0 -279
- package/src/lib/selectors/index.ts +0 -11
- package/src/lib/selectors/json-selector.ts +0 -52
- package/src/mock/create-open-api-mock-middleware.ts +0 -40
- package/src/mock/create-router-middleware.ts +0 -158
- package/src/mock/index.ts +0 -26
- package/src/mock/resolve-open-api-mock-response.ts +0 -36
- package/src/module.ts +0 -149
- package/src/provider.ts +0 -225
- package/src/version.ts +0 -2
- package/tests/HttpClient.test.ts +0 -173
- package/tests/HttpMiddlewareHandler.test.ts +0 -58
- package/tests/mock/adapters.test.ts +0 -62
- package/tests/mock/router-middleware.test.ts +0 -135
- package/tests/operators.test.ts +0 -137
- package/tests/sse.selector.test.ts +0 -162
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -12
package/src/lib/client/client.ts
DELETED
|
@@ -1,496 +0,0 @@
|
|
|
1
|
-
import { finalize, firstValueFrom, of, Subject } from 'rxjs';
|
|
2
|
-
import { switchMap, take, takeUntil, tap } from 'rxjs/operators';
|
|
3
|
-
import { fromFetch } from 'rxjs/fetch';
|
|
4
|
-
|
|
5
|
-
import { HttpMiddlewareHandler, HttpRequestHandler, HttpResponseHandler } from '../operators';
|
|
6
|
-
import { blobSelector, jsonSelector } from '../selectors';
|
|
7
|
-
|
|
8
|
-
import type { Observable, ObservableInput } from 'rxjs';
|
|
9
|
-
import type {
|
|
10
|
-
IHttpMiddlewareHandler,
|
|
11
|
-
IHttpRequestHandler,
|
|
12
|
-
IHttpResponseHandler,
|
|
13
|
-
} from '../operators';
|
|
14
|
-
import type {
|
|
15
|
-
BlobResult,
|
|
16
|
-
FetchRequest,
|
|
17
|
-
FetchRequestInit,
|
|
18
|
-
FetchResponse,
|
|
19
|
-
IHttpClient,
|
|
20
|
-
JsonRequest,
|
|
21
|
-
StreamResponse,
|
|
22
|
-
} from './types';
|
|
23
|
-
|
|
24
|
-
import { HttpResponseError } from '../../errors/index.js';
|
|
25
|
-
import {
|
|
26
|
-
createSseSelector,
|
|
27
|
-
type ServerSentEvent,
|
|
28
|
-
type SseSelectorOptions,
|
|
29
|
-
} from '../selectors/create-sse-selector';
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Configuration options for creating an `HttpClient` instance.
|
|
33
|
-
*
|
|
34
|
-
* @template TRequest - The type of the request object. Defaults to `FetchRequest`.
|
|
35
|
-
* @template TResponse - The type of the response object. Defaults to `Response`.
|
|
36
|
-
* @property {IHttpRequestHandler<TRequest>} requestHandler - A handler for customizing the request before it is sent.
|
|
37
|
-
* @property {IHttpResponseHandler<TResponse>} responseHandler - A handler for customizing the response after it is received.
|
|
38
|
-
*/
|
|
39
|
-
export type HttpClientCreateOptions<
|
|
40
|
-
TRequest extends FetchRequest = FetchRequest,
|
|
41
|
-
TResponse = Response,
|
|
42
|
-
> = {
|
|
43
|
-
requestHandler: IHttpRequestHandler<TRequest>;
|
|
44
|
-
responseHandler: IHttpResponseHandler<TResponse>;
|
|
45
|
-
middlewareHandler: IHttpMiddlewareHandler;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/** Base http client for executing requests */
|
|
49
|
-
export class HttpClient<
|
|
50
|
-
TRequest extends FetchRequest = FetchRequest,
|
|
51
|
-
TResponse extends FetchResponse = FetchResponse,
|
|
52
|
-
> implements IHttpClient<TRequest, TResponse>
|
|
53
|
-
{
|
|
54
|
-
/**
|
|
55
|
-
* A request handler that can be used to customize the request before it is sent.
|
|
56
|
-
* This property is part of the `HttpClientCreateOptions` configuration object used to create an `HttpClient` instance.
|
|
57
|
-
*/
|
|
58
|
-
public readonly requestHandler: IHttpRequestHandler<TRequest>;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* A handler for customizing the response after it is received.
|
|
62
|
-
* This property is part of the `HttpClientCreateOptions` configuration object used to create an `HttpClient` instance.
|
|
63
|
-
*/
|
|
64
|
-
public readonly responseHandler: IHttpResponseHandler<TResponse>;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Middleware wrapping the network call, for cross-cutting concerns such as retries,
|
|
68
|
-
* caching, or telemetry. This property is part of the `HttpClientCreateOptions`
|
|
69
|
-
* configuration object used to create an `HttpClient` instance.
|
|
70
|
-
*/
|
|
71
|
-
public readonly middlewareHandler: IHttpMiddlewareHandler;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* A stream of requests that are about to be executed.
|
|
75
|
-
* This property is used internally by the `HttpClient` class to manage the lifecycle of requests.
|
|
76
|
-
*/
|
|
77
|
-
protected _request$ = new Subject<TRequest>();
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* A stream of responses that have been received.
|
|
81
|
-
* This property is used internally by the `HttpClient` class to manage the lifecycle of responses.
|
|
82
|
-
*/
|
|
83
|
-
protected _response$ = new Subject<TResponse>();
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* A stream that is used to signal the abortion of requests.
|
|
87
|
-
* This property is used internally by the `HttpClient` class to manage the lifecycle of requests.
|
|
88
|
-
*/
|
|
89
|
-
protected _abort$ = new Subject<void>();
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* A stream of requests that are about to be executed.
|
|
93
|
-
* @returns An `Observable` that emits each request before it is executed.
|
|
94
|
-
*/
|
|
95
|
-
public get request$(): Observable<TRequest> {
|
|
96
|
-
return this._request$.asObservable();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* A stream of responses that have been received.
|
|
101
|
-
* @returns An `Observable` that emits each response as it is received.
|
|
102
|
-
*/
|
|
103
|
-
public get response$(): Observable<TResponse> {
|
|
104
|
-
return this._response$.asObservable();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Creates a new `HttpClient`.
|
|
109
|
-
* @param uri - The base URI used to resolve relative request paths.
|
|
110
|
-
* @param options - Optional request and response handlers.
|
|
111
|
-
*/
|
|
112
|
-
constructor(
|
|
113
|
-
public uri: string,
|
|
114
|
-
options?: Partial<HttpClientCreateOptions<TRequest, TResponse>>,
|
|
115
|
-
) {
|
|
116
|
-
this.requestHandler = new HttpRequestHandler<TRequest>(options?.requestHandler);
|
|
117
|
-
this.responseHandler = new HttpResponseHandler<TResponse>(options?.responseHandler);
|
|
118
|
-
this.middlewareHandler = new HttpMiddlewareHandler(options?.middlewareHandler);
|
|
119
|
-
this._init();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Internal method called by child classes to perform initialization logic in the constructor.
|
|
124
|
-
* This method is intended to be overridden by child classes to add their own initialization logic.
|
|
125
|
-
* @protected
|
|
126
|
-
* @virtual
|
|
127
|
-
*/
|
|
128
|
-
protected _init(): void {
|
|
129
|
-
// called by children for constructor setup
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Fetches data from the specified path and returns a stream response.
|
|
134
|
-
*
|
|
135
|
-
* @template T - The expected shape of the fetched data.
|
|
136
|
-
* @param path - The path to fetch data from.
|
|
137
|
-
* @param args - Optional request initialization options, including a custom selector function.
|
|
138
|
-
* @returns A stream response containing the fetched data.
|
|
139
|
-
*/
|
|
140
|
-
public fetch$<T = TResponse>(
|
|
141
|
-
path: string,
|
|
142
|
-
args?: FetchRequestInit<T, TRequest, TResponse>,
|
|
143
|
-
): StreamResponse<T> {
|
|
144
|
-
return this._fetch$(path, args);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Fetches data from the specified path and returns a Promise containing the fetched data.
|
|
149
|
-
*
|
|
150
|
-
* @template T - The expected shape of the fetched data.
|
|
151
|
-
* @param path - The path to fetch data from.
|
|
152
|
-
* @param args - Optional request initialization options, including a custom selector function.
|
|
153
|
-
* @returns A Promise containing the fetched data.
|
|
154
|
-
*/
|
|
155
|
-
public fetch<T = TResponse>(
|
|
156
|
-
path: string,
|
|
157
|
-
args?: FetchRequestInit<T, TRequest, TResponse>,
|
|
158
|
-
): Promise<T> {
|
|
159
|
-
return firstValueFrom(this.fetch$<T>(path, args));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* @deprecated Use {@link fetch} instead.
|
|
164
|
-
* @template T - The expected shape of the fetched data.
|
|
165
|
-
* @param path - The path to fetch data from.
|
|
166
|
-
* @param args - Optional request initialization options, including a custom selector function.
|
|
167
|
-
* @returns A Promise containing the fetched data.
|
|
168
|
-
*/
|
|
169
|
-
public fetchAsync<T = TResponse>(
|
|
170
|
-
path: string,
|
|
171
|
-
args?: FetchRequestInit<T, TRequest, TResponse>,
|
|
172
|
-
): Promise<T> {
|
|
173
|
-
return this.fetch(path, args);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Fetches data from the specified path and returns a stream response containing the data in JSON format.
|
|
178
|
-
*
|
|
179
|
-
* @template T - The expected shape of the parsed JSON data.
|
|
180
|
-
* @param path - The path to fetch the data from.
|
|
181
|
-
* @param args - Optional request initialization options, including a custom selector function and request body.
|
|
182
|
-
* - `body`: The request body, which will be automatically serialized to JSON if it's an object.
|
|
183
|
-
* - `selector`: A custom selector function to transform the response data. If not provided, the `jsonSelector` function will be used.
|
|
184
|
-
* - `headers`: Additional headers to include in the request. The `Accept` and `Content-Type` headers will be automatically set to `application/json`.
|
|
185
|
-
* @returns A stream response containing the fetched data in JSON format.
|
|
186
|
-
*/
|
|
187
|
-
public json$<T = unknown>(
|
|
188
|
-
path: string,
|
|
189
|
-
args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
|
|
190
|
-
): StreamResponse<T> {
|
|
191
|
-
const body = typeof args?.body === 'object' ? JSON.stringify(args?.body) : args?.body;
|
|
192
|
-
const selector = args?.selector ?? jsonSelector;
|
|
193
|
-
const headers = new Headers(args?.headers);
|
|
194
|
-
headers.append('Accept', 'application/json');
|
|
195
|
-
headers.append('Content-Type', 'application/json');
|
|
196
|
-
return this.fetch$(path, {
|
|
197
|
-
...args,
|
|
198
|
-
body,
|
|
199
|
-
selector,
|
|
200
|
-
headers,
|
|
201
|
-
} as FetchRequestInit<T, TRequest, TResponse>);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Fetches data from the specified path and returns a Promise containing the fetched data in JSON format.
|
|
206
|
-
*
|
|
207
|
-
* @template T - The expected shape of the parsed JSON data.
|
|
208
|
-
* @param path - The path to fetch the data from.
|
|
209
|
-
* @param args - Optional request initialization options, including a custom selector function and request body.
|
|
210
|
-
* - `body`: The request body, which will be automatically serialized to JSON if it's an object.
|
|
211
|
-
* - `selector`: A custom selector function to transform the response data. If not provided, the `jsonSelector` function will be used.
|
|
212
|
-
* - `headers`: Additional headers to include in the request. The `Accept` and `Content-Type` headers will be automatically set to `application/json`.
|
|
213
|
-
* @returns A Promise containing the fetched data in JSON format.
|
|
214
|
-
*/
|
|
215
|
-
public json<T = unknown>(
|
|
216
|
-
path: string,
|
|
217
|
-
args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
|
|
218
|
-
): Promise<T> {
|
|
219
|
-
return firstValueFrom(this.json$<T>(path, args));
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Fetches a blob resource from the specified path and returns a stream response.
|
|
224
|
-
*
|
|
225
|
-
* @template T - The expected shape of the fetched blob data.
|
|
226
|
-
* @param path - The path to the blob resource.
|
|
227
|
-
* @param args - Optional request initialization options, including a custom selector function.
|
|
228
|
-
* @returns A stream response containing the fetched blob data.
|
|
229
|
-
*/
|
|
230
|
-
public blob$<T = BlobResult>(
|
|
231
|
-
path: string,
|
|
232
|
-
args?: FetchRequestInit<T, TRequest, TResponse>,
|
|
233
|
-
): StreamResponse<T> {
|
|
234
|
-
// Get the selector value from the provided args, or use the default blobSelector
|
|
235
|
-
const selector = args?.selector ?? blobSelector;
|
|
236
|
-
|
|
237
|
-
// Create the FetchRequestInit object with the provided args and the selector
|
|
238
|
-
const init = {
|
|
239
|
-
...args,
|
|
240
|
-
selector,
|
|
241
|
-
} as FetchRequestInit<T, TRequest, TResponse>;
|
|
242
|
-
|
|
243
|
-
// Call the fetch$ method with the provided path and the constructed init object
|
|
244
|
-
return this.fetch$(path, init);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Fetches a blob from the specified path and returns a Promise that resolves to the blob result.
|
|
249
|
-
*
|
|
250
|
-
* @template T - The expected shape of the fetched blob data.
|
|
251
|
-
* @param path - The path to fetch the blob from.
|
|
252
|
-
* @param args - Optional arguments for the fetch request, including request body, headers, and response type.
|
|
253
|
-
* @returns A Promise that resolves to the blob result.
|
|
254
|
-
*/
|
|
255
|
-
public blob<T = BlobResult>(
|
|
256
|
-
path: string,
|
|
257
|
-
args?: FetchRequestInit<T, TRequest, TResponse>,
|
|
258
|
-
): Promise<T> {
|
|
259
|
-
return firstValueFrom(this.blob$(path, args));
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Initiates a Server-Sent Events (SSE) stream request to the specified path and returns a stream of events.
|
|
264
|
-
*
|
|
265
|
-
* @template T - The type of the event data expected from the SSE stream.
|
|
266
|
-
* @param path - The endpoint path to connect to for the SSE stream.
|
|
267
|
-
* @param args - Optional fetch request initialization options, including headers and abort signal.
|
|
268
|
-
* @param options - Optional selector options for customizing the SSE stream, excluding the abort signal.
|
|
269
|
-
*
|
|
270
|
-
* @returns A `StreamResponse` that emits `ServerSentEvent<T>` objects as they are received from the server.
|
|
271
|
-
*
|
|
272
|
-
* @example
|
|
273
|
-
* const sse$ = httpClient.sse$(
|
|
274
|
-
* '/events',
|
|
275
|
-
* { method: 'POST', body: JSON.stringify({ prompt: 'tell me a joke' }) },
|
|
276
|
-
* { eventFilter: ['message'] },
|
|
277
|
-
* );
|
|
278
|
-
* sse$.subscribe({
|
|
279
|
-
* next: (event) => console.log(event),
|
|
280
|
-
* error: (err) => console.error(err),
|
|
281
|
-
* complete: () => console.log('Completed'),
|
|
282
|
-
* });
|
|
283
|
-
*/
|
|
284
|
-
public sse$<T = unknown>(
|
|
285
|
-
path: string,
|
|
286
|
-
args?: FetchRequestInit<ServerSentEvent<T>, TRequest, TResponse> | null,
|
|
287
|
-
options?: Omit<SseSelectorOptions<T>, 'abortSignal'>,
|
|
288
|
-
): StreamResponse<ServerSentEvent<T>> {
|
|
289
|
-
// Setup default common headers for SSE
|
|
290
|
-
const headers = new Headers(args?.headers);
|
|
291
|
-
headers.append('Accept', 'text/event-stream');
|
|
292
|
-
headers.append('Content-Type', 'text/event-stream');
|
|
293
|
-
headers.append('Cache-Control', 'no-cache');
|
|
294
|
-
headers.append('Connection', 'keep-alive');
|
|
295
|
-
|
|
296
|
-
// Create the selector using the provided options and the abort signal from args
|
|
297
|
-
const selector = createSseSelector<T>({ ...options, abortSignal: args?.signal });
|
|
298
|
-
|
|
299
|
-
// Call the fetch$ method with the provided path and the constructed init object
|
|
300
|
-
return this._fetch$(path, { selector, ...args, headers } as FetchRequestInit<
|
|
301
|
-
ServerSentEvent<T>,
|
|
302
|
-
TRequest,
|
|
303
|
-
TResponse
|
|
304
|
-
>);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* @deprecated Use {@link json} instead.
|
|
309
|
-
* @template T - The expected shape of the parsed JSON data.
|
|
310
|
-
* @param path - The path to fetch the data from.
|
|
311
|
-
* @param args - Optional request initialization options, including a custom selector function and request body.
|
|
312
|
-
* @returns A Promise containing the fetched data in JSON format.
|
|
313
|
-
*/
|
|
314
|
-
public jsonAsync<T = unknown>(
|
|
315
|
-
path: string,
|
|
316
|
-
args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
|
|
317
|
-
): Promise<T> {
|
|
318
|
-
return this.json(path, args);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Executes an HTTP request using the specified method and path.
|
|
323
|
-
*
|
|
324
|
-
* @template T - The expected shape of the result.
|
|
325
|
-
* @template TMethod - The name of the `IHttpClient` method to invoke.
|
|
326
|
-
* @param method - The HTTP method to use for the request, such as 'fetch', 'json', or 'blob'.
|
|
327
|
-
* @param path - The path to the resource to fetch.
|
|
328
|
-
* @param init - Optional request initialization options, including request body, headers, and response type.
|
|
329
|
-
* @returns The result of the HTTP request, which will be of the same type as the return value of the specified method.
|
|
330
|
-
*/
|
|
331
|
-
public execute<T = TResponse, TMethod extends 'fetch' | 'fetch$' | 'json' | 'json$' = 'fetch'>(
|
|
332
|
-
method: TMethod,
|
|
333
|
-
path: string,
|
|
334
|
-
init?: FetchRequestInit<T, TRequest, TResponse>,
|
|
335
|
-
): ReturnType<IHttpClient[TMethod]> {
|
|
336
|
-
return this[method](path, init) as ReturnType<IHttpClient[TMethod]>;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Aborts any ongoing HTTP requests made by this `IHttpClient` instance.
|
|
341
|
-
* This will trigger the `takeUntil` operator in the `_fetch$` method,
|
|
342
|
-
* causing any in-flight requests to be cancelled, and abort the
|
|
343
|
-
* per-request `AbortSignal` passed through to `_performFetch`, so the
|
|
344
|
-
* underlying network call is cancelled even behind registered middleware.
|
|
345
|
-
*/
|
|
346
|
-
public abort(): void {
|
|
347
|
-
this._abort$.next();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Fetches data from the specified path and returns an Observable that emits the response.
|
|
352
|
-
*
|
|
353
|
-
* @template T - The expected shape of the emitted data.
|
|
354
|
-
* @param path - The path to fetch the data from.
|
|
355
|
-
* @param args - Optional arguments for the fetch request, including a response selector function, request body, headers, and response type.
|
|
356
|
-
* @returns {Observable<T>} An Observable that emits the response data.
|
|
357
|
-
*
|
|
358
|
-
* @throws {HttpResponseError} When the optional `selector` throws while transforming the response.
|
|
359
|
-
*
|
|
360
|
-
* This method handles the following steps:
|
|
361
|
-
* 1. Resolves the full URL by combining the base URI and the provided path.
|
|
362
|
-
* 2. Prepares the request by passing it through the `requestHandler.process()` method.
|
|
363
|
-
* 3. Executes the fetch request using the prepared request.
|
|
364
|
-
* 4. Prepares the response by passing it through the `responseHandler.process()` method.
|
|
365
|
-
* 5. Applies the optional response selector function to transform the response data.
|
|
366
|
-
* 6. Cancels the request if the `_abort$` observable emits.
|
|
367
|
-
*/
|
|
368
|
-
protected _fetch$<T = TResponse>(
|
|
369
|
-
path: string,
|
|
370
|
-
args?: FetchRequestInit<T, TRequest, TResponse>,
|
|
371
|
-
): Observable<T> {
|
|
372
|
-
const { selector, ...options } = args || {};
|
|
373
|
-
// A registered middleware's `next(...)` resolves through a `Promise` (see
|
|
374
|
-
// `HttpMiddlewareHandler`), which `firstValueFrom` fulfils via its own independent
|
|
375
|
-
// subscription to `_performFetch` — one the `takeUntil(this._abort$)` below never reaches,
|
|
376
|
-
// since it sits outside the subscription tree that `takeUntil` tears down. Combining this
|
|
377
|
-
// controller's signal into the request `init` lets `_performFetch` (`fromFetch` by default)
|
|
378
|
-
// abort the underlying network call directly, regardless of whether middleware severed the
|
|
379
|
-
// RxJS teardown chain.
|
|
380
|
-
const abortController = new AbortController();
|
|
381
|
-
// abort only fires once per request; the subscription is torn down in `finalize` below
|
|
382
|
-
const abort = this._abort$.pipe(take(1)).subscribe(() => abortController.abort());
|
|
383
|
-
const callerSignal = (options as RequestInit).signal;
|
|
384
|
-
const signal = callerSignal
|
|
385
|
-
? AbortSignal.any([callerSignal, abortController.signal])
|
|
386
|
-
: abortController.signal;
|
|
387
|
-
// `fromFetch` yields the raw fetch `Response`, but `responseHandler.process()` (called via
|
|
388
|
-
// `_prepareResponse`) expects the pipeline's generic `TResponse` shape — cast through
|
|
389
|
-
// `unknown` since the two are only compatible after that processing step.
|
|
390
|
-
const response$ = of({
|
|
391
|
-
...options,
|
|
392
|
-
signal,
|
|
393
|
-
path,
|
|
394
|
-
uri: this._resolveUrl(path),
|
|
395
|
-
} as TRequest).pipe(
|
|
396
|
-
/** prepare request, allow extensions to modify request */
|
|
397
|
-
switchMap((x) => this._prepareRequest(x)),
|
|
398
|
-
/** push request to event buss */
|
|
399
|
-
tap((x) => this._request$.next(x)),
|
|
400
|
-
/** execute request through registered middleware, terminating at _performFetch */
|
|
401
|
-
switchMap(({ uri, path: _path, ...init }) =>
|
|
402
|
-
this.middlewareHandler.process(uri, init, (u, i) => this._performFetch(u, i)),
|
|
403
|
-
),
|
|
404
|
-
/** prepare response, allow extensions to modify response */
|
|
405
|
-
switchMap((x) => this._prepareResponse(x as unknown as TResponse)),
|
|
406
|
-
/** push response to event buss */
|
|
407
|
-
tap((x) => this._response$.next(x)),
|
|
408
|
-
|
|
409
|
-
/** execute selector */
|
|
410
|
-
switchMap((response) => {
|
|
411
|
-
// only run the selector when one was provided; otherwise pass the response through untouched
|
|
412
|
-
if (selector) {
|
|
413
|
-
try {
|
|
414
|
-
return selector(response);
|
|
415
|
-
} catch (err) {
|
|
416
|
-
throw new HttpResponseError(
|
|
417
|
-
'failed to execute response selector',
|
|
418
|
-
response as Response,
|
|
419
|
-
{
|
|
420
|
-
cause: err,
|
|
421
|
-
},
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
return of(response);
|
|
426
|
-
}),
|
|
427
|
-
/** cancel request on abort signal */
|
|
428
|
-
takeUntil(this._abort$),
|
|
429
|
-
/** the abort signal subscription only ever fires once; tear it down once this request settles either way */
|
|
430
|
-
finalize(() => abort.unsubscribe()),
|
|
431
|
-
);
|
|
432
|
-
// The pipe above resolves to the per-call generic `T` (via the optional `selector`), but
|
|
433
|
-
// the observable's static type tracks the class-level `TResponse` — cast to the caller's `T`.
|
|
434
|
-
return response$ as unknown as Observable<T>;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Performs the actual network call for a prepared request.
|
|
439
|
-
*
|
|
440
|
-
* @remarks
|
|
441
|
-
* Isolated from {@link _fetch$} so a test double can replace only this step —
|
|
442
|
-
* matching a request against registered route handlers instead of reaching
|
|
443
|
-
* the network — while everything around it (request preparation, the
|
|
444
|
-
* response pipeline, abort handling) runs unchanged. See
|
|
445
|
-
* `@equinor/fusion-framework-module-http/mock`.
|
|
446
|
-
*
|
|
447
|
-
* @param uri - The fully resolved URL for the request.
|
|
448
|
-
* @param init - The prepared `fetch` request options.
|
|
449
|
-
* @returns An observable of the raw `Response`, ahead of {@link _prepareResponse}.
|
|
450
|
-
*/
|
|
451
|
-
protected _performFetch(uri: string, init: RequestInit): ObservableInput<Response> {
|
|
452
|
-
return fromFetch(uri, init);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Prepares the request by passing it through the `requestHandler.process()` method.
|
|
457
|
-
* This method is an implementation detail of the `_fetch$()` method, and is not part of the public API.
|
|
458
|
-
* It takes a `TRequest` object as input, which represents the request data, and returns an `ObservableInput<TRequest>`,
|
|
459
|
-
* which can be used to further process the request before it is executed.
|
|
460
|
-
*
|
|
461
|
-
* @param init The request data to be processed.
|
|
462
|
-
* @returns An `ObservableInput<TRequest>` that represents the processed request.
|
|
463
|
-
*/
|
|
464
|
-
protected _prepareRequest(init: TRequest): ObservableInput<TRequest> {
|
|
465
|
-
return this.requestHandler.process(init);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Prepares the response by passing it through the `responseHandler.process()` method.
|
|
470
|
-
* This method is an implementation detail of the `_fetch$()` method, and is not part of the public API.
|
|
471
|
-
* It takes a `TResponse` object as input, which represents the response data, and returns an `ObservableInput<TResponse>`,
|
|
472
|
-
* which can be used to further process the response before it is returned.
|
|
473
|
-
*
|
|
474
|
-
* @param response The response data to be processed.
|
|
475
|
-
* @returns An `ObservableInput<TResponse>` that represents the processed response.
|
|
476
|
-
*/
|
|
477
|
-
protected _prepareResponse(response: TResponse): ObservableInput<TResponse> {
|
|
478
|
-
return this.responseHandler.process(response);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Resolves the full URL for a given path by combining it with the base URL.
|
|
483
|
-
* This is a protected method and is an implementation detail of the `HttpClient` class.
|
|
484
|
-
*
|
|
485
|
-
* @param path - The path to be resolved.
|
|
486
|
-
* @returns The full URL for the given path.
|
|
487
|
-
*/
|
|
488
|
-
protected _resolveUrl(path: string): string {
|
|
489
|
-
const { origin, pathname: basePath } = new URL(
|
|
490
|
-
this.uri,
|
|
491
|
-
this.uri.startsWith('http') ? undefined : window.location.origin,
|
|
492
|
-
);
|
|
493
|
-
const pathname = [basePath, path].join('/').replace(/\/{2,}/g, '/');
|
|
494
|
-
return new URL(pathname, origin).href;
|
|
495
|
-
}
|
|
496
|
-
}
|
package/src/lib/client/index.ts
DELETED