@equinor/fusion-framework-module-context 2.0.14 → 3.0.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 +10 -0
- package/dist/esm/ContextConfigBuilder.js +18 -0
- package/dist/esm/ContextConfigBuilder.js.map +1 -1
- package/dist/esm/ContextProvider.js +129 -25
- package/dist/esm/ContextProvider.js.map +1 -1
- package/dist/esm/configurator.js +10 -1
- package/dist/esm/configurator.js.map +1 -1
- package/dist/esm/selectors.js +4 -0
- package/dist/esm/selectors.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ContextConfigBuilder.d.ts +4 -1
- package/dist/types/ContextProvider.d.ts +25 -1
- package/dist/types/configurator.d.ts +5 -1
- package/dist/types/selectors.d.ts +1 -0
- package/dist/types/types.d.ts +6 -0
- package/package.json +6 -6
- package/src/ContextConfigBuilder.ts +26 -1
- package/src/ContextProvider.ts +195 -25
- package/src/configurator.ts +33 -2
- package/src/selectors.ts +6 -0
- package/src/types.ts +2 -0
|
@@ -2,7 +2,7 @@ import type { AnyModule, ModuleInitializerArgs, Modules, ModuleType } from '@equ
|
|
|
2
2
|
import type { QueryCtorOptions, QueryFn } from '@equinor/fusion-query';
|
|
3
3
|
import type { GetContextParameters } from './client/ContextClient';
|
|
4
4
|
import type { ContextModuleConfig, ContextModuleConfigurator, IContextModuleConfigurator } from './configurator';
|
|
5
|
-
import type { ContextItem, QueryContextParameters } from './types';
|
|
5
|
+
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
6
6
|
export type ContextConfigBuilderCallback = <TDeps extends Array<AnyModule> = []>(builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>) => void | Promise<void>;
|
|
7
7
|
export declare class ContextConfigBuilder<TModules extends Array<AnyModule> = [], TInit extends ModuleInitializerArgs<any, any> = ModuleInitializerArgs<ContextModuleConfigurator, TModules>> {
|
|
8
8
|
#private;
|
|
@@ -13,8 +13,11 @@ export declare class ContextConfigBuilder<TModules extends Array<AnyModule> = []
|
|
|
13
13
|
setContextType(type: ContextModuleConfig['contextType']): void;
|
|
14
14
|
setContextFilter(filter: ContextModuleConfig['contextFilter']): void;
|
|
15
15
|
setContextParameterFn(fn: ContextModuleConfig['contextParameterFn']): void;
|
|
16
|
+
setValidateContext(fn: ContextModuleConfig['validateContext']): void;
|
|
17
|
+
setResolveContext(fn: ContextModuleConfig['resolveContext']): void;
|
|
16
18
|
setContextClient(client: {
|
|
17
19
|
get: QueryFn<ContextItem, GetContextParameters> | QueryCtorOptions<ContextItem, GetContextParameters>;
|
|
18
20
|
query: QueryFn<ContextItem[], QueryContextParameters> | QueryCtorOptions<ContextItem[], QueryContextParameters>;
|
|
21
|
+
related?: QueryFn<ContextItem[], RelatedContextParameters> | QueryCtorOptions<ContextItem[], RelatedContextParameters>;
|
|
19
22
|
}, expire?: number): void;
|
|
20
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
2
|
import { ContextModuleConfig } from './configurator';
|
|
3
3
|
import { ContextClient } from './client/ContextClient';
|
|
4
|
-
import { ContextItem, QueryContextParameters } from './types';
|
|
4
|
+
import { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
5
5
|
import { ModuleType } from '@equinor/fusion-framework-module';
|
|
6
6
|
import { EventModule, FrameworkEvent, FrameworkEventInit } from '@equinor/fusion-framework-module-event';
|
|
7
7
|
import Query from '@equinor/fusion-query';
|
|
@@ -12,6 +12,11 @@ export interface IContextProvider {
|
|
|
12
12
|
currentContext: ContextItem | undefined;
|
|
13
13
|
queryContext(search: string): Observable<Array<ContextItem>>;
|
|
14
14
|
queryContextAsync(search: string): Promise<Array<ContextItem>>;
|
|
15
|
+
validateContext(item: ContextItem<Record<string, unknown>>): boolean;
|
|
16
|
+
resolveContext: (current: ContextItem) => Observable<ContextItem>;
|
|
17
|
+
resolveContextAsync: (current: ContextItem) => Promise<ContextItem>;
|
|
18
|
+
relatedContexts: (args: RelatedContextParameters) => Observable<Array<ContextItem<Record<string, unknown>>>>;
|
|
19
|
+
relatedContextsAsync: (args: RelatedContextParameters) => Promise<Array<ContextItem<Record<string, unknown>>>>;
|
|
15
20
|
clearCurrentContext: VoidFunction;
|
|
16
21
|
}
|
|
17
22
|
export declare class ContextProvider implements IContextProvider {
|
|
@@ -26,8 +31,17 @@ export declare class ContextProvider implements IContextProvider {
|
|
|
26
31
|
event?: ModuleType<EventModule>;
|
|
27
32
|
parentContext?: IContextProvider;
|
|
28
33
|
});
|
|
34
|
+
setCurrentContext(context: ContextItem<Record<string, unknown>>, opt?: {
|
|
35
|
+
validate?: boolean;
|
|
36
|
+
resolve?: boolean;
|
|
37
|
+
}): Promise<ContextItem<Record<string, unknown>>>;
|
|
29
38
|
queryContext(search: string): Observable<Array<ContextItem>>;
|
|
30
39
|
queryContextAsync(search: string): Promise<Array<ContextItem>>;
|
|
40
|
+
validateContext(item: ContextItem<Record<string, unknown>>): boolean;
|
|
41
|
+
resolveContext(item: ContextItem<Record<string, unknown>>): Observable<ContextItem<Record<string, unknown>>>;
|
|
42
|
+
resolveContextAsync(item: ContextItem<Record<string, unknown>>): Promise<ContextItem<Record<string, unknown>>>;
|
|
43
|
+
relatedContexts(args: RelatedContextParameters): Observable<Array<ContextItem<Record<string, unknown>>>>;
|
|
44
|
+
relatedContextsAsync(args: RelatedContextParameters): Promise<Array<ContextItem<Record<string, unknown>>>>;
|
|
31
45
|
clearCurrentContext(): void;
|
|
32
46
|
dispose(): void;
|
|
33
47
|
}
|
|
@@ -41,5 +55,15 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
41
55
|
next: ContextItem | undefined;
|
|
42
56
|
previous: ContextItem | undefined;
|
|
43
57
|
}, IContextProvider>>;
|
|
58
|
+
onParentContextChanged: FrameworkEvent<FrameworkEventInit<{
|
|
59
|
+
context: ContextItem | undefined;
|
|
60
|
+
}, IContextProvider>>;
|
|
61
|
+
onSetContextResolve: FrameworkEvent<FrameworkEventInit<{
|
|
62
|
+
context: ContextItem;
|
|
63
|
+
}, IContextProvider>>;
|
|
64
|
+
onSetContextResolved: FrameworkEvent<FrameworkEventInit<{
|
|
65
|
+
input: ContextItem;
|
|
66
|
+
result?: ContextItem | null;
|
|
67
|
+
}, IContextProvider>>;
|
|
44
68
|
}
|
|
45
69
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { ModuleInitializerArgs } from '@equinor/fusion-framework-module';
|
|
2
2
|
import { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
|
|
3
3
|
import { QueryCtorOptions } from '@equinor/fusion-query';
|
|
4
|
-
import { ContextFilterFn, ContextItem, QueryContextParameters } from './types';
|
|
4
|
+
import { ContextFilterFn, ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
5
5
|
import { GetContextParameters } from './client/ContextClient';
|
|
6
6
|
import { ContextConfigBuilderCallback } from './ContextConfigBuilder';
|
|
7
|
+
import { IContextProvider } from 'ContextProvider';
|
|
7
8
|
export interface ContextModuleConfig {
|
|
8
9
|
client: {
|
|
9
10
|
get: QueryCtorOptions<ContextItem, GetContextParameters>;
|
|
10
11
|
query: QueryCtorOptions<ContextItem[], QueryContextParameters>;
|
|
12
|
+
related?: QueryCtorOptions<ContextItem[], RelatedContextParameters>;
|
|
11
13
|
};
|
|
12
14
|
contextType?: string[];
|
|
13
15
|
contextFilter?: ContextFilterFn;
|
|
@@ -15,6 +17,8 @@ export interface ContextModuleConfig {
|
|
|
15
17
|
search: string;
|
|
16
18
|
type: ContextModuleConfig['contextType'];
|
|
17
19
|
}) => string | QueryContextParameters;
|
|
20
|
+
resolveContext?: (this: IContextProvider, item: ContextItem | null) => ReturnType<IContextProvider['resolveContext']>;
|
|
21
|
+
validateContext?: (this: IContextProvider, item: ContextItem | null) => ReturnType<IContextProvider['validateContext']>;
|
|
18
22
|
}
|
|
19
23
|
export interface IContextModuleConfigurator {
|
|
20
24
|
addConfigBuilder: (init: ContextConfigBuilderCallback) => void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { ContextItem } from './types';
|
|
2
2
|
export declare const getContextSelector: (response: Response) => Promise<ContextItem>;
|
|
3
3
|
export declare const queryContextSelector: (response: Response) => Promise<ContextItem[]>;
|
|
4
|
+
export declare const relatedContextSelector: (response: Response) => Promise<ContextItem[]>;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -23,4 +23,10 @@ export type QueryContextParameters = {
|
|
|
23
23
|
externalId?: string;
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
|
+
export type RelatedContextParameters = {
|
|
27
|
+
item: ContextItem;
|
|
28
|
+
filter?: {
|
|
29
|
+
type?: string[];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
26
32
|
export type ContextFilterFn = (items: ContextItem[]) => ContextItem[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-context",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -31,18 +31,18 @@
|
|
|
31
31
|
"directory": "packages/modules/context"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@equinor/fusion-query": "^
|
|
34
|
+
"@equinor/fusion-query": "^3.0.0",
|
|
35
35
|
"fast-deep-equal": "^3.1.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@equinor/fusion-framework-module": "^
|
|
39
|
-
"@equinor/fusion-framework-module-event": "^
|
|
40
|
-
"@equinor/fusion-framework-module-services": "^
|
|
38
|
+
"@equinor/fusion-framework-module": "^3.0.0",
|
|
39
|
+
"@equinor/fusion-framework-module-event": "^3.0.0",
|
|
40
|
+
"@equinor/fusion-framework-module-services": "^3.0.0",
|
|
41
41
|
"rxjs": "^7.5.7"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@equinor/fusion-framework-module": ">=1.2.5",
|
|
45
45
|
"rxjs": "^7.5.7"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "3462d21feac22290eec2dce3b00602da22a7a74d"
|
|
48
48
|
}
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
IContextModuleConfigurator,
|
|
16
16
|
} from './configurator';
|
|
17
17
|
|
|
18
|
-
import type { ContextItem, QueryContextParameters } from './types';
|
|
18
|
+
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
19
19
|
|
|
20
20
|
export type ContextConfigBuilderCallback = <TDeps extends Array<AnyModule> = []>(
|
|
21
21
|
builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>
|
|
@@ -57,6 +57,14 @@ export class ContextConfigBuilder<
|
|
|
57
57
|
this.config.contextParameterFn = fn;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
setValidateContext(fn: ContextModuleConfig['validateContext']) {
|
|
61
|
+
this.config.validateContext = fn;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setResolveContext(fn: ContextModuleConfig['resolveContext']) {
|
|
65
|
+
this.config.resolveContext = fn;
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
setContextClient(
|
|
61
69
|
client: {
|
|
62
70
|
get:
|
|
@@ -65,6 +73,9 @@ export class ContextConfigBuilder<
|
|
|
65
73
|
query:
|
|
66
74
|
| QueryFn<ContextItem[], QueryContextParameters>
|
|
67
75
|
| QueryCtorOptions<ContextItem[], QueryContextParameters>;
|
|
76
|
+
related?:
|
|
77
|
+
| QueryFn<ContextItem[], RelatedContextParameters>
|
|
78
|
+
| QueryCtorOptions<ContextItem[], RelatedContextParameters>;
|
|
68
79
|
},
|
|
69
80
|
expire = 1 * 60 * 1000
|
|
70
81
|
): void {
|
|
@@ -91,5 +102,19 @@ export class ContextConfigBuilder<
|
|
|
91
102
|
}
|
|
92
103
|
: client.query,
|
|
93
104
|
};
|
|
105
|
+
if (client.related) {
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
107
|
+
this.config.client!.related =
|
|
108
|
+
typeof client.related === 'function'
|
|
109
|
+
? {
|
|
110
|
+
// TODO - might cast to checksum
|
|
111
|
+
key: (args) => JSON.stringify(args),
|
|
112
|
+
client: {
|
|
113
|
+
fn: client.related,
|
|
114
|
+
},
|
|
115
|
+
expire,
|
|
116
|
+
}
|
|
117
|
+
: client.related;
|
|
118
|
+
}
|
|
94
119
|
}
|
|
95
120
|
}
|
package/src/ContextProvider.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { lastValueFrom, Observable, Subscription } from 'rxjs';
|
|
2
|
-
import { map, pairwise } from 'rxjs/operators';
|
|
1
|
+
import { lastValueFrom, Observable, Subscription, throwError } from 'rxjs';
|
|
2
|
+
import { filter, map, pairwise, switchMap } from 'rxjs/operators';
|
|
3
3
|
|
|
4
4
|
import { ContextModuleConfig } from './configurator';
|
|
5
5
|
|
|
6
6
|
import { ContextClient } from './client/ContextClient';
|
|
7
|
-
import { ContextItem, QueryContextParameters } from './types';
|
|
7
|
+
import { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
8
8
|
import { ModuleType } from '@equinor/fusion-framework-module';
|
|
9
9
|
import {
|
|
10
10
|
EventModule,
|
|
@@ -27,12 +27,22 @@ export interface IContextProvider {
|
|
|
27
27
|
currentContext: ContextItem | undefined;
|
|
28
28
|
queryContext(search: string): Observable<Array<ContextItem>>;
|
|
29
29
|
queryContextAsync(search: string): Promise<Array<ContextItem>>;
|
|
30
|
+
validateContext(item: ContextItem<Record<string, unknown>>): boolean;
|
|
31
|
+
resolveContext: (current: ContextItem) => Observable<ContextItem>;
|
|
32
|
+
resolveContextAsync: (current: ContextItem) => Promise<ContextItem>;
|
|
33
|
+
relatedContexts: (
|
|
34
|
+
args: RelatedContextParameters
|
|
35
|
+
) => Observable<Array<ContextItem<Record<string, unknown>>>>;
|
|
36
|
+
relatedContextsAsync: (
|
|
37
|
+
args: RelatedContextParameters
|
|
38
|
+
) => Promise<Array<ContextItem<Record<string, unknown>>>>;
|
|
30
39
|
clearCurrentContext: VoidFunction;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export class ContextProvider implements IContextProvider {
|
|
34
43
|
#contextClient: ContextClient;
|
|
35
44
|
#contextQuery: Query<Array<ContextItem>, QueryContextParameters>;
|
|
45
|
+
#contextRelated?: Query<Array<ContextItem>, RelatedContextParameters>;
|
|
36
46
|
|
|
37
47
|
#event?: ModuleType<EventModule>;
|
|
38
48
|
|
|
@@ -59,24 +69,7 @@ export class ContextProvider implements IContextProvider {
|
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
set currentContext(context: ContextItem | undefined) {
|
|
62
|
-
|
|
63
|
-
/** notify listeners that context is about to change */
|
|
64
|
-
this.#event
|
|
65
|
-
.dispatchEvent('onCurrentContextChange', {
|
|
66
|
-
source: this,
|
|
67
|
-
canBubble: true,
|
|
68
|
-
cancelable: true,
|
|
69
|
-
detail: { context },
|
|
70
|
-
})
|
|
71
|
-
.then((e) => {
|
|
72
|
-
/** check if setting context was prevented by listener */
|
|
73
|
-
if (!e.canceled) {
|
|
74
|
-
this.#contextClient.setCurrentContext(context);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
} else {
|
|
78
|
-
this.#contextClient.setCurrentContext(context);
|
|
79
|
-
}
|
|
72
|
+
context ? this.setCurrentContext(context) : this.clearCurrentContext();
|
|
80
73
|
}
|
|
81
74
|
|
|
82
75
|
constructor(args: {
|
|
@@ -86,11 +79,19 @@ export class ContextProvider implements IContextProvider {
|
|
|
86
79
|
}) {
|
|
87
80
|
const { config, event, parentContext } = args;
|
|
88
81
|
|
|
82
|
+
config.resolveContext && (this.resolveContext = config.resolveContext?.bind(this));
|
|
83
|
+
config.validateContext && (this.validateContext = config.validateContext?.bind(this));
|
|
84
|
+
|
|
89
85
|
this.#contextType = config.contextType;
|
|
90
86
|
this.#contextFilter = config.contextFilter;
|
|
91
87
|
|
|
92
88
|
this.#contextClient = new ContextClient(config.client.get);
|
|
93
89
|
this.#contextQuery = new Query(config.client.query);
|
|
90
|
+
|
|
91
|
+
if (config.client.related) {
|
|
92
|
+
this.#contextRelated = new Query(config.client.related);
|
|
93
|
+
}
|
|
94
|
+
|
|
94
95
|
this.#contextParameterFn =
|
|
95
96
|
config.contextParameterFn ??
|
|
96
97
|
((args: Parameters<Required<ContextModuleConfig>['contextParameterFn']>[0]) => ({
|
|
@@ -127,13 +128,104 @@ export class ContextProvider implements IContextProvider {
|
|
|
127
128
|
|
|
128
129
|
if (parentContext) {
|
|
129
130
|
this.#subscriptions.add(
|
|
130
|
-
parentContext.contextClient.currentContext
|
|
131
|
-
(
|
|
132
|
-
|
|
131
|
+
parentContext.contextClient.currentContext$
|
|
132
|
+
.pipe(
|
|
133
|
+
switchMap(async (next) => {
|
|
134
|
+
if (next) {
|
|
135
|
+
const onParentContextChanged = await this.#event?.dispatchEvent(
|
|
136
|
+
'onParentContextChanged',
|
|
137
|
+
{
|
|
138
|
+
source: this,
|
|
139
|
+
detail: next,
|
|
140
|
+
cancelable: true,
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
return { next, canceled: onParentContextChanged?.canceled };
|
|
144
|
+
}
|
|
145
|
+
return { next };
|
|
146
|
+
}),
|
|
147
|
+
filter((x) => !x.canceled),
|
|
148
|
+
map(({ next }) => next)
|
|
149
|
+
)
|
|
150
|
+
.subscribe((next) => {
|
|
151
|
+
if (next) {
|
|
152
|
+
try {
|
|
153
|
+
this.setCurrentContext(next, {
|
|
154
|
+
validate: true,
|
|
155
|
+
resolve: true,
|
|
156
|
+
});
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.warn('ContextProvider::onParentContextChanged', err);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
this.clearCurrentContext();
|
|
162
|
+
}
|
|
163
|
+
})
|
|
133
164
|
);
|
|
134
165
|
}
|
|
135
166
|
}
|
|
136
167
|
|
|
168
|
+
public async setCurrentContext(
|
|
169
|
+
context: ContextItem<Record<string, unknown>>,
|
|
170
|
+
opt?: { validate?: boolean; resolve?: boolean }
|
|
171
|
+
): Promise<ContextItem<Record<string, unknown>>> {
|
|
172
|
+
if (context === this.currentContext) {
|
|
173
|
+
return context;
|
|
174
|
+
}
|
|
175
|
+
if (opt?.validate && !this.validateContext(context)) {
|
|
176
|
+
if (opt.resolve) {
|
|
177
|
+
/** notify listeners about to resolve invalid context */
|
|
178
|
+
const onSetContextResolve = await this.#event?.dispatchEvent(
|
|
179
|
+
'onSetContextResolve',
|
|
180
|
+
{
|
|
181
|
+
source: this,
|
|
182
|
+
cancelable: true,
|
|
183
|
+
detail: { context },
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
if (onSetContextResolve?.canceled) {
|
|
187
|
+
throw Error('resolving of context was canceled');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const resolvedContext = await this.resolveContextAsync(context);
|
|
192
|
+
/** notify listeners about to resolved invalid context */
|
|
193
|
+
const onSetContextResolved = await this.#event?.dispatchEvent(
|
|
194
|
+
'onSetContextResolved',
|
|
195
|
+
{
|
|
196
|
+
source: this,
|
|
197
|
+
cancelable: true,
|
|
198
|
+
detail: { input: context, result: resolvedContext },
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
if (onSetContextResolved?.canceled) {
|
|
202
|
+
throw Error('resolving of context was canceled');
|
|
203
|
+
}
|
|
204
|
+
return this.setCurrentContext(resolvedContext);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.error('failed to resolve context', context, err);
|
|
207
|
+
this.clearCurrentContext();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
throw Error('failed to validate provided context');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const onCurrentContextChange = await this.#event?.dispatchEvent('onCurrentContextChange', {
|
|
214
|
+
source: this,
|
|
215
|
+
canBubble: true,
|
|
216
|
+
cancelable: true,
|
|
217
|
+
detail: { context: context },
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (onCurrentContextChange?.canceled) {
|
|
221
|
+
throw Error('change of context was aborted');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.#contextClient.setCurrentContext(context);
|
|
225
|
+
|
|
226
|
+
return context;
|
|
227
|
+
}
|
|
228
|
+
|
|
137
229
|
public queryContext(search: string): Observable<Array<ContextItem>> {
|
|
138
230
|
const query$ = this.queryClient
|
|
139
231
|
.query(
|
|
@@ -151,8 +243,60 @@ export class ContextProvider implements IContextProvider {
|
|
|
151
243
|
return lastValueFrom(this.queryContext(search));
|
|
152
244
|
}
|
|
153
245
|
|
|
246
|
+
public validateContext(item: ContextItem<Record<string, unknown>>): boolean {
|
|
247
|
+
if (!this.#contextType) return true;
|
|
248
|
+
return this.#contextType.map((x) => x.toLowerCase()).includes(item.type.id.toLowerCase());
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public resolveContext(
|
|
252
|
+
item: ContextItem<Record<string, unknown>>
|
|
253
|
+
): Observable<ContextItem<Record<string, unknown>>> {
|
|
254
|
+
return this.relatedContexts({ item, filter: { type: this.#contextType } }).pipe(
|
|
255
|
+
map((x) => x.filter((item) => this.validateContext(item))),
|
|
256
|
+
map((values) => {
|
|
257
|
+
const value = values.shift();
|
|
258
|
+
if (!value) {
|
|
259
|
+
throw Error('failed to resolve context');
|
|
260
|
+
}
|
|
261
|
+
if (values.length) {
|
|
262
|
+
console.warn(
|
|
263
|
+
'ContextProvider::relatedContext',
|
|
264
|
+
'multiple items found 🤣',
|
|
265
|
+
values
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return value;
|
|
269
|
+
})
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public resolveContextAsync(
|
|
274
|
+
item: ContextItem<Record<string, unknown>>
|
|
275
|
+
): Promise<ContextItem<Record<string, unknown>>> {
|
|
276
|
+
return lastValueFrom(this.resolveContext(item));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
public relatedContexts(
|
|
280
|
+
args: RelatedContextParameters
|
|
281
|
+
): Observable<Array<ContextItem<Record<string, unknown>>>> {
|
|
282
|
+
if (!this.#contextRelated) {
|
|
283
|
+
return throwError(() =>
|
|
284
|
+
Error(
|
|
285
|
+
'ContextProvider::relatedContexts - no client defined for resolving related context'
|
|
286
|
+
)
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return this.#contextRelated.query(args).pipe(map(({ value }) => value));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
public relatedContextsAsync(
|
|
293
|
+
args: RelatedContextParameters
|
|
294
|
+
): Promise<Array<ContextItem<Record<string, unknown>>>> {
|
|
295
|
+
return lastValueFrom(this.relatedContexts(args));
|
|
296
|
+
}
|
|
297
|
+
|
|
154
298
|
public clearCurrentContext() {
|
|
155
|
-
this.
|
|
299
|
+
this.#contextClient.setCurrentContext(undefined);
|
|
156
300
|
}
|
|
157
301
|
|
|
158
302
|
dispose() {
|
|
@@ -183,5 +327,31 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
183
327
|
IContextProvider
|
|
184
328
|
>
|
|
185
329
|
>;
|
|
330
|
+
onParentContextChanged: FrameworkEvent<
|
|
331
|
+
FrameworkEventInit<
|
|
332
|
+
{
|
|
333
|
+
context: ContextItem | undefined;
|
|
334
|
+
},
|
|
335
|
+
IContextProvider
|
|
336
|
+
>
|
|
337
|
+
>;
|
|
338
|
+
|
|
339
|
+
onSetContextResolve: FrameworkEvent<
|
|
340
|
+
FrameworkEventInit<
|
|
341
|
+
{
|
|
342
|
+
context: ContextItem;
|
|
343
|
+
},
|
|
344
|
+
IContextProvider
|
|
345
|
+
>
|
|
346
|
+
>;
|
|
347
|
+
onSetContextResolved: FrameworkEvent<
|
|
348
|
+
FrameworkEventInit<
|
|
349
|
+
{
|
|
350
|
+
input: ContextItem;
|
|
351
|
+
result?: ContextItem | null;
|
|
352
|
+
},
|
|
353
|
+
IContextProvider
|
|
354
|
+
>
|
|
355
|
+
>;
|
|
186
356
|
}
|
|
187
357
|
}
|
package/src/configurator.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { ModuleInitializerArgs, ModulesInstanceType } from '@equinor/fusion-framework-module';
|
|
2
2
|
import { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
|
|
3
|
-
import { getContextSelector, queryContextSelector } from './selectors';
|
|
3
|
+
import { getContextSelector, queryContextSelector, relatedContextSelector } from './selectors';
|
|
4
4
|
import { QueryCtorOptions } from '@equinor/fusion-query';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ContextFilterFn,
|
|
7
|
+
ContextItem,
|
|
8
|
+
QueryContextParameters,
|
|
9
|
+
RelatedContextParameters,
|
|
10
|
+
} from './types';
|
|
6
11
|
import { GetContextParameters } from './client/ContextClient';
|
|
7
12
|
import { ContextConfigBuilder, ContextConfigBuilderCallback } from './ContextConfigBuilder';
|
|
13
|
+
import { IContextProvider } from 'ContextProvider';
|
|
8
14
|
|
|
9
15
|
export interface ContextModuleConfig {
|
|
10
16
|
client: {
|
|
11
17
|
get: QueryCtorOptions<ContextItem, GetContextParameters>;
|
|
12
18
|
query: QueryCtorOptions<ContextItem[], QueryContextParameters>;
|
|
19
|
+
related?: QueryCtorOptions<ContextItem[], RelatedContextParameters>;
|
|
13
20
|
};
|
|
14
21
|
contextType?: string[];
|
|
15
22
|
contextFilter?: ContextFilterFn;
|
|
@@ -21,6 +28,16 @@ export interface ContextModuleConfig {
|
|
|
21
28
|
search: string;
|
|
22
29
|
type: ContextModuleConfig['contextType'];
|
|
23
30
|
}) => string | QueryContextParameters;
|
|
31
|
+
|
|
32
|
+
resolveContext?: (
|
|
33
|
+
this: IContextProvider,
|
|
34
|
+
item: ContextItem | null
|
|
35
|
+
) => ReturnType<IContextProvider['resolveContext']>;
|
|
36
|
+
|
|
37
|
+
validateContext?: (
|
|
38
|
+
this: IContextProvider,
|
|
39
|
+
item: ContextItem | null
|
|
40
|
+
) => ReturnType<IContextProvider['validateContext']>;
|
|
24
41
|
}
|
|
25
42
|
|
|
26
43
|
export interface IContextModuleConfigurator {
|
|
@@ -87,6 +104,20 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
|
|
|
87
104
|
key: (args) => JSON.stringify(args),
|
|
88
105
|
expire: this.defaultExpireTime,
|
|
89
106
|
},
|
|
107
|
+
related: {
|
|
108
|
+
client: {
|
|
109
|
+
fn: (args) => {
|
|
110
|
+
return contextClient.related(
|
|
111
|
+
'v1',
|
|
112
|
+
{ id: args.item.id, query: { filter: args.filter } },
|
|
113
|
+
{ selector: relatedContextSelector }
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
// TODO - might cast to checksum
|
|
118
|
+
key: (args) => JSON.stringify(args),
|
|
119
|
+
expire: this.defaultExpireTime,
|
|
120
|
+
},
|
|
90
121
|
};
|
|
91
122
|
})();
|
|
92
123
|
|
package/src/selectors.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ApiVersion, ApiContextEntity } from '@equinor/fusion-framework-module-services/context';
|
|
2
2
|
import type { GetContextResponse } from '@equinor/fusion-framework-module-services/context/get';
|
|
3
3
|
import type { QueryContextResponse } from '@equinor/fusion-framework-module-services/context/query';
|
|
4
|
+
import type { RelatedContextResponse } from '@equinor/fusion-framework-module-services/context/related';
|
|
4
5
|
import type { ContextItem, ContextItemType } from './types';
|
|
5
6
|
|
|
6
7
|
const parseContextType = (type: GetContextResponse<'v1'>['type']): ContextItemType => ({
|
|
@@ -33,3 +34,8 @@ export const queryContextSelector = async (response: Response): Promise<ContextI
|
|
|
33
34
|
const result = (await response.json()) as QueryContextResponse<'v1'>;
|
|
34
35
|
return result.map(parseContextItem);
|
|
35
36
|
};
|
|
37
|
+
|
|
38
|
+
export const relatedContextSelector = async (response: Response): Promise<ContextItem[]> => {
|
|
39
|
+
const result = (await response.json()) as RelatedContextResponse<'v1'>;
|
|
40
|
+
return result.map(parseContextItem);
|
|
41
|
+
};
|