@equinor/fusion-framework-module-http 0.1.0-beta.9 → 0.2.0
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/CHANGELOG.md +42 -0
- package/README.md +49 -3
- package/dist/esm/client-msal.js +6 -5
- package/dist/esm/client-msal.js.map +1 -1
- package/dist/esm/client.js +29 -5
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/configurator.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/provider.js +16 -9
- package/dist/esm/provider.js.map +1 -1
- package/dist/esm/selector.js +12 -0
- package/dist/esm/selector.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/client-msal.d.ts +7 -5
- package/dist/types/client.d.ts +17 -9
- package/dist/types/configurator.d.ts +5 -4
- package/dist/types/index.d.ts +2 -1
- package/dist/types/provider.d.ts +4 -2
- package/dist/types/selector.d.ts +1 -0
- package/package.json +6 -3
- package/src/client-msal.ts +18 -8
- package/src/client.ts +97 -24
- package/src/configurator.ts +50 -5
- package/src/index.ts +7 -1
- package/src/provider.ts +23 -9
- package/src/selector.ts +12 -0
package/src/client.ts
CHANGED
|
@@ -3,15 +3,25 @@ import { switchMap, takeUntil, tap } from 'rxjs/operators';
|
|
|
3
3
|
import { fromFetch } from 'rxjs/fetch';
|
|
4
4
|
|
|
5
5
|
import { ProcessOperators } from './process-operators';
|
|
6
|
+
import { jsonSelector } from './selector';
|
|
6
7
|
|
|
7
|
-
export type
|
|
8
|
+
export type FetchRequest = RequestInit & {
|
|
9
|
+
uri: string;
|
|
10
|
+
path: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type FetchRequestInit<
|
|
14
|
+
TReturn = unknown,
|
|
15
|
+
TRequest = FetchRequest,
|
|
16
|
+
TResponse = Response
|
|
17
|
+
> = Omit<TRequest, 'uri' | 'path'> & {
|
|
18
|
+
selector?: (response: TResponse) => ObservableInput<TReturn>;
|
|
19
|
+
};
|
|
8
20
|
|
|
9
21
|
/**
|
|
10
22
|
* Extends @see {ProcessOperators} for pre-processing requests.
|
|
11
23
|
*/
|
|
12
|
-
export class HttpRequestHandler<
|
|
13
|
-
T extends HttpRequestInit = HttpRequestInit
|
|
14
|
-
> extends ProcessOperators<T> {
|
|
24
|
+
export class HttpRequestHandler<T extends FetchRequest = FetchRequest> extends ProcessOperators<T> {
|
|
15
25
|
/**
|
|
16
26
|
* Set header that will apply on all requests done by consumer @see {HttpClient}
|
|
17
27
|
* @param key - name of header
|
|
@@ -26,16 +36,17 @@ export class HttpRequestHandler<
|
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
export type HttpClientCreateOptions<T extends
|
|
39
|
+
export type HttpClientCreateOptions<T extends FetchRequest = FetchRequest> = {
|
|
30
40
|
requestHandler: HttpRequestHandler<T>;
|
|
31
41
|
};
|
|
32
42
|
|
|
33
43
|
export type HttpResponseHandler<T> = (response: Response) => Promise<T>;
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
/**
|
|
46
|
+
* @template TRequest request arguments @see {@link https://developer.mozilla.org/en-US/docs/Web/API/request|request}
|
|
47
|
+
* @template TResponse request arguments @see {@link https://developer.mozilla.org/en-US/docs/Web/API/response|response}
|
|
48
|
+
*/
|
|
49
|
+
export interface IHttpClient<TRequest extends FetchRequest = FetchRequest, TResponse = Response> {
|
|
39
50
|
uri: string;
|
|
40
51
|
/** pre-processor of requests */
|
|
41
52
|
readonly requestHandler: HttpRequestHandler<TRequest>;
|
|
@@ -52,11 +63,13 @@ export interface IHttpClient<
|
|
|
52
63
|
* Observable request.
|
|
53
64
|
* Simplifies execution of request and
|
|
54
65
|
* note: request will not be executed until subscribe!
|
|
66
|
+
*
|
|
55
67
|
* @see {@link https://rxjs.dev/api/fetch/fromFetch|RXJS}
|
|
68
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch|fetch}
|
|
56
69
|
* @example
|
|
57
70
|
* ```ts
|
|
58
71
|
* // Observer changes of a input field
|
|
59
|
-
* const client =
|
|
72
|
+
* const client = modules.http.createClient('my-client');
|
|
60
73
|
* const input$ = fromEvent(document.getElementById('input'), 'input');
|
|
61
74
|
* input$.pipe(
|
|
62
75
|
* // only call after no key input in .5s
|
|
@@ -80,10 +93,14 @@ export interface IHttpClient<
|
|
|
80
93
|
* ).subscribe(console.log);
|
|
81
94
|
* ```
|
|
82
95
|
*/
|
|
83
|
-
fetch
|
|
96
|
+
fetch<T = TResponse>(
|
|
97
|
+
path: string,
|
|
98
|
+
init?: FetchRequestInit<T, TRequest, TResponse>
|
|
99
|
+
): Observable<T>;
|
|
84
100
|
|
|
85
101
|
/**
|
|
86
102
|
* Fetch a resource as an promise
|
|
103
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch|fetch}
|
|
87
104
|
* @example
|
|
88
105
|
* ```ts
|
|
89
106
|
* let controller: AbortController;
|
|
@@ -110,7 +127,20 @@ export interface IHttpClient<
|
|
|
110
127
|
* });
|
|
111
128
|
* ```
|
|
112
129
|
*/
|
|
113
|
-
fetchAsync
|
|
130
|
+
fetchAsync<T = TResponse>(
|
|
131
|
+
path: string,
|
|
132
|
+
init?: FetchRequestInit<T, TRequest, TResponse>
|
|
133
|
+
): Promise<T>;
|
|
134
|
+
|
|
135
|
+
json<T = TResponse>(
|
|
136
|
+
path: string,
|
|
137
|
+
init?: FetchRequestInit<T, TRequest, TResponse>
|
|
138
|
+
): Observable<T>;
|
|
139
|
+
|
|
140
|
+
jsonAsync<T = TResponse>(
|
|
141
|
+
path: string,
|
|
142
|
+
init?: FetchRequestInit<T, TRequest, TResponse>
|
|
143
|
+
): Promise<T>;
|
|
114
144
|
|
|
115
145
|
/**
|
|
116
146
|
* Abort all ongoing request for current client
|
|
@@ -119,7 +149,7 @@ export interface IHttpClient<
|
|
|
119
149
|
}
|
|
120
150
|
|
|
121
151
|
/** Base http client for executing requests */
|
|
122
|
-
export class HttpClient<TRequest extends
|
|
152
|
+
export class HttpClient<TRequest extends FetchRequest = FetchRequest, TResponse = Response>
|
|
123
153
|
implements IHttpClient<TRequest, TResponse>
|
|
124
154
|
{
|
|
125
155
|
readonly requestHandler: HttpRequestHandler<TRequest>;
|
|
@@ -153,9 +183,57 @@ export class HttpClient<TRequest extends HttpRequestInit = HttpRequestInit, TRes
|
|
|
153
183
|
// called by children for constructor setup
|
|
154
184
|
}
|
|
155
185
|
|
|
156
|
-
public fetch
|
|
157
|
-
|
|
158
|
-
|
|
186
|
+
public fetch<T = TResponse>(
|
|
187
|
+
path: string,
|
|
188
|
+
args?: FetchRequestInit<T, TRequest, TResponse>
|
|
189
|
+
): Observable<T> {
|
|
190
|
+
return this._fetch(path, args);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public fetchAsync<T = TResponse>(
|
|
194
|
+
path: string,
|
|
195
|
+
args?: FetchRequestInit<T, TRequest, TResponse>
|
|
196
|
+
): Promise<T> {
|
|
197
|
+
return firstValueFrom(this.fetch<T>(path, args));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public json<T = TResponse>(
|
|
201
|
+
path: string,
|
|
202
|
+
args?: FetchRequestInit<T, TRequest, TResponse>
|
|
203
|
+
): Observable<T> {
|
|
204
|
+
const body = typeof args?.body === 'object' ? JSON.stringify(args?.body) : args?.body;
|
|
205
|
+
const selector = args?.selector ?? jsonSelector;
|
|
206
|
+
const header = new Headers(args?.headers);
|
|
207
|
+
header.append('Content-Type', 'application/json');
|
|
208
|
+
return this.fetch(path, {
|
|
209
|
+
...args,
|
|
210
|
+
body,
|
|
211
|
+
selector,
|
|
212
|
+
} as FetchRequestInit<T, TRequest, TResponse>);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public jsonAsync<T = TResponse>(
|
|
216
|
+
path: string,
|
|
217
|
+
args?: FetchRequestInit<T, TRequest, TResponse>
|
|
218
|
+
): Promise<T> {
|
|
219
|
+
return firstValueFrom(this.json<T>(path, args));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public abort(): void {
|
|
223
|
+
this._abort$.next();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
protected _fetch<T = TResponse>(
|
|
227
|
+
path: string,
|
|
228
|
+
args?: FetchRequestInit<T, TRequest, TResponse>
|
|
229
|
+
): Observable<T> {
|
|
230
|
+
const { selector, ...options } = Object.assign({}, args || { selector: undefined }, {
|
|
231
|
+
path,
|
|
232
|
+
});
|
|
233
|
+
const response$ = of({
|
|
234
|
+
...options,
|
|
235
|
+
uri: this._resolveUrl(options.path),
|
|
236
|
+
} as TRequest).pipe(
|
|
159
237
|
/** prepare request, allow extensions to modify request */
|
|
160
238
|
switchMap((x) => this._prepareRequest(x)),
|
|
161
239
|
/** push request to event buss */
|
|
@@ -166,17 +244,12 @@ export class HttpClient<TRequest extends HttpRequestInit = HttpRequestInit, TRes
|
|
|
166
244
|
switchMap((x) => this._prepareResponse(x)),
|
|
167
245
|
/** push response to event buss */
|
|
168
246
|
tap((x) => this._response$.next(x)),
|
|
247
|
+
|
|
248
|
+
switchMap((x) => (selector ? selector(x) : Promise.resolve(x))),
|
|
169
249
|
/** cancel request on abort signal */
|
|
170
250
|
takeUntil(this._abort$)
|
|
171
251
|
);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
public fetchAsync(init: Omit<TRequest, 'uri'> | string): Promise<TResponse> {
|
|
175
|
-
return firstValueFrom(this.fetch(init));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
public abort(): void {
|
|
179
|
-
this._abort$.next();
|
|
252
|
+
return response$ as unknown as Observable<T>;
|
|
180
253
|
}
|
|
181
254
|
|
|
182
255
|
protected _prepareRequest(init: TRequest): ObservableInput<TRequest> {
|
package/src/configurator.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { HttpRequestHandler,
|
|
1
|
+
import { HttpRequestHandler, FetchRequest, IHttpClient } from './client';
|
|
2
2
|
|
|
3
|
-
interface HttpClientConstructorOptions<TInit extends
|
|
3
|
+
interface HttpClientConstructorOptions<TInit extends FetchRequest> {
|
|
4
4
|
requestHandler: HttpRequestHandler<TInit>;
|
|
5
5
|
}
|
|
6
6
|
|
|
@@ -11,8 +11,9 @@ interface HttpClientConstructor<TClient extends IHttpClient> {
|
|
|
11
11
|
): TClient;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
interface HttpClientOptions<TClient extends IHttpClient> {
|
|
14
|
+
export interface HttpClientOptions<TClient extends IHttpClient> {
|
|
15
15
|
baseUri?: string;
|
|
16
|
+
defaultScopes?: string[];
|
|
16
17
|
ctor?: HttpClientConstructor<TClient>;
|
|
17
18
|
onCreate?: (client: TClient) => void;
|
|
18
19
|
requestHandler?: HttpRequestHandler<HttpClientRequestInitType<TClient>>;
|
|
@@ -20,49 +21,93 @@ interface HttpClientOptions<TClient extends IHttpClient> {
|
|
|
20
21
|
|
|
21
22
|
type HttpClientRequestInitType<T extends IHttpClient> = T extends IHttpClient<infer U> ? U : never;
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Instance for configuring http client
|
|
26
|
+
* @template TClient base type of client that the provider will create
|
|
27
|
+
*/
|
|
23
28
|
export interface IHttpClientConfigurator<TClient extends IHttpClient = IHttpClient> {
|
|
24
29
|
readonly clients: Record<string, HttpClientOptions<TClient>>;
|
|
25
30
|
readonly defaultHttpClientCtor: HttpClientConstructor<TClient>;
|
|
26
31
|
readonly defaultHttpRequestHandler: HttpRequestHandler<HttpClientRequestInitType<TClient>>;
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Configure a client with arguments
|
|
35
|
+
* @param name name of the client
|
|
36
|
+
* @param args option that are used cor creating a client
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* configurator.http.configureClient('foo',{
|
|
40
|
+
* baseUri: 'https://foo.bar',
|
|
41
|
+
* defaultScopes: ['foobar/.default']
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
30
45
|
configureClient<T extends TClient>(
|
|
31
46
|
name: string,
|
|
32
47
|
args: HttpClientOptions<T>
|
|
33
48
|
): HttpClientConfigurator<TClient>;
|
|
34
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Configure a simple client by name to an endpoint
|
|
52
|
+
* @param name name of the client
|
|
53
|
+
* @param uri base endpoint for the client
|
|
54
|
+
*/
|
|
55
|
+
configureClient(name: string, uri: string): HttpClientConfigurator<TClient>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates a client with callback configuration
|
|
59
|
+
* @param name name of the client
|
|
60
|
+
* @param onCreate callback when a client is created
|
|
61
|
+
* ```ts
|
|
62
|
+
* configurator.http.configureClient('foo',(client) => {
|
|
63
|
+
* client.requestHandler.add('logger', (request) => console.log(request));
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
35
67
|
configureClient<T extends TClient>(
|
|
36
68
|
name: string,
|
|
37
69
|
onCreate: (client: T) => void
|
|
38
70
|
): HttpClientConfigurator<TClient>;
|
|
39
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Check if there is a configuration for provided name
|
|
74
|
+
*/
|
|
40
75
|
hasClient(name: string): boolean;
|
|
41
76
|
}
|
|
42
77
|
|
|
78
|
+
/** @inheritdoc */
|
|
43
79
|
export class HttpClientConfigurator<TClient extends IHttpClient>
|
|
44
80
|
implements IHttpClientConfigurator<TClient>
|
|
45
81
|
{
|
|
46
82
|
protected _clients: Record<string, HttpClientOptions<TClient>> = {};
|
|
47
83
|
|
|
84
|
+
/** Get a clone of all configured clients */
|
|
48
85
|
public get clients(): Record<string, HttpClientOptions<TClient>> {
|
|
49
86
|
return { ...this._clients };
|
|
50
87
|
}
|
|
51
88
|
|
|
89
|
+
/** default class for creation of http clients */
|
|
52
90
|
readonly defaultHttpClientCtor: HttpClientConstructor<TClient>;
|
|
53
91
|
|
|
92
|
+
/** default request handler for http clients, applied on creation */
|
|
54
93
|
readonly defaultHttpRequestHandler = new HttpRequestHandler<
|
|
55
94
|
HttpClientRequestInitType<TClient>
|
|
56
95
|
>();
|
|
57
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Create a instance of http configuration
|
|
99
|
+
* @param client defaultHttpRequestHandler
|
|
100
|
+
*/
|
|
58
101
|
constructor(client: HttpClientConstructor<TClient>) {
|
|
59
102
|
this.defaultHttpClientCtor = client;
|
|
60
103
|
}
|
|
61
104
|
|
|
105
|
+
/** @inheritdoc */
|
|
62
106
|
hasClient(name: string): boolean {
|
|
63
107
|
return Object.keys(this._clients).includes(name);
|
|
64
108
|
}
|
|
65
109
|
|
|
110
|
+
/** @inheritdoc */
|
|
66
111
|
configureClient<T extends TClient>(
|
|
67
112
|
name: string,
|
|
68
113
|
args: string | HttpClientOptions<T> | HttpClientOptions<T>['onCreate']
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* [[include:module-http/README.MD]]
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { HttpClient, IHttpClient, FetchRequestInit } from './client';
|
|
2
7
|
export { HttpClientMsal } from './client-msal';
|
|
3
8
|
|
|
4
9
|
export * from './configurator';
|
|
5
10
|
export * from './provider';
|
|
6
11
|
export * from './module';
|
|
12
|
+
export * from './selector';
|
|
7
13
|
|
|
8
14
|
export { default } from './module';
|
package/src/provider.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from './client';
|
|
2
|
-
import { IHttpClientConfigurator } from './configurator';
|
|
2
|
+
import { HttpClientOptions, IHttpClientConfigurator } from './configurator';
|
|
3
3
|
|
|
4
4
|
export class ClientNotFoundException extends Error {
|
|
5
5
|
constructor(message: string) {
|
|
@@ -12,6 +12,7 @@ export interface IHttpClientProvider<TClient extends HttpClient = HttpClient> {
|
|
|
12
12
|
hasClient(key: string): boolean;
|
|
13
13
|
/** create a new http client */
|
|
14
14
|
createClient(key: string): TClient;
|
|
15
|
+
createClient(key: HttpClientOptions<TClient>): TClient;
|
|
15
16
|
/**
|
|
16
17
|
* Class cast creation of custom client
|
|
17
18
|
* @example
|
|
@@ -46,21 +47,18 @@ export class HttpClientProvider<TClient extends HttpClient>
|
|
|
46
47
|
return Object.keys(this.config.clients).includes(key);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
public createClient(
|
|
50
|
-
|
|
51
|
-
if (!config && isURL(key)) {
|
|
52
|
-
config = { baseUri: key };
|
|
53
|
-
} else if (!config) {
|
|
54
|
-
throw new ClientNotFoundException(`No registered http client for key [${key}]`);
|
|
55
|
-
}
|
|
50
|
+
public createClient(keyOrConfig: string | HttpClientOptions<TClient>): TClient {
|
|
51
|
+
const config = this._resolveConfig(keyOrConfig);
|
|
56
52
|
const {
|
|
57
53
|
baseUri,
|
|
54
|
+
defaultScopes = [],
|
|
58
55
|
onCreate,
|
|
59
56
|
ctor = this.config.defaultHttpClientCtor,
|
|
60
57
|
requestHandler = this.config.defaultHttpRequestHandler,
|
|
61
|
-
} = config
|
|
58
|
+
} = config as HttpClientOptions<TClient>;
|
|
62
59
|
const options = { requestHandler };
|
|
63
60
|
const instance = new ctor(baseUri || '', options) as TClient;
|
|
61
|
+
Object.assign(instance, { defaultScopes });
|
|
64
62
|
onCreate && onCreate(instance as TClient);
|
|
65
63
|
return instance as TClient;
|
|
66
64
|
}
|
|
@@ -68,4 +66,20 @@ export class HttpClientProvider<TClient extends HttpClient>
|
|
|
68
66
|
public createCustomClient<T extends HttpClient>(key: string): T {
|
|
69
67
|
return this.createClient(key) as unknown as T;
|
|
70
68
|
}
|
|
69
|
+
|
|
70
|
+
protected _resolveConfig(
|
|
71
|
+
keyOrConfig: string | HttpClientOptions<TClient>
|
|
72
|
+
): HttpClientOptions<TClient> {
|
|
73
|
+
if (typeof keyOrConfig === 'string') {
|
|
74
|
+
const config = this.config.clients[keyOrConfig];
|
|
75
|
+
if (!config && isURL(keyOrConfig)) {
|
|
76
|
+
return { baseUri: keyOrConfig };
|
|
77
|
+
} else if (!config) {
|
|
78
|
+
throw new ClientNotFoundException(
|
|
79
|
+
`No registered http client for key [${keyOrConfig}]`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return keyOrConfig as HttpClientOptions<TClient>;
|
|
84
|
+
}
|
|
71
85
|
}
|
package/src/selector.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const jsonSelector = <TType = unknown, TResponse extends Response = Response>(
|
|
2
|
+
response: TResponse
|
|
3
|
+
): Promise<TType> => {
|
|
4
|
+
if (!response.ok) {
|
|
5
|
+
throw new Error('Network response was not OK');
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
return response.json();
|
|
9
|
+
} catch (err) {
|
|
10
|
+
throw Error('failed to parse response', { cause: err as Error });
|
|
11
|
+
}
|
|
12
|
+
};
|