@equinor/fusion-framework-module-context 4.1.0 → 4.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/CHANGELOG.md +116 -110
- package/dist/esm/ContextConfigBuilder.js.map +1 -1
- package/dist/esm/ContextProvider.js +18 -11
- package/dist/esm/ContextProvider.js.map +1 -1
- package/dist/esm/client/ContextClient.js.map +1 -1
- package/dist/esm/configurator.js.map +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/selectors.js.map +1 -1
- package/dist/esm/utils/resolve-context-from-path.js.map +1 -1
- package/dist/esm/utils/resolve-initial-context.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/ContextConfigBuilder.ts +2 -0
- package/src/ContextProvider.ts +192 -43
- package/src/errors.ts +20 -0
- package/src/module.ts +18 -1
- package/src/selectors.ts +26 -0
- package/src/version.ts +1 -1
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.1.
|
|
1
|
+
export declare const version = "4.1.1";
|
package/package.json
CHANGED
|
@@ -21,6 +21,8 @@ export type ContextConfigBuilderCallback = <TDeps extends Array<AnyModule> = []>
|
|
|
21
21
|
builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>,
|
|
22
22
|
) => void | Promise<void>;
|
|
23
23
|
|
|
24
|
+
// TODO - this should extend the BaseConfigBuilder
|
|
25
|
+
|
|
24
26
|
export class ContextConfigBuilder<
|
|
25
27
|
TModules extends Array<AnyModule> = [],
|
|
26
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/ContextProvider.ts
CHANGED
|
@@ -26,35 +26,114 @@ import Query from '@equinor/fusion-query';
|
|
|
26
26
|
* WARNING: this is an initial out cast.
|
|
27
27
|
* api clients will most probably not be exposed in future!
|
|
28
28
|
*/
|
|
29
|
+
/**
|
|
30
|
+
* Represents a context provider that manages the current context and provides methods for querying and manipulating context items.
|
|
31
|
+
*/
|
|
29
32
|
export interface IContextProvider {
|
|
30
33
|
/** DANGER */
|
|
31
34
|
readonly contextClient: ContextClient;
|
|
32
35
|
/** DANGER */
|
|
33
36
|
readonly queryClient: Query<ContextItem[], QueryContextParameters>;
|
|
34
37
|
|
|
38
|
+
// stream of current context
|
|
35
39
|
readonly currentContext$: Observable<ContextItem | null | undefined>;
|
|
40
|
+
|
|
41
|
+
// current context of the current state
|
|
36
42
|
currentContext: ContextItem | null | undefined;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Queries the context items based on the provided search string.
|
|
46
|
+
* @param search The search string.
|
|
47
|
+
* @returns An observable that emits an array of context items.
|
|
48
|
+
*/
|
|
37
49
|
queryContext(search: string): Observable<Array<ContextItem>>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Queries the context items asynchronously based on the provided search string.
|
|
53
|
+
* @param search The search string.
|
|
54
|
+
* @returns A promise that resolves to an array of context items.
|
|
55
|
+
*/
|
|
38
56
|
queryContextAsync(search: string): Promise<Array<ContextItem>>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validates the given context item.
|
|
60
|
+
* @param item The context item to validate.
|
|
61
|
+
* @returns A boolean indicating whether the context item is valid or not.
|
|
62
|
+
*/
|
|
39
63
|
validateContext(item: ContextItem<Record<string, unknown>>): boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolves the context item as a stream.
|
|
67
|
+
* @param current The current context item.
|
|
68
|
+
* @returns An observable that emits the resolved context item.
|
|
69
|
+
*/
|
|
40
70
|
resolveContext: (current: ContextItem) => Observable<ContextItem>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolves the context item asynchronously.
|
|
74
|
+
* @param current The current context item.
|
|
75
|
+
* @returns A promise that resolves to the resolved context item.
|
|
76
|
+
*/
|
|
41
77
|
resolveContextAsync: (current: ContextItem) => Promise<ContextItem>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Retrieves the related context items based on the provided parameters.
|
|
81
|
+
* @param args The parameters for retrieving related context items.
|
|
82
|
+
* @returns An observable that emits an array of related context items.
|
|
83
|
+
*/
|
|
42
84
|
relatedContexts: (
|
|
43
85
|
args: RelatedContextParameters,
|
|
44
86
|
) => Observable<Array<ContextItem<Record<string, unknown>>>>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Retrieves the related context items asynchronously based on the provided parameters.
|
|
90
|
+
* @param args The parameters for retrieving related context items.
|
|
91
|
+
* @returns A promise that resolves to an array of related context items.
|
|
92
|
+
*/
|
|
45
93
|
relatedContextsAsync: (
|
|
46
94
|
args: RelatedContextParameters,
|
|
47
95
|
) => Promise<Array<ContextItem<Record<string, unknown>>>>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Clears the current context.
|
|
99
|
+
*/
|
|
48
100
|
clearCurrentContext: VoidFunction;
|
|
49
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Sets the current context item by its ID.
|
|
104
|
+
* @param id The ID of the context item.
|
|
105
|
+
* @returns An observable that emits the current context item.
|
|
106
|
+
*/
|
|
50
107
|
setCurrentContextById(id: string): Observable<ContextItem<Record<string, unknown>>>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Sets the current context item by its ID asynchronously.
|
|
111
|
+
* @param id The ID of the context item.
|
|
112
|
+
* @returns A promise that resolves to the current context item.
|
|
113
|
+
*/
|
|
51
114
|
setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>>;
|
|
52
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Sets the current context item.
|
|
118
|
+
* @param context The context item to set as the current context.
|
|
119
|
+
* @param opt Optional settings for the operation.
|
|
120
|
+
* @param opt.validate Specifies whether to validate the context item. Default is `true`.
|
|
121
|
+
* @param opt.resolve Specifies whether to resolve the context item. Default is `true`.
|
|
122
|
+
* @returns An observable that emits the current context item or `null`.
|
|
123
|
+
*/
|
|
53
124
|
setCurrentContext(
|
|
54
125
|
context: ContextItem<Record<string, unknown>> | null,
|
|
55
126
|
opt?: { validate?: boolean; resolve?: boolean },
|
|
56
127
|
): Observable<ContextItem<Record<string, unknown>> | null>;
|
|
57
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Sets the current context item asynchronously.
|
|
131
|
+
* @param context The context item to set as the current context.
|
|
132
|
+
* @param opt Optional settings for the operation.
|
|
133
|
+
* @param opt.validate Specifies whether to validate the context item. Default is `true`.
|
|
134
|
+
* @param opt.resolve Specifies whether to resolve the context item. Default is `true`.
|
|
135
|
+
* @returns A promise that resolves to the current context item or `null`.
|
|
136
|
+
*/
|
|
58
137
|
setCurrentContextAsync(
|
|
59
138
|
context: ContextItem<Record<string, unknown>> | null,
|
|
60
139
|
opt?: { validate?: boolean; resolve?: boolean },
|
|
@@ -72,7 +151,7 @@ export class ContextProvider implements IContextProvider {
|
|
|
72
151
|
|
|
73
152
|
#contextType?: ContextModuleConfig['contextType'];
|
|
74
153
|
#contextFilter: ContextModuleConfig['contextFilter'];
|
|
75
|
-
#contextParameterFn: Required<ContextModuleConfig['contextParameterFn']
|
|
154
|
+
#contextParameterFn: Required<ContextModuleConfig>['contextParameterFn'];
|
|
76
155
|
|
|
77
156
|
#contextQueue = new Subject<Observable<ContextItem<Record<string, unknown>>>>();
|
|
78
157
|
|
|
@@ -122,12 +201,14 @@ export class ContextProvider implements IContextProvider {
|
|
|
122
201
|
|
|
123
202
|
this.#event = event;
|
|
124
203
|
|
|
204
|
+
// set the resolve and validate context functions
|
|
125
205
|
config.resolveContext && (this.resolveContext = config.resolveContext?.bind(this));
|
|
126
206
|
config.validateContext && (this.validateContext = config.validateContext?.bind(this));
|
|
127
207
|
|
|
128
208
|
this.#contextType = config.contextType;
|
|
129
209
|
this.#contextFilter = config.contextFilter;
|
|
130
210
|
|
|
211
|
+
// create clients
|
|
131
212
|
this.#contextClient = new ContextClient(config.client.get);
|
|
132
213
|
this.#contextQuery = new Query(config.client.query);
|
|
133
214
|
|
|
@@ -135,27 +216,36 @@ export class ContextProvider implements IContextProvider {
|
|
|
135
216
|
this.#contextRelated = new Query(config.client.related);
|
|
136
217
|
}
|
|
137
218
|
|
|
219
|
+
// set the context parameter function
|
|
138
220
|
this.#contextParameterFn =
|
|
139
221
|
config.contextParameterFn ??
|
|
222
|
+
// fallback to default
|
|
140
223
|
((args: Parameters<Required<ContextModuleConfig>['contextParameterFn']>[0]) => ({
|
|
141
224
|
search: args.search,
|
|
142
225
|
filter: { type: args.type },
|
|
143
226
|
}));
|
|
144
227
|
|
|
228
|
+
// if event module is available, setup event listeners
|
|
145
229
|
if (this.#event) {
|
|
146
230
|
this.#subscriptions.add(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
231
|
+
// observe current context changes
|
|
232
|
+
this.currentContext$
|
|
233
|
+
.pipe(
|
|
234
|
+
// emit previous and next context
|
|
235
|
+
pairwise(),
|
|
236
|
+
)
|
|
237
|
+
.subscribe(([previous, next]) => {
|
|
238
|
+
this.#event?.dispatchEvent('onCurrentContextChanged', {
|
|
239
|
+
source: this,
|
|
240
|
+
canBubble: true,
|
|
241
|
+
detail: { previous, next },
|
|
242
|
+
});
|
|
243
|
+
}),
|
|
154
244
|
);
|
|
155
245
|
this.#subscriptions.add(
|
|
156
|
-
|
|
246
|
+
// observe current context changes from child modules
|
|
157
247
|
this.#event.addEventListener('onCurrentContextChanged', (e) => {
|
|
158
|
-
|
|
248
|
+
// prevent infinite loop, only set context if source is not this
|
|
159
249
|
if (e.source !== this && e.detail.next !== undefined) {
|
|
160
250
|
this.setCurrentContext(e.detail.next);
|
|
161
251
|
}
|
|
@@ -163,13 +253,18 @@ export class ContextProvider implements IContextProvider {
|
|
|
163
253
|
);
|
|
164
254
|
}
|
|
165
255
|
|
|
256
|
+
// wire up context queue
|
|
166
257
|
this.#subscriptions.add(
|
|
167
258
|
this.#contextQueue
|
|
168
259
|
.pipe(
|
|
260
|
+
// resolve context item from queue
|
|
169
261
|
switchMap((next) => next),
|
|
170
262
|
tap((x) => console.debug('ContextProvider::#contextQueue', x)),
|
|
171
263
|
)
|
|
172
|
-
.subscribe((context) =>
|
|
264
|
+
.subscribe((context) => {
|
|
265
|
+
// set context from resolved context item from queue
|
|
266
|
+
this.#contextClient.setCurrentContext(context ?? null);
|
|
267
|
+
}),
|
|
173
268
|
);
|
|
174
269
|
}
|
|
175
270
|
|
|
@@ -181,6 +276,8 @@ export class ContextProvider implements IContextProvider {
|
|
|
181
276
|
// do not set context if parent has not initialized
|
|
182
277
|
filter((x): x is ContextItem | null => x !== undefined),
|
|
183
278
|
filter((next, index) => {
|
|
279
|
+
// skip first item if opt.skipFirst is true
|
|
280
|
+
// TODO: this is a bit hacky, should be handled in a better way
|
|
184
281
|
if (opt?.skipFirst && index <= 1) {
|
|
185
282
|
console.debug(
|
|
186
283
|
'ContextProvider::connectParentContext',
|
|
@@ -189,24 +286,29 @@ export class ContextProvider implements IContextProvider {
|
|
|
189
286
|
);
|
|
190
287
|
return false;
|
|
191
288
|
}
|
|
289
|
+
// only set context if it has changed
|
|
192
290
|
return this.currentContext?.id !== next?.id;
|
|
193
291
|
}),
|
|
194
292
|
switchMap(async (next) => {
|
|
195
|
-
if (next) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
{
|
|
199
|
-
source: this,
|
|
200
|
-
detail: next,
|
|
201
|
-
cancelable: true,
|
|
202
|
-
},
|
|
203
|
-
);
|
|
204
|
-
return { next, canceled: onParentContextChanged?.canceled };
|
|
293
|
+
if (!next) {
|
|
294
|
+
// if parent context is null, just return
|
|
295
|
+
return { next };
|
|
205
296
|
}
|
|
206
|
-
|
|
297
|
+
// notify event observers that parent context is about to change and await for cancelation
|
|
298
|
+
const onParentContextChanged = await this.#event?.dispatchEvent(
|
|
299
|
+
'onParentContextChanged',
|
|
300
|
+
{
|
|
301
|
+
source: this,
|
|
302
|
+
detail: next,
|
|
303
|
+
cancelable: true,
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
return { next, canceled: onParentContextChanged?.canceled };
|
|
207
307
|
}),
|
|
308
|
+
// filter out canceled context changes
|
|
208
309
|
filter((x) => !x.canceled),
|
|
209
310
|
switchMap(({ next }) => {
|
|
311
|
+
// set current context with validation and resolution
|
|
210
312
|
return this.setCurrentContext(next, {
|
|
211
313
|
validate: true,
|
|
212
314
|
resolve: true,
|
|
@@ -217,17 +319,22 @@ export class ContextProvider implements IContextProvider {
|
|
|
217
319
|
'setCurrentContext',
|
|
218
320
|
err,
|
|
219
321
|
);
|
|
322
|
+
// do not emit any value if an error occurs
|
|
220
323
|
return EMPTY;
|
|
221
324
|
}),
|
|
222
325
|
);
|
|
223
326
|
}),
|
|
224
327
|
catchError((err) => {
|
|
225
328
|
console.warn('ContextProvider::onParentContextChanged', 'unhandled exception', err);
|
|
329
|
+
// do not emit any value if an error occurs
|
|
226
330
|
return EMPTY;
|
|
227
331
|
}),
|
|
228
332
|
);
|
|
229
333
|
|
|
334
|
+
// subscribe to parent context changes
|
|
230
335
|
const subscription = parentContext$.subscribe();
|
|
336
|
+
|
|
337
|
+
// add subscription to internal teardown
|
|
231
338
|
this.#subscriptions.add(subscription);
|
|
232
339
|
return subscription;
|
|
233
340
|
}
|
|
@@ -236,19 +343,24 @@ export class ContextProvider implements IContextProvider {
|
|
|
236
343
|
return new Observable((subscriber) => {
|
|
237
344
|
try {
|
|
238
345
|
this.#contextClient
|
|
346
|
+
// resolve context item by id
|
|
239
347
|
.resolveContext(id)
|
|
240
348
|
.pipe(
|
|
349
|
+
// filter out invalid context items
|
|
241
350
|
filter((item): item is ContextItem => !!item),
|
|
351
|
+
// set current context with validation and resolution
|
|
242
352
|
switchMap((item) => this.setCurrentContext(item)),
|
|
243
353
|
)
|
|
244
354
|
.subscribe(subscriber);
|
|
245
355
|
} catch (err) {
|
|
356
|
+
// catch any unhandled exceptions and emit error
|
|
246
357
|
subscriber.error(err);
|
|
247
358
|
}
|
|
248
359
|
});
|
|
249
360
|
}
|
|
250
361
|
|
|
251
362
|
public setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>> {
|
|
363
|
+
// return last value from observable
|
|
252
364
|
return lastValueFrom(this.setCurrentContextById(id));
|
|
253
365
|
}
|
|
254
366
|
|
|
@@ -267,33 +379,33 @@ export class ContextProvider implements IContextProvider {
|
|
|
267
379
|
context: T,
|
|
268
380
|
opt?: { validate?: boolean; resolve?: boolean },
|
|
269
381
|
): Observable<T> {
|
|
270
|
-
|
|
382
|
+
// signal for aborting the queue entry
|
|
271
383
|
const abort$ = new Subject();
|
|
272
384
|
|
|
273
|
-
|
|
385
|
+
// wrapper for returning an observable to the caller
|
|
274
386
|
const subject$ = new Subject<T>();
|
|
275
387
|
|
|
276
388
|
const task$ = this._setCurrentContext(context, opt).pipe(
|
|
277
|
-
|
|
389
|
+
// send context item which was set to the caller
|
|
278
390
|
tap((x) => subject$.next(x)),
|
|
279
|
-
|
|
391
|
+
// abort task on signal
|
|
280
392
|
takeUntil(abort$),
|
|
281
|
-
|
|
393
|
+
// close the observable sent to the caller
|
|
282
394
|
finalize(() => subject$.complete()),
|
|
283
|
-
|
|
395
|
+
// catch any unhandled exceptions to not stall the queue
|
|
284
396
|
catchError((err) => {
|
|
285
|
-
|
|
397
|
+
// emit error to caller
|
|
286
398
|
subject$.error(err);
|
|
287
|
-
|
|
399
|
+
// skip setting any context
|
|
288
400
|
return EMPTY;
|
|
289
401
|
}),
|
|
290
402
|
);
|
|
291
403
|
|
|
292
|
-
|
|
404
|
+
// add task to internal queue
|
|
293
405
|
this.#contextQueue.next(task$ as Observable<ContextItem<Record<string, unknown>>>);
|
|
294
406
|
|
|
295
407
|
return subject$.pipe(
|
|
296
|
-
|
|
408
|
+
// if caller subscribes, unsubscribe should abort queue entry
|
|
297
409
|
finalize(() => abort$.next(true)),
|
|
298
410
|
);
|
|
299
411
|
}
|
|
@@ -303,24 +415,29 @@ export class ContextProvider implements IContextProvider {
|
|
|
303
415
|
opt?: { validate?: boolean; resolve?: boolean },
|
|
304
416
|
): Observable<T> {
|
|
305
417
|
return new Observable((subscriber) => {
|
|
418
|
+
// if context is the same as current, just emit and complete
|
|
306
419
|
if (context === this.currentContext) {
|
|
307
420
|
subscriber.next(context);
|
|
308
421
|
return subscriber.complete();
|
|
309
422
|
}
|
|
423
|
+
// check if context is provided and should be validated
|
|
310
424
|
if (context && opt?.validate && !this.validateContext(context)) {
|
|
425
|
+
// check if the resolve context is provided
|
|
311
426
|
if (!opt.resolve) {
|
|
312
|
-
|
|
427
|
+
// notify event observers that context validation failed since resolve is not provided
|
|
313
428
|
this.#event?.dispatchEvent('onSetContextValidationFailed', {
|
|
314
429
|
source: this,
|
|
315
430
|
detail: { context },
|
|
316
431
|
});
|
|
432
|
+
// emit error and complete
|
|
317
433
|
return subscriber.error(Error('failed to validate provided context'));
|
|
318
434
|
}
|
|
319
435
|
if (opt.resolve) {
|
|
320
436
|
return of(context)
|
|
321
437
|
.pipe(
|
|
322
|
-
|
|
438
|
+
// notify event observers that context is about to get resolved
|
|
323
439
|
switchMap(async (context) => {
|
|
440
|
+
// wait for event listeners to handle the event
|
|
324
441
|
const event = await this.#event?.dispatchEvent(
|
|
325
442
|
'onSetContextResolve',
|
|
326
443
|
{
|
|
@@ -329,12 +446,13 @@ export class ContextProvider implements IContextProvider {
|
|
|
329
446
|
detail: { context },
|
|
330
447
|
},
|
|
331
448
|
);
|
|
449
|
+
// check if event was canceled and abort if so
|
|
332
450
|
if (event?.canceled) {
|
|
333
451
|
throw Error('resolving of context was canceled');
|
|
334
452
|
}
|
|
335
453
|
return context;
|
|
336
454
|
}),
|
|
337
|
-
|
|
455
|
+
// resolve context
|
|
338
456
|
switchMap((context) =>
|
|
339
457
|
this.resolveContext(context).pipe(
|
|
340
458
|
map((resolved) => ({
|
|
@@ -343,8 +461,9 @@ export class ContextProvider implements IContextProvider {
|
|
|
343
461
|
})),
|
|
344
462
|
),
|
|
345
463
|
),
|
|
346
|
-
|
|
464
|
+
// notify event listeners that context was resolved
|
|
347
465
|
switchMap(async ({ context, resolved }) => {
|
|
466
|
+
// wait for event listeners to handle the event
|
|
348
467
|
const event = await this.#event?.dispatchEvent(
|
|
349
468
|
'onSetContextResolved',
|
|
350
469
|
{
|
|
@@ -353,12 +472,13 @@ export class ContextProvider implements IContextProvider {
|
|
|
353
472
|
detail: { context, resolved },
|
|
354
473
|
},
|
|
355
474
|
);
|
|
475
|
+
// check if event was canceled and abort if so
|
|
356
476
|
if (event?.canceled) {
|
|
357
477
|
throw Error('resolving of context was canceled');
|
|
358
478
|
}
|
|
359
479
|
return resolved;
|
|
360
480
|
}),
|
|
361
|
-
|
|
481
|
+
// recursive call to set current context without validation and resolution
|
|
362
482
|
switchMap((resolved) =>
|
|
363
483
|
this._setCurrentContext(resolved as unknown as T),
|
|
364
484
|
),
|
|
@@ -367,8 +487,10 @@ export class ContextProvider implements IContextProvider {
|
|
|
367
487
|
}
|
|
368
488
|
}
|
|
369
489
|
|
|
490
|
+
// make the context an observable
|
|
370
491
|
return of(context)
|
|
371
492
|
.pipe(
|
|
493
|
+
// alert event listeners that context is about to change
|
|
372
494
|
switchMap(async (context) => {
|
|
373
495
|
const event = await this.#event?.dispatchEvent('onCurrentContextChange', {
|
|
374
496
|
source: this,
|
|
@@ -377,6 +499,7 @@ export class ContextProvider implements IContextProvider {
|
|
|
377
499
|
detail: { context: context },
|
|
378
500
|
});
|
|
379
501
|
|
|
502
|
+
// check if event was canceled and abort if so
|
|
380
503
|
if (event?.canceled) {
|
|
381
504
|
throw Error('change of context was aborted');
|
|
382
505
|
}
|
|
@@ -385,7 +508,9 @@ export class ContextProvider implements IContextProvider {
|
|
|
385
508
|
}),
|
|
386
509
|
)
|
|
387
510
|
.subscribe((context) => {
|
|
511
|
+
// emit context to the caller
|
|
388
512
|
subscriber.next(context);
|
|
513
|
+
// only take the first value and complete
|
|
389
514
|
subscriber.complete();
|
|
390
515
|
});
|
|
391
516
|
});
|
|
@@ -401,13 +526,15 @@ export class ContextProvider implements IContextProvider {
|
|
|
401
526
|
public queryContext(search: string): Observable<Array<ContextItem>> {
|
|
402
527
|
const query$ = this.queryClient
|
|
403
528
|
.query(
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
529
|
+
// generate query parameters
|
|
530
|
+
this.#contextParameterFn({
|
|
531
|
+
search,
|
|
532
|
+
type: this.#contextType,
|
|
533
|
+
}) as QueryContextParameters,
|
|
408
534
|
)
|
|
409
535
|
.pipe(
|
|
410
536
|
catchError((err) => {
|
|
537
|
+
// if query client throws a QueryClientError, extract the cause and throw it
|
|
411
538
|
if (err.name === 'QueryClientError') {
|
|
412
539
|
throw err.cause;
|
|
413
540
|
}
|
|
@@ -416,6 +543,7 @@ export class ContextProvider implements IContextProvider {
|
|
|
416
543
|
map((x) => x.value),
|
|
417
544
|
);
|
|
418
545
|
|
|
546
|
+
// apply context filter if available
|
|
419
547
|
return this.#contextFilter ? query$.pipe(map(this.#contextFilter)) : query$;
|
|
420
548
|
}
|
|
421
549
|
|
|
@@ -431,13 +559,20 @@ export class ContextProvider implements IContextProvider {
|
|
|
431
559
|
public resolveContext(
|
|
432
560
|
item: ContextItem<Record<string, unknown>>,
|
|
433
561
|
): Observable<ContextItem<Record<string, unknown>>> {
|
|
562
|
+
// request related context items for the given context item with the same context type which the provider is configured with
|
|
434
563
|
return this.relatedContexts({ item, filter: { type: this.#contextType } }).pipe(
|
|
564
|
+
// filter out invalid context items
|
|
435
565
|
map((x) => x.filter((item) => this.validateContext(item))),
|
|
436
566
|
map((values) => {
|
|
567
|
+
// related context should be resolved to a single context item
|
|
437
568
|
const value = values.shift();
|
|
569
|
+
|
|
570
|
+
// if no value is found, throw an error
|
|
438
571
|
if (!value) {
|
|
439
572
|
throw Error('failed to resolve context');
|
|
440
573
|
}
|
|
574
|
+
|
|
575
|
+
// if multiple items are found, log a warning
|
|
441
576
|
if (values.length) {
|
|
442
577
|
console.warn(
|
|
443
578
|
'ContextProvider::relatedContext',
|
|
@@ -445,6 +580,8 @@ export class ContextProvider implements IContextProvider {
|
|
|
445
580
|
values,
|
|
446
581
|
);
|
|
447
582
|
}
|
|
583
|
+
|
|
584
|
+
// return the resolved context item
|
|
448
585
|
return value;
|
|
449
586
|
}),
|
|
450
587
|
);
|
|
@@ -459,6 +596,7 @@ export class ContextProvider implements IContextProvider {
|
|
|
459
596
|
public relatedContexts(
|
|
460
597
|
args: RelatedContextParameters,
|
|
461
598
|
): Observable<Array<ContextItem<Record<string, unknown>>>> {
|
|
599
|
+
// check if related context client is available
|
|
462
600
|
if (!this.#contextRelated) {
|
|
463
601
|
return throwError(() =>
|
|
464
602
|
Error(
|
|
@@ -466,6 +604,8 @@ export class ContextProvider implements IContextProvider {
|
|
|
466
604
|
),
|
|
467
605
|
);
|
|
468
606
|
}
|
|
607
|
+
|
|
608
|
+
// request related context items
|
|
469
609
|
return this.#contextRelated.query(args).pipe(
|
|
470
610
|
map(({ value }) => value),
|
|
471
611
|
catchError((err) => {
|
|
@@ -497,7 +637,7 @@ export default ContextProvider;
|
|
|
497
637
|
|
|
498
638
|
declare module '@equinor/fusion-framework-module-event' {
|
|
499
639
|
interface FrameworkEventMap {
|
|
500
|
-
|
|
640
|
+
// event which is dispatched before the context changes
|
|
501
641
|
onCurrentContextChange: FrameworkEvent<
|
|
502
642
|
FrameworkEventInit<
|
|
503
643
|
{
|
|
@@ -506,7 +646,7 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
506
646
|
IContextProvider
|
|
507
647
|
>
|
|
508
648
|
>;
|
|
509
|
-
|
|
649
|
+
// event which is dispatched after the context changes
|
|
510
650
|
onCurrentContextChanged: FrameworkEvent<
|
|
511
651
|
FrameworkEventInit<
|
|
512
652
|
{
|
|
@@ -516,6 +656,8 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
516
656
|
IContextProvider
|
|
517
657
|
>
|
|
518
658
|
>;
|
|
659
|
+
|
|
660
|
+
// event which is dispatched before the parent context changes
|
|
519
661
|
onParentContextChanged: FrameworkEvent<
|
|
520
662
|
FrameworkEventInit<
|
|
521
663
|
{
|
|
@@ -525,6 +667,7 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
525
667
|
>
|
|
526
668
|
>;
|
|
527
669
|
|
|
670
|
+
// event which is dispatched before the context will be resolved
|
|
528
671
|
onSetContextResolve: FrameworkEvent<
|
|
529
672
|
FrameworkEventInit<
|
|
530
673
|
{
|
|
@@ -533,6 +676,8 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
533
676
|
IContextProvider
|
|
534
677
|
>
|
|
535
678
|
>;
|
|
679
|
+
|
|
680
|
+
// event which is dispatched after the context was resolved
|
|
536
681
|
onSetContextResolved: FrameworkEvent<
|
|
537
682
|
FrameworkEventInit<
|
|
538
683
|
{
|
|
@@ -542,6 +687,8 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
542
687
|
IContextProvider
|
|
543
688
|
>
|
|
544
689
|
>;
|
|
690
|
+
|
|
691
|
+
// event which is dispatched if the context validation failed
|
|
545
692
|
onSetContextValidationFailed: FrameworkEvent<
|
|
546
693
|
FrameworkEventInit<
|
|
547
694
|
{
|
|
@@ -550,6 +697,8 @@ declare module '@equinor/fusion-framework-module-event' {
|
|
|
550
697
|
IContextProvider
|
|
551
698
|
>
|
|
552
699
|
>;
|
|
700
|
+
|
|
701
|
+
// event which is dispatched if the context resolve failed
|
|
553
702
|
onSetContextResolveFailed: FrameworkEvent<
|
|
554
703
|
FrameworkEventInit<
|
|
555
704
|
{
|
package/src/errors.ts
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents an error that occurs during a search in the Fusion Context.
|
|
3
|
+
*/
|
|
1
4
|
export class FusionContextSearchError extends Error {
|
|
2
5
|
#details;
|
|
3
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The title of the error.
|
|
9
|
+
*/
|
|
4
10
|
get title(): string {
|
|
5
11
|
return this.#details.title;
|
|
6
12
|
}
|
|
7
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The description of the error, if available.
|
|
16
|
+
*/
|
|
8
17
|
get description(): string | undefined {
|
|
9
18
|
return this.#details.description;
|
|
10
19
|
}
|
|
11
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new instance of FusionContextSearchError.
|
|
23
|
+
* @param details - The details of the error.
|
|
24
|
+
* @param options - Optional parameters for the error.
|
|
25
|
+
*/
|
|
12
26
|
constructor(
|
|
13
27
|
details: {
|
|
28
|
+
/**
|
|
29
|
+
* The title of the error.
|
|
30
|
+
*/
|
|
14
31
|
title: string;
|
|
32
|
+
/**
|
|
33
|
+
* The description of the error, if available.
|
|
34
|
+
*/
|
|
15
35
|
description?: string;
|
|
16
36
|
},
|
|
17
37
|
options?: ErrorOptions,
|