@equinor/fusion-framework-module-http 0.0.0-next-20230915111233

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.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +427 -0
  3. package/LICENSE +21 -0
  4. package/README.md +57 -0
  5. package/dist/esm/configurator.js +22 -0
  6. package/dist/esm/configurator.js.map +1 -0
  7. package/dist/esm/errors.js +8 -0
  8. package/dist/esm/errors.js.map +1 -0
  9. package/dist/esm/index.js +6 -0
  10. package/dist/esm/index.js.map +1 -0
  11. package/dist/esm/lib/client/client-msal.js +15 -0
  12. package/dist/esm/lib/client/client-msal.js.map +1 -0
  13. package/dist/esm/lib/client/client.js +108 -0
  14. package/dist/esm/lib/client/client.js.map +1 -0
  15. package/dist/esm/lib/client/index.js +4 -0
  16. package/dist/esm/lib/client/index.js.map +1 -0
  17. package/dist/esm/lib/client/types.js +2 -0
  18. package/dist/esm/lib/client/types.js.map +1 -0
  19. package/dist/esm/lib/index.js +4 -0
  20. package/dist/esm/lib/index.js.map +1 -0
  21. package/dist/esm/lib/operators/http-request-handler.js +10 -0
  22. package/dist/esm/lib/operators/http-request-handler.js.map +1 -0
  23. package/dist/esm/lib/operators/http-response-handler.js +5 -0
  24. package/dist/esm/lib/operators/http-response-handler.js.map +1 -0
  25. package/dist/esm/lib/operators/index.js +5 -0
  26. package/dist/esm/lib/operators/index.js.map +1 -0
  27. package/dist/esm/lib/operators/process-operators.js +27 -0
  28. package/dist/esm/lib/operators/process-operators.js.map +1 -0
  29. package/dist/esm/lib/operators/request-operator-header.js +7 -0
  30. package/dist/esm/lib/operators/request-operator-header.js.map +1 -0
  31. package/dist/esm/lib/operators/types.js +2 -0
  32. package/dist/esm/lib/operators/types.js.map +1 -0
  33. package/dist/esm/lib/selectors/blob-selector.js +16 -0
  34. package/dist/esm/lib/selectors/blob-selector.js.map +1 -0
  35. package/dist/esm/lib/selectors/index.js +3 -0
  36. package/dist/esm/lib/selectors/index.js.map +1 -0
  37. package/dist/esm/lib/selectors/json-selector.js +16 -0
  38. package/dist/esm/lib/selectors/json-selector.js.map +1 -0
  39. package/dist/esm/module.js +48 -0
  40. package/dist/esm/module.js.map +1 -0
  41. package/dist/esm/provider.js +51 -0
  42. package/dist/esm/provider.js.map +1 -0
  43. package/dist/esm/version.js +2 -0
  44. package/dist/esm/version.js.map +1 -0
  45. package/dist/tsconfig.tsbuildinfo +1 -0
  46. package/dist/types/configurator.d.ts +37 -0
  47. package/dist/types/errors.d.ts +5 -0
  48. package/dist/types/index.d.ts +6 -0
  49. package/dist/types/lib/client/client-msal.d.ts +12 -0
  50. package/dist/types/lib/client/client.d.ts +34 -0
  51. package/dist/types/lib/client/index.d.ts +3 -0
  52. package/dist/types/lib/client/types.d.ts +36 -0
  53. package/dist/types/lib/index.d.ts +3 -0
  54. package/dist/types/lib/operators/http-request-handler.d.ts +6 -0
  55. package/dist/types/lib/operators/http-response-handler.d.ts +4 -0
  56. package/dist/types/lib/operators/index.d.ts +4 -0
  57. package/dist/types/lib/operators/process-operators.d.ts +9 -0
  58. package/dist/types/lib/operators/request-operator-header.d.ts +4 -0
  59. package/dist/types/lib/operators/types.d.ts +14 -0
  60. package/dist/types/lib/selectors/blob-selector.d.ts +2 -0
  61. package/dist/types/lib/selectors/index.d.ts +2 -0
  62. package/dist/types/lib/selectors/json-selector.d.ts +2 -0
  63. package/dist/types/module.d.ts +18 -0
  64. package/dist/types/provider.d.ts +23 -0
  65. package/dist/types/version.d.ts +1 -0
  66. package/package.json +51 -0
  67. package/src/configurator.ts +131 -0
  68. package/src/errors.ts +9 -0
  69. package/src/index.ts +14 -0
  70. package/src/lib/client/client-msal.ts +33 -0
  71. package/src/lib/client/client.ts +204 -0
  72. package/src/lib/client/index.ts +4 -0
  73. package/src/lib/client/types.ts +168 -0
  74. package/src/lib/index.ts +3 -0
  75. package/src/lib/operators/http-request-handler.ts +21 -0
  76. package/src/lib/operators/http-response-handler.ts +5 -0
  77. package/src/lib/operators/index.ts +5 -0
  78. package/src/lib/operators/process-operators.ts +43 -0
  79. package/src/lib/operators/request-operator-header.ts +12 -0
  80. package/src/lib/operators/types.ts +42 -0
  81. package/src/lib/selectors/blob-selector.ts +19 -0
  82. package/src/lib/selectors/index.ts +2 -0
  83. package/src/lib/selectors/json-selector.ts +19 -0
  84. package/src/module.ts +80 -0
  85. package/src/provider.ts +98 -0
  86. package/src/version.ts +2 -0
  87. package/tsconfig.json +24 -0
@@ -0,0 +1,43 @@
1
+ import { from, of } from 'rxjs';
2
+ import type { Observable } from 'rxjs';
3
+ import { last, mergeScan } from 'rxjs/operators';
4
+ import { IProcessOperators, ProcessOperator } from './types';
5
+
6
+ export class ProcessOperators<T> implements IProcessOperators<T> {
7
+ protected _operators: Record<string, ProcessOperator<T>> = {};
8
+
9
+ add(key: string, operator: ProcessOperator<T>): ProcessOperators<T> {
10
+ if (Object.keys(this._operators).includes(key))
11
+ throw Error(`Operator [${key}] already defined`);
12
+ return this.set(key, operator);
13
+ }
14
+
15
+ set(key: string, operator: ProcessOperator<T>): ProcessOperators<T> {
16
+ this._operators[key] = operator;
17
+ return this;
18
+ }
19
+
20
+ get(key: string): ProcessOperator<T> {
21
+ return this._operators[key];
22
+ }
23
+
24
+ process(request: T): Observable<T> {
25
+ const operators = Object.values(this._operators);
26
+ /** if no operators registered, just return the observable value */
27
+ if (!operators.length) {
28
+ return of(request);
29
+ }
30
+ return from(Object.values(this._operators)).pipe(
31
+ mergeScan(
32
+ // resolve current operator and return result or previous if void
33
+ (value, operator) => Promise.resolve(operator(value)).then((x) => x ?? value),
34
+ // initial value
35
+ request,
36
+ // only allow concurrency of one operator
37
+ 1,
38
+ ),
39
+ // output result of last operator
40
+ last(),
41
+ );
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import type { FetchRequest } from '../client';
2
+ import type { ProcessOperator } from './types';
3
+
4
+ export const requestOperatorHeader =
5
+ <T extends FetchRequest = FetchRequest>(key: string, value: string): ProcessOperator<T> =>
6
+ (request) => {
7
+ const headers = new Headers(request.headers);
8
+ headers.append(key, value);
9
+ return { ...request, headers };
10
+ };
11
+
12
+ export default requestOperatorHeader;
@@ -0,0 +1,42 @@
1
+ import type { Observable } from 'rxjs';
2
+ import type { FetchRequest } from '../client';
3
+
4
+ export type ProcessOperator<T, R = T> = (request: T) => R | void | Promise<R | void>;
5
+
6
+ /**
7
+ * Container for sync/async operators.
8
+ * Pipes each operator sequential
9
+ */
10
+ export interface IProcessOperators<T> {
11
+ /**
12
+ * Add a new operator (throw error if already defined)
13
+ */
14
+ add(key: string, operator: ProcessOperator<T>): IProcessOperators<T>;
15
+
16
+ /**
17
+ * Add or sets a operator
18
+ */
19
+ set(key: string, operator: ProcessOperator<T>): IProcessOperators<T>;
20
+
21
+ /**
22
+ * Get a operator, will return undefined on invalid key.
23
+ */
24
+ get(key: string): ProcessOperator<T>;
25
+
26
+ /**
27
+ * Process registered processors.
28
+ */
29
+ process(request: T): Observable<T>;
30
+ }
31
+
32
+ export interface IHttpRequestHandler<T extends FetchRequest = FetchRequest>
33
+ extends IProcessOperators<T> {
34
+ /**
35
+ * Set header that will apply on all requests done by consumer @see {HttpClient}
36
+ * @param key - name of header
37
+ * @param value - header value
38
+ */
39
+ setHeader(key: string, value: string): IHttpRequestHandler<T>;
40
+ }
41
+
42
+ export interface IHttpResponseHandler<T = Response> extends IProcessOperators<T> {}
@@ -0,0 +1,19 @@
1
+ export const blobSelector = <TResponse extends Response = Response>(
2
+ response: TResponse,
3
+ ): Promise<Blob> => {
4
+ if (!response.ok) {
5
+ throw new Error('network response was not OK');
6
+ }
7
+ //Status code 204 is no content
8
+ if (response.status === 204) {
9
+ throw new Error('no content');
10
+ }
11
+
12
+ try {
13
+ return response.blob();
14
+ } catch (err) {
15
+ throw Error('failed to parse response');
16
+ }
17
+ };
18
+
19
+ export default blobSelector;
@@ -0,0 +1,2 @@
1
+ export { jsonSelector } from './json-selector';
2
+ export { blobSelector } from './blob-selector';
@@ -0,0 +1,19 @@
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
+ //Status code 204 is no content
8
+ if (response.status === 204) {
9
+ return Promise.resolve() as Promise<TType>;
10
+ }
11
+
12
+ try {
13
+ return response.json();
14
+ } catch (err) {
15
+ throw Error('failed to parse response');
16
+ }
17
+ };
18
+
19
+ export default jsonSelector;
package/src/module.ts ADDED
@@ -0,0 +1,80 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { HttpClientMsal } from './lib/client';
3
+ import { IHttpClientConfigurator, HttpClientConfigurator, HttpClientOptions } from './configurator';
4
+ import { IHttpClientProvider, HttpClientProvider } from './provider';
5
+
6
+ import type {
7
+ Module,
8
+ ModuleConfigType,
9
+ IModuleConfigurator,
10
+ } from '@equinor/fusion-framework-module';
11
+
12
+ import { MsalModule } from '@equinor/fusion-framework-module-msal';
13
+
14
+ export type HttpModule = Module<'http', IHttpClientProvider, IHttpClientConfigurator>;
15
+
16
+ export type HttpMsalModule = Module<
17
+ 'http',
18
+ IHttpClientProvider<HttpClientMsal>,
19
+ IHttpClientConfigurator<HttpClientMsal>,
20
+ [MsalModule]
21
+ >;
22
+
23
+ /**
24
+ * Configure http-client
25
+ */
26
+ export const module: HttpMsalModule = {
27
+ name: 'http',
28
+ configure: () => new HttpClientConfigurator(HttpClientMsal),
29
+ initialize: async ({
30
+ config,
31
+ hasModule,
32
+ requireInstance,
33
+ }): Promise<HttpClientProvider<HttpClientMsal>> => {
34
+ const httpProvider = new HttpClientProvider(config);
35
+ if (hasModule('auth')) {
36
+ const authProvider = await requireInstance('auth');
37
+ httpProvider.defaultHttpRequestHandler.set('MSAL', async (request) => {
38
+ const { scopes = [] } = request;
39
+ if (scopes.length) {
40
+ /** TODO should be try catch, check caller for handling */
41
+ const token = await authProvider.acquireToken({
42
+ scopes,
43
+ });
44
+ if (token) {
45
+ const headers = new Headers(request.headers);
46
+ headers.set('Authorization', `Bearer ${token.accessToken}`);
47
+ return { ...request, headers };
48
+ }
49
+ }
50
+ });
51
+ }
52
+ return httpProvider;
53
+ },
54
+ };
55
+
56
+ export const configureHttp = <TRef = unknown>(
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ configure: (config: ModuleConfigType<HttpMsalModule>, ref?: TRef) => void,
59
+ ): IModuleConfigurator<HttpMsalModule, TRef> => ({
60
+ module,
61
+ configure,
62
+ });
63
+
64
+ export const configureHttpClient = <TRef = unknown>(
65
+ name: string,
66
+ args: HttpClientOptions<HttpClientMsal>,
67
+ ): IModuleConfigurator<HttpMsalModule, TRef> => ({
68
+ module,
69
+ configure: (config: ModuleConfigType<HttpMsalModule>) => {
70
+ config.configureClient(name, args);
71
+ },
72
+ });
73
+
74
+ declare module '@equinor/fusion-framework-module' {
75
+ interface Modules {
76
+ http: HttpMsalModule;
77
+ }
78
+ }
79
+
80
+ export default module;
@@ -0,0 +1,98 @@
1
+ import { HttpClient } from './lib/client';
2
+ import {
3
+ HttpClientOptions,
4
+ HttpClientRequestInitType,
5
+ IHttpClientConfigurator,
6
+ } from './configurator';
7
+
8
+ import type { IHttpRequestHandler } from './lib/operators';
9
+ import type { IHttpClient } from './lib/client';
10
+
11
+ export class ClientNotFoundException extends Error {
12
+ constructor(message: string) {
13
+ super(message);
14
+ }
15
+ }
16
+
17
+ export interface IHttpClientProvider<TClient extends IHttpClient = IHttpClient> {
18
+ readonly defaultHttpRequestHandler: IHttpRequestHandler<HttpClientRequestInitType<TClient>>;
19
+ /** check if a client is configured */
20
+ hasClient(key: string): boolean;
21
+ /** create a new http client */
22
+ createClient(key: string): TClient;
23
+ createClient(key: HttpClientOptions<TClient>): TClient;
24
+ /**
25
+ * Class cast creation of custom client
26
+ * @example
27
+ * ```ts
28
+ * config.http.configureClient('foobar', (client) => {
29
+ * client.ctor = MyClient;
30
+ * client.uri = 'https://foobar.com';
31
+ * });
32
+ */
33
+ createCustomClient<T extends HttpClient>(key: string): T;
34
+ }
35
+
36
+ const isURL = (url: string) => {
37
+ const pattern = new RegExp(
38
+ '^(https?:\\/\\/)?' + // protocol
39
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name
40
+ '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
41
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
42
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
43
+ '(\\#[-a-z\\d_]*)?$',
44
+ 'i',
45
+ ); // fragment locator
46
+ return pattern.test(url);
47
+ };
48
+
49
+ export class HttpClientProvider<TClient extends IHttpClient = IHttpClient>
50
+ implements IHttpClientProvider<TClient>
51
+ {
52
+ get defaultHttpRequestHandler(): IHttpRequestHandler<HttpClientRequestInitType<TClient>> {
53
+ return this.config.defaultHttpRequestHandler;
54
+ }
55
+
56
+ constructor(protected config: IHttpClientConfigurator<TClient>) {}
57
+
58
+ public hasClient(key: string): boolean {
59
+ return Object.keys(this.config.clients).includes(key);
60
+ }
61
+
62
+ public createClient(keyOrConfig: string | HttpClientOptions<TClient>): TClient {
63
+ const config = this._resolveConfig(keyOrConfig);
64
+ const {
65
+ baseUri,
66
+ defaultScopes = [],
67
+ onCreate,
68
+ ctor = this.config.defaultHttpClientCtor,
69
+ requestHandler = this.defaultHttpRequestHandler,
70
+ } = config as HttpClientOptions<TClient>;
71
+ const options = { requestHandler };
72
+ const instance = new ctor(baseUri || '', options) as TClient;
73
+ Object.assign(instance, { defaultScopes });
74
+ onCreate && onCreate(instance as TClient);
75
+ return instance as TClient;
76
+ }
77
+
78
+ public createCustomClient<T extends HttpClient>(key: string): T {
79
+ return this.createClient(key) as unknown as T;
80
+ }
81
+
82
+ protected _resolveConfig(
83
+ keyOrConfig: string | HttpClientOptions<TClient>,
84
+ ): HttpClientOptions<TClient> {
85
+ if (typeof keyOrConfig === 'string') {
86
+ const config = this.config.clients[keyOrConfig];
87
+ if (!config && isURL(keyOrConfig)) {
88
+ return { baseUri: keyOrConfig };
89
+ } else if (!config) {
90
+ throw new ClientNotFoundException(
91
+ `No registered http client for key [${keyOrConfig}]`,
92
+ );
93
+ }
94
+ return config;
95
+ }
96
+ return keyOrConfig as HttpClientOptions<TClient>;
97
+ }
98
+ }
package/src/version.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Generated by genversion.
2
+ export const version = '5.0.6';
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist/esm",
5
+ "rootDir": "src",
6
+ "declarationDir": "./dist/types",
7
+
8
+ },
9
+ "references": [
10
+ {
11
+ "path": "../module"
12
+ },
13
+ {
14
+ "path": "../msal"
15
+ }
16
+ ],
17
+ "include": [
18
+ "src/**/*",
19
+ ],
20
+ "exclude": [
21
+ "node_modules",
22
+ "lib"
23
+ ]
24
+ }