@equinor/fusion-framework-module-widget 0.0.2
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 +8 -0
- package/LICENSE +21 -0
- package/dist/esm/WidgetModuleConfigBuilder.js +42 -0
- package/dist/esm/WidgetModuleConfigBuilder.js.map +1 -0
- package/dist/esm/WidgetModuleConfigurator.js +98 -0
- package/dist/esm/WidgetModuleConfigurator.js.map +1 -0
- package/dist/esm/WidgetModuleProvider.js +52 -0
- package/dist/esm/WidgetModuleProvider.js.map +1 -0
- package/dist/esm/enable-widget-module.js +10 -0
- package/dist/esm/enable-widget-module.js.map +1 -0
- package/dist/esm/errors.js +22 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/module.js +26 -0
- package/dist/esm/module.js.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/WidgetModuleConfigBuilder.d.ts +19 -0
- package/dist/types/WidgetModuleConfigurator.d.ts +23 -0
- package/dist/types/WidgetModuleProvider.d.ts +21 -0
- package/dist/types/enable-widget-module.d.ts +3 -0
- package/dist/types/errors.d.ts +11 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/module.d.ts +13 -0
- package/dist/types/types.d.ts +40 -0
- package/package.json +44 -0
- package/src/WidgetModuleConfigBuilder.ts +67 -0
- package/src/WidgetModuleConfigurator.ts +128 -0
- package/src/WidgetModuleProvider.ts +76 -0
- package/src/enable-widget-module.ts +21 -0
- package/src/errors.ts +30 -0
- package/src/index.ts +15 -0
- package/src/module.ts +35 -0
- package/src/types.ts +51 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ModuleInitializerArgs } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { HttpModule, IHttpClient } from '@equinor/fusion-framework-module-http';
|
|
3
|
+
import { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery';
|
|
4
|
+
import { QueryCtorOptions } from '@equinor/fusion-query';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
WidgetModuleConfigBuilder,
|
|
8
|
+
WidgetModuleConfigBuilderCallback,
|
|
9
|
+
WidgetEndpointBuilder,
|
|
10
|
+
} from './WidgetModuleConfigBuilder';
|
|
11
|
+
|
|
12
|
+
import { moduleKey } from './module';
|
|
13
|
+
|
|
14
|
+
import type { GetWidgetParameters, WidgetManifest } from './types';
|
|
15
|
+
|
|
16
|
+
export interface WidgetModuleConfig {
|
|
17
|
+
client: {
|
|
18
|
+
// getWidgetManifest: QueryCtorOptions<WidgetManifest, { widgetKey: string }>;
|
|
19
|
+
getWidget: QueryCtorOptions<WidgetManifest, GetWidgetParameters>;
|
|
20
|
+
};
|
|
21
|
+
endpointBuilder: WidgetEndpointBuilder;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface IWidgetModuleConfigurator {
|
|
25
|
+
addConfigBuilder: (init: WidgetModuleConfigBuilderCallback) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultEndpointBuilder: WidgetEndpointBuilder = (params) => {
|
|
29
|
+
const { widgetKey, args } = params;
|
|
30
|
+
const { type, value } = args ?? {};
|
|
31
|
+
switch (type) {
|
|
32
|
+
case 'tag':
|
|
33
|
+
case 'version':
|
|
34
|
+
return `/widgets/${widgetKey}/versions/${value}`;
|
|
35
|
+
default:
|
|
36
|
+
return `/widgets/${widgetKey}`;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const widgetSelector =
|
|
41
|
+
(args: { apiVersion: string; uri: string }) =>
|
|
42
|
+
async (response: Response): Promise<WidgetManifest> => {
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
...data,
|
|
47
|
+
importBundle: async () =>
|
|
48
|
+
import(
|
|
49
|
+
new URL(
|
|
50
|
+
`${data.assetPath}/${data.entryPoint}?api-version=${args.apiVersion}`,
|
|
51
|
+
args.uri
|
|
52
|
+
).toString()
|
|
53
|
+
),
|
|
54
|
+
} as WidgetManifest;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export class WidgetModuleConfigurator implements IWidgetModuleConfigurator {
|
|
58
|
+
defaultExpireTime = 1 * 60 * 1000;
|
|
59
|
+
|
|
60
|
+
#configBuilders: Array<WidgetModuleConfigBuilderCallback> = [];
|
|
61
|
+
#apiVersion = '1.0';
|
|
62
|
+
|
|
63
|
+
addConfigBuilder(init: WidgetModuleConfigBuilderCallback): void {
|
|
64
|
+
this.#configBuilders.push(init);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
constructor(apiVersion?: string) {
|
|
68
|
+
if (apiVersion) {
|
|
69
|
+
this.#apiVersion = apiVersion;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* WARNING: this function will be remove in future
|
|
75
|
+
*/
|
|
76
|
+
protected async _createHttpClient(
|
|
77
|
+
init: ModuleInitializerArgs<IWidgetModuleConfigurator, [HttpModule, ServiceDiscoveryModule]>
|
|
78
|
+
): Promise<IHttpClient> {
|
|
79
|
+
const http = await init.requireInstance('http');
|
|
80
|
+
/** check if the http provider has configure a client */
|
|
81
|
+
if (http.hasClient(moduleKey)) {
|
|
82
|
+
return http.createClient(moduleKey);
|
|
83
|
+
} else {
|
|
84
|
+
/** load service discovery module */
|
|
85
|
+
const serviceDiscovery = await init.requireInstance('serviceDiscovery');
|
|
86
|
+
|
|
87
|
+
const discoClient = await serviceDiscovery.createClient('apps');
|
|
88
|
+
|
|
89
|
+
return discoClient;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public async createConfig(
|
|
94
|
+
init: ModuleInitializerArgs<IWidgetModuleConfigurator, [HttpModule, ServiceDiscoveryModule]>
|
|
95
|
+
): Promise<WidgetModuleConfig> {
|
|
96
|
+
const config = await this.#configBuilders.reduce(async (cur, cb) => {
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
const builder = new WidgetModuleConfigBuilder(init, await cur);
|
|
99
|
+
await Promise.resolve(cb(builder));
|
|
100
|
+
return Object.assign(cur, builder.config);
|
|
101
|
+
}, Promise.resolve({} as Partial<WidgetModuleConfig>));
|
|
102
|
+
|
|
103
|
+
const { endpointBuilder = defaultEndpointBuilder } = config;
|
|
104
|
+
|
|
105
|
+
// TODO - make less lazy
|
|
106
|
+
config.client ??= await (async (): Promise<WidgetModuleConfig['client']> => {
|
|
107
|
+
const httpClient = await this._createHttpClient(init);
|
|
108
|
+
httpClient.requestHandler.setHeader('api-version', this.#apiVersion);
|
|
109
|
+
return {
|
|
110
|
+
getWidget: {
|
|
111
|
+
client: {
|
|
112
|
+
fn: (args) =>
|
|
113
|
+
httpClient.json$(endpointBuilder(args), {
|
|
114
|
+
selector: widgetSelector({
|
|
115
|
+
apiVersion: this.#apiVersion,
|
|
116
|
+
uri: httpClient.uri,
|
|
117
|
+
}),
|
|
118
|
+
}),
|
|
119
|
+
},
|
|
120
|
+
key: (args) => JSON.stringify(args),
|
|
121
|
+
expire: this.defaultExpireTime,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
})();
|
|
125
|
+
|
|
126
|
+
return config as WidgetModuleConfig;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { catchError, Observable, Subscription } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
import { ModuleType } from '@equinor/fusion-framework-module';
|
|
4
|
+
import { HttpResponseError } from '@equinor/fusion-framework-module-http';
|
|
5
|
+
import { EventModule } from '@equinor/fusion-framework-module-event';
|
|
6
|
+
|
|
7
|
+
import { Query } from '@equinor/fusion-query';
|
|
8
|
+
|
|
9
|
+
import type { GetWidgetParameters, WidgetManifest } from './types';
|
|
10
|
+
|
|
11
|
+
import { WidgetModuleConfig } from './WidgetModuleConfigurator';
|
|
12
|
+
import { GetWidgetError } from './errors';
|
|
13
|
+
|
|
14
|
+
export interface IWidgetModuleProvider {
|
|
15
|
+
getWidget(
|
|
16
|
+
widgetKey: GetWidgetParameters['widgetKey'],
|
|
17
|
+
args: GetWidgetParameters['args']
|
|
18
|
+
): Observable<WidgetManifest>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class WidgetModuleProvider implements IWidgetModuleProvider {
|
|
22
|
+
#widgetClient: Query<WidgetManifest, GetWidgetParameters>;
|
|
23
|
+
#subscription = new Subscription();
|
|
24
|
+
|
|
25
|
+
constructor(args: { config: WidgetModuleConfig; event?: ModuleType<EventModule> }) {
|
|
26
|
+
const { config } = args;
|
|
27
|
+
|
|
28
|
+
this.#widgetClient = new Query(config.client.getWidget);
|
|
29
|
+
|
|
30
|
+
this.#subscription.add(() => this.#widgetClient.complete());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getWidget(name: string): Observable<WidgetManifest> {
|
|
34
|
+
return this._getWidget(name);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public getWidgetByVersion(name: string, version: string): Observable<WidgetManifest> {
|
|
38
|
+
return this._getWidget(name, { type: 'version', value: version });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public getWidgetByTag(name: string, tag: string): Observable<WidgetManifest> {
|
|
42
|
+
return this._getWidget(name, { type: 'tag', value: tag });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* fetch configuration for a widget
|
|
47
|
+
* @param widgetKey - widget key
|
|
48
|
+
* @param version - version of widget to use
|
|
49
|
+
*/
|
|
50
|
+
protected _getWidget(
|
|
51
|
+
widgetKey: GetWidgetParameters['widgetKey'],
|
|
52
|
+
args?: GetWidgetParameters['args']
|
|
53
|
+
): Observable<WidgetManifest> {
|
|
54
|
+
return Query.extractQueryValue(
|
|
55
|
+
this.#widgetClient.query({ widgetKey, args }).pipe(
|
|
56
|
+
catchError((err) => {
|
|
57
|
+
/** extract cause, since error will be a `QueryError` */
|
|
58
|
+
const { cause } = err;
|
|
59
|
+
if (cause instanceof GetWidgetError) {
|
|
60
|
+
throw cause;
|
|
61
|
+
}
|
|
62
|
+
if (cause instanceof HttpResponseError) {
|
|
63
|
+
throw GetWidgetError.fromHttpResponse(cause.response, { cause });
|
|
64
|
+
}
|
|
65
|
+
throw new GetWidgetError('unknown', 'failed to load config', { cause });
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public dispose() {
|
|
72
|
+
this.#subscription.unsubscribe();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default WidgetModuleProvider;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IModulesConfigurator } from '@equinor/fusion-framework-module';
|
|
2
|
+
import type { WidgetModuleConfigBuilderCallback } from './WidgetModuleConfigBuilder';
|
|
3
|
+
|
|
4
|
+
import { module } from './module';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Method for enabling the widget module
|
|
8
|
+
* @param configurator - configuration object
|
|
9
|
+
*/
|
|
10
|
+
export const enableWidgetModule = (
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
configurator: IModulesConfigurator<any, any>,
|
|
13
|
+
builder?: WidgetModuleConfigBuilderCallback
|
|
14
|
+
): void => {
|
|
15
|
+
configurator.addConfig({
|
|
16
|
+
module,
|
|
17
|
+
configure: (widgetConfigurator) => {
|
|
18
|
+
builder && widgetConfigurator.addConfigBuilder(builder);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
};
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type WidgetErrorType = 'not_found' | 'unauthorized' | 'unknown';
|
|
2
|
+
|
|
3
|
+
export class GetWidgetError extends Error {
|
|
4
|
+
static fromHttpResponse(response: Response, options?: ErrorOptions) {
|
|
5
|
+
switch (response.status) {
|
|
6
|
+
case 401:
|
|
7
|
+
return new GetWidgetError(
|
|
8
|
+
'unauthorized',
|
|
9
|
+
'failed to load widget manifest, request not authorized',
|
|
10
|
+
options
|
|
11
|
+
);
|
|
12
|
+
case 404:
|
|
13
|
+
return new GetWidgetError('not_found', 'widget manifest not found', options);
|
|
14
|
+
}
|
|
15
|
+
return new GetWidgetError(
|
|
16
|
+
'unknown',
|
|
17
|
+
`failed to load widget manifest, status code ${response.status}`,
|
|
18
|
+
options
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
constructor(public readonly type: WidgetErrorType, message?: string, options?: ErrorOptions) {
|
|
22
|
+
super(message, options);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class WidgetScriptModuleError extends Error {
|
|
27
|
+
constructor(public readonly type: WidgetErrorType, message?: string, options?: ErrorOptions) {
|
|
28
|
+
super(message, options);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
WidgetModuleConfigurator,
|
|
3
|
+
IWidgetModuleConfigurator,
|
|
4
|
+
WidgetModuleConfig,
|
|
5
|
+
} from './WidgetModuleConfigurator';
|
|
6
|
+
|
|
7
|
+
export { WidgetModuleProvider } from './WidgetModuleProvider';
|
|
8
|
+
|
|
9
|
+
export type { IWidgetModuleProvider } from './WidgetModuleProvider';
|
|
10
|
+
|
|
11
|
+
export * from './types';
|
|
12
|
+
|
|
13
|
+
export { enableWidgetModule } from './enable-widget-module';
|
|
14
|
+
|
|
15
|
+
export { default, WidgetModule, module, moduleKey } from './module';
|
package/src/module.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Module } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { ModuleDeps } from './types';
|
|
3
|
+
|
|
4
|
+
import { IWidgetModuleConfigurator, WidgetModuleConfigurator } from './WidgetModuleConfigurator';
|
|
5
|
+
import { IWidgetModuleProvider, WidgetModuleProvider } from './WidgetModuleProvider';
|
|
6
|
+
|
|
7
|
+
export const moduleKey = 'widget';
|
|
8
|
+
|
|
9
|
+
export type WidgetModule = Module<
|
|
10
|
+
typeof moduleKey,
|
|
11
|
+
IWidgetModuleProvider,
|
|
12
|
+
IWidgetModuleConfigurator,
|
|
13
|
+
ModuleDeps
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
export const module: WidgetModule = {
|
|
17
|
+
name: moduleKey,
|
|
18
|
+
configure: () => new WidgetModuleConfigurator('1.0-preview'),
|
|
19
|
+
initialize: async (args) => {
|
|
20
|
+
const config = await (args.config as WidgetModuleConfigurator).createConfig(args);
|
|
21
|
+
const event = await args.requireInstance('event').catch(() => undefined);
|
|
22
|
+
return new WidgetModuleProvider({ config, event });
|
|
23
|
+
},
|
|
24
|
+
dispose: (args) => {
|
|
25
|
+
(args.instance as unknown as WidgetModuleProvider).dispose();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default module;
|
|
30
|
+
|
|
31
|
+
declare module '@equinor/fusion-framework-module' {
|
|
32
|
+
interface Modules {
|
|
33
|
+
widget: WidgetModule;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AnyModule, CombinedModules, ModulesInstance } from '@equinor/fusion-framework-module';
|
|
2
|
+
import type { EventModule } from '@equinor/fusion-framework-module-event';
|
|
3
|
+
import type { HttpModule } from '@equinor/fusion-framework-module-http';
|
|
4
|
+
import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery';
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
type Fusion = any;
|
|
8
|
+
|
|
9
|
+
export type WidgetEnv = {
|
|
10
|
+
config?: WidgetManifest;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// TODO: change to module-services when new app service is created
|
|
14
|
+
export type ModuleDeps = [HttpModule, ServiceDiscoveryModule, EventModule];
|
|
15
|
+
|
|
16
|
+
export type GetWidgetParameters = {
|
|
17
|
+
widgetKey: string;
|
|
18
|
+
args?: { type: 'version' | 'tag'; value: string };
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type WidgetManifest = {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
description: string;
|
|
26
|
+
maintainers?: string[];
|
|
27
|
+
entryPoint: string;
|
|
28
|
+
assetPath: string;
|
|
29
|
+
// TODO move to @equinor/fusion-widget
|
|
30
|
+
importBundle: () => Promise<WidgetScriptModule>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type WidgetModules<TModules extends Array<AnyModule> | unknown = unknown> = CombinedModules<
|
|
34
|
+
TModules,
|
|
35
|
+
[EventModule, ServiceDiscoveryModule]
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
export type WidgetRenderArgs<TFusion extends Fusion = Fusion, TEnv = WidgetEnv> = {
|
|
39
|
+
fusion: TFusion;
|
|
40
|
+
env: TEnv;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type WidgetScriptModule<
|
|
44
|
+
TProps extends Record<PropertyKey, unknown> = Record<PropertyKey, unknown>
|
|
45
|
+
> = {
|
|
46
|
+
default: (el: HTMLElement, args: WidgetRenderArgs, props?: TProps) => VoidFunction;
|
|
47
|
+
renderWidget: (el: HTMLElement, args: WidgetRenderArgs, props?: TProps) => VoidFunction;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type WidgetModulesInstance<TModules extends Array<AnyModule> | unknown = unknown> =
|
|
51
|
+
ModulesInstance<WidgetModules<TModules>>;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist/esm",
|
|
5
|
+
"rootDir": "src",
|
|
6
|
+
"declarationDir": "./dist/types",
|
|
7
|
+
"baseUrl": "src",
|
|
8
|
+
},
|
|
9
|
+
"references": [
|
|
10
|
+
{
|
|
11
|
+
"path": "../../utils/query"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"path": "../module"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"path": "../http"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "../event"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"path": "../service-discovery"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"include": [
|
|
27
|
+
"src/**/*",
|
|
28
|
+
],
|
|
29
|
+
"exclude": [
|
|
30
|
+
"node_modules",
|
|
31
|
+
"lib"
|
|
32
|
+
]
|
|
33
|
+
}
|