@equinor/fusion-framework-module-context
Advanced tools
Comparing version 2.0.15 to 3.0.0
@@ -6,2 +6,8 @@ # Change Log | ||
## 3.0.0 (2023-04-16) | ||
### Features | ||
- **modules/context:** resolve related context ([0e92583](https://github.com/equinor/fusion-framework/commit/0e925837a4f2651ff9f2a003d13731f6d866412d)) | ||
## 2.0.15 (2023-04-14) | ||
@@ -8,0 +14,0 @@ |
@@ -16,3 +16,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
var _ContextModuleConfigurator_configBuilders; | ||
import { getContextSelector, queryContextSelector } from './selectors'; | ||
import { getContextSelector, queryContextSelector, relatedContextSelector } from './selectors'; | ||
import { ContextConfigBuilder } from './ContextConfigBuilder'; | ||
@@ -68,2 +68,11 @@ export class ContextModuleConfigurator { | ||
}, | ||
related: { | ||
client: { | ||
fn: (args) => { | ||
return contextClient.related('v1', { id: args.item.id, query: { filter: args.filter } }, { selector: relatedContextSelector }); | ||
}, | ||
}, | ||
key: (args) => JSON.stringify(args), | ||
expire: this.defaultExpireTime, | ||
}, | ||
}; | ||
@@ -70,0 +79,0 @@ }))()); |
@@ -31,2 +31,8 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
} | ||
setValidateContext(fn) { | ||
this.config.validateContext = fn; | ||
} | ||
setResolveContext(fn) { | ||
this.config.resolveContext = fn; | ||
} | ||
setContextClient(client, expire = 1 * 60 * 1000) { | ||
@@ -53,2 +59,14 @@ this.config.client = { | ||
}; | ||
if (client.related) { | ||
this.config.client.related = | ||
typeof client.related === 'function' | ||
? { | ||
key: (args) => JSON.stringify(args), | ||
client: { | ||
fn: client.related, | ||
}, | ||
expire, | ||
} | ||
: client.related; | ||
} | ||
} | ||
@@ -55,0 +73,0 @@ } |
@@ -0,1 +1,10 @@ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
@@ -12,5 +21,5 @@ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
}; | ||
var _ContextProvider_contextClient, _ContextProvider_contextQuery, _ContextProvider_event, _ContextProvider_subscriptions, _ContextProvider_contextType, _ContextProvider_contextFilter, _ContextProvider_contextParameterFn; | ||
import { lastValueFrom, Subscription } from 'rxjs'; | ||
import { map, pairwise } from 'rxjs/operators'; | ||
var _ContextProvider_contextClient, _ContextProvider_contextQuery, _ContextProvider_contextRelated, _ContextProvider_event, _ContextProvider_subscriptions, _ContextProvider_contextType, _ContextProvider_contextFilter, _ContextProvider_contextParameterFn; | ||
import { lastValueFrom, Subscription, throwError } from 'rxjs'; | ||
import { filter, map, pairwise, switchMap } from 'rxjs/operators'; | ||
import { ContextClient } from './client/ContextClient'; | ||
@@ -32,24 +41,9 @@ import Query from '@equinor/fusion-query'; | ||
set currentContext(context) { | ||
if (__classPrivateFieldGet(this, _ContextProvider_event, "f")) { | ||
__classPrivateFieldGet(this, _ContextProvider_event, "f") | ||
.dispatchEvent('onCurrentContextChange', { | ||
source: this, | ||
canBubble: true, | ||
cancelable: true, | ||
detail: { context }, | ||
}) | ||
.then((e) => { | ||
if (!e.canceled) { | ||
__classPrivateFieldGet(this, _ContextProvider_contextClient, "f").setCurrentContext(context); | ||
} | ||
}); | ||
} | ||
else { | ||
__classPrivateFieldGet(this, _ContextProvider_contextClient, "f").setCurrentContext(context); | ||
} | ||
context ? this.setCurrentContext(context) : this.clearCurrentContext(); | ||
} | ||
constructor(args) { | ||
var _a; | ||
var _a, _b, _c; | ||
_ContextProvider_contextClient.set(this, void 0); | ||
_ContextProvider_contextQuery.set(this, void 0); | ||
_ContextProvider_contextRelated.set(this, void 0); | ||
_ContextProvider_event.set(this, void 0); | ||
@@ -61,2 +55,4 @@ _ContextProvider_subscriptions.set(this, new Subscription()); | ||
const { config, event, parentContext } = args; | ||
config.resolveContext && (this.resolveContext = (_a = config.resolveContext) === null || _a === void 0 ? void 0 : _a.bind(this)); | ||
config.validateContext && (this.validateContext = (_b = config.validateContext) === null || _b === void 0 ? void 0 : _b.bind(this)); | ||
__classPrivateFieldSet(this, _ContextProvider_contextType, config.contextType, "f"); | ||
@@ -66,3 +62,6 @@ __classPrivateFieldSet(this, _ContextProvider_contextFilter, config.contextFilter, "f"); | ||
__classPrivateFieldSet(this, _ContextProvider_contextQuery, new Query(config.client.query), "f"); | ||
__classPrivateFieldSet(this, _ContextProvider_contextParameterFn, (_a = config.contextParameterFn) !== null && _a !== void 0 ? _a : ((args) => ({ | ||
if (config.client.related) { | ||
__classPrivateFieldSet(this, _ContextProvider_contextRelated, new Query(config.client.related), "f"); | ||
} | ||
__classPrivateFieldSet(this, _ContextProvider_contextParameterFn, (_c = config.contextParameterFn) !== null && _c !== void 0 ? _c : ((args) => ({ | ||
search: args.search, | ||
@@ -89,5 +88,81 @@ filter: { type: args.type }, | ||
if (parentContext) { | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add(parentContext.contextClient.currentContext$.subscribe((next) => (this.currentContext = next))); | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add(parentContext.contextClient.currentContext$ | ||
.pipe(switchMap((next) => __awaiter(this, void 0, void 0, function* () { | ||
var _d; | ||
if (next) { | ||
const onParentContextChanged = yield ((_d = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _d === void 0 ? void 0 : _d.dispatchEvent('onParentContextChanged', { | ||
source: this, | ||
detail: next, | ||
cancelable: true, | ||
})); | ||
return { next, canceled: onParentContextChanged === null || onParentContextChanged === void 0 ? void 0 : onParentContextChanged.canceled }; | ||
} | ||
return { next }; | ||
})), filter((x) => !x.canceled), map(({ next }) => next)) | ||
.subscribe((next) => { | ||
if (next) { | ||
try { | ||
this.setCurrentContext(next, { | ||
validate: true, | ||
resolve: true, | ||
}); | ||
} | ||
catch (err) { | ||
console.warn('ContextProvider::onParentContextChanged', err); | ||
} | ||
} | ||
else { | ||
this.clearCurrentContext(); | ||
} | ||
})); | ||
} | ||
} | ||
setCurrentContext(context, opt) { | ||
var _a, _b, _c; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (context === this.currentContext) { | ||
return context; | ||
} | ||
if ((opt === null || opt === void 0 ? void 0 : opt.validate) && !this.validateContext(context)) { | ||
if (opt.resolve) { | ||
const onSetContextResolve = yield ((_a = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _a === void 0 ? void 0 : _a.dispatchEvent('onSetContextResolve', { | ||
source: this, | ||
cancelable: true, | ||
detail: { context }, | ||
})); | ||
if (onSetContextResolve === null || onSetContextResolve === void 0 ? void 0 : onSetContextResolve.canceled) { | ||
throw Error('resolving of context was canceled'); | ||
} | ||
try { | ||
const resolvedContext = yield this.resolveContextAsync(context); | ||
const onSetContextResolved = yield ((_b = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _b === void 0 ? void 0 : _b.dispatchEvent('onSetContextResolved', { | ||
source: this, | ||
cancelable: true, | ||
detail: { input: context, result: resolvedContext }, | ||
})); | ||
if (onSetContextResolved === null || onSetContextResolved === void 0 ? void 0 : onSetContextResolved.canceled) { | ||
throw Error('resolving of context was canceled'); | ||
} | ||
return this.setCurrentContext(resolvedContext); | ||
} | ||
catch (err) { | ||
console.error('failed to resolve context', context, err); | ||
this.clearCurrentContext(); | ||
} | ||
} | ||
throw Error('failed to validate provided context'); | ||
} | ||
const onCurrentContextChange = yield ((_c = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _c === void 0 ? void 0 : _c.dispatchEvent('onCurrentContextChange', { | ||
source: this, | ||
canBubble: true, | ||
cancelable: true, | ||
detail: { context: context }, | ||
})); | ||
if (onCurrentContextChange === null || onCurrentContextChange === void 0 ? void 0 : onCurrentContextChange.canceled) { | ||
throw Error('change of context was aborted'); | ||
} | ||
__classPrivateFieldGet(this, _ContextProvider_contextClient, "f").setCurrentContext(context); | ||
return context; | ||
}); | ||
} | ||
queryContext(search) { | ||
@@ -102,4 +177,33 @@ const query$ = this.queryClient | ||
} | ||
validateContext(item) { | ||
if (!__classPrivateFieldGet(this, _ContextProvider_contextType, "f")) | ||
return true; | ||
return __classPrivateFieldGet(this, _ContextProvider_contextType, "f").map((x) => x.toLowerCase()).includes(item.type.id.toLowerCase()); | ||
} | ||
resolveContext(item) { | ||
return this.relatedContexts({ item, filter: { type: __classPrivateFieldGet(this, _ContextProvider_contextType, "f") } }).pipe(map((x) => x.filter((item) => this.validateContext(item))), map((values) => { | ||
const value = values.shift(); | ||
if (!value) { | ||
throw Error('failed to resolve context'); | ||
} | ||
if (values.length) { | ||
console.warn('ContextProvider::relatedContext', 'multiple items found 🤣', values); | ||
} | ||
return value; | ||
})); | ||
} | ||
resolveContextAsync(item) { | ||
return lastValueFrom(this.resolveContext(item)); | ||
} | ||
relatedContexts(args) { | ||
if (!__classPrivateFieldGet(this, _ContextProvider_contextRelated, "f")) { | ||
return throwError(() => Error('ContextProvider::relatedContexts - no client defined for resolving related context')); | ||
} | ||
return __classPrivateFieldGet(this, _ContextProvider_contextRelated, "f").query(args).pipe(map(({ value }) => value)); | ||
} | ||
relatedContextsAsync(args) { | ||
return lastValueFrom(this.relatedContexts(args)); | ||
} | ||
clearCurrentContext() { | ||
this.currentContext = undefined; | ||
__classPrivateFieldGet(this, _ContextProvider_contextClient, "f").setCurrentContext(undefined); | ||
} | ||
@@ -110,4 +214,4 @@ dispose() { | ||
} | ||
_ContextProvider_contextClient = new WeakMap(), _ContextProvider_contextQuery = new WeakMap(), _ContextProvider_event = new WeakMap(), _ContextProvider_subscriptions = new WeakMap(), _ContextProvider_contextType = new WeakMap(), _ContextProvider_contextFilter = new WeakMap(), _ContextProvider_contextParameterFn = new WeakMap(); | ||
_ContextProvider_contextClient = new WeakMap(), _ContextProvider_contextQuery = new WeakMap(), _ContextProvider_contextRelated = new WeakMap(), _ContextProvider_event = new WeakMap(), _ContextProvider_subscriptions = new WeakMap(), _ContextProvider_contextType = new WeakMap(), _ContextProvider_contextFilter = new WeakMap(), _ContextProvider_contextParameterFn = new WeakMap(); | ||
export default ContextProvider; | ||
//# sourceMappingURL=ContextProvider.js.map |
@@ -40,2 +40,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
}); | ||
export const relatedContextSelector = (response) => __awaiter(void 0, void 0, void 0, function* () { | ||
const result = (yield response.json()); | ||
return result.map(parseContextItem); | ||
}); | ||
//# sourceMappingURL=selectors.js.map |
import { ModuleInitializerArgs } from '@equinor/fusion-framework-module'; | ||
import { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services'; | ||
import { QueryCtorOptions } from '@equinor/fusion-query'; | ||
import { ContextFilterFn, ContextItem, QueryContextParameters } from './types'; | ||
import { ContextFilterFn, ContextItem, QueryContextParameters, RelatedContextParameters } from './types'; | ||
import { GetContextParameters } from './client/ContextClient'; | ||
import { ContextConfigBuilderCallback } from './ContextConfigBuilder'; | ||
import { IContextProvider } from 'ContextProvider'; | ||
export interface ContextModuleConfig { | ||
@@ -11,2 +12,3 @@ client: { | ||
query: QueryCtorOptions<ContextItem[], QueryContextParameters>; | ||
related?: QueryCtorOptions<ContextItem[], RelatedContextParameters>; | ||
}; | ||
@@ -19,2 +21,4 @@ contextType?: string[]; | ||
}) => string | QueryContextParameters; | ||
resolveContext?: (this: IContextProvider, item: ContextItem | null) => ReturnType<IContextProvider['resolveContext']>; | ||
validateContext?: (this: IContextProvider, item: ContextItem | null) => ReturnType<IContextProvider['validateContext']>; | ||
} | ||
@@ -21,0 +25,0 @@ export interface IContextModuleConfigurator { |
@@ -5,3 +5,3 @@ import type { AnyModule, ModuleInitializerArgs, Modules, ModuleType } from '@equinor/fusion-framework-module'; | ||
import type { ContextModuleConfig, ContextModuleConfigurator, IContextModuleConfigurator } from './configurator'; | ||
import type { ContextItem, QueryContextParameters } from './types'; | ||
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types'; | ||
export type ContextConfigBuilderCallback = <TDeps extends Array<AnyModule> = []>(builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>) => void | Promise<void>; | ||
@@ -17,6 +17,9 @@ export declare class ContextConfigBuilder<TModules extends Array<AnyModule> = [], TInit extends ModuleInitializerArgs<any, any> = ModuleInitializerArgs<ContextModuleConfigurator, TModules>> { | ||
setContextParameterFn(fn: ContextModuleConfig['contextParameterFn']): void; | ||
setValidateContext(fn: ContextModuleConfig['validateContext']): void; | ||
setResolveContext(fn: ContextModuleConfig['resolveContext']): void; | ||
setContextClient(client: { | ||
get: QueryFn<ContextItem, GetContextParameters> | QueryCtorOptions<ContextItem, GetContextParameters>; | ||
query: QueryFn<ContextItem[], QueryContextParameters> | QueryCtorOptions<ContextItem[], QueryContextParameters>; | ||
related?: QueryFn<ContextItem[], RelatedContextParameters> | QueryCtorOptions<ContextItem[], RelatedContextParameters>; | ||
}, expire?: number): void; | ||
} |
import { Observable } from 'rxjs'; | ||
import { ContextModuleConfig } from './configurator'; | ||
import { ContextClient } from './client/ContextClient'; | ||
import { ContextItem, QueryContextParameters } from './types'; | ||
import { ContextItem, QueryContextParameters, RelatedContextParameters } from './types'; | ||
import { ModuleType } from '@equinor/fusion-framework-module'; | ||
@@ -15,2 +15,7 @@ import { EventModule, FrameworkEvent, FrameworkEventInit } from '@equinor/fusion-framework-module-event'; | ||
queryContextAsync(search: string): Promise<Array<ContextItem>>; | ||
validateContext(item: ContextItem<Record<string, unknown>>): boolean; | ||
resolveContext: (current: ContextItem) => Observable<ContextItem>; | ||
resolveContextAsync: (current: ContextItem) => Promise<ContextItem>; | ||
relatedContexts: (args: RelatedContextParameters) => Observable<Array<ContextItem<Record<string, unknown>>>>; | ||
relatedContextsAsync: (args: RelatedContextParameters) => Promise<Array<ContextItem<Record<string, unknown>>>>; | ||
clearCurrentContext: VoidFunction; | ||
@@ -30,4 +35,13 @@ } | ||
}); | ||
setCurrentContext(context: ContextItem<Record<string, unknown>>, opt?: { | ||
validate?: boolean; | ||
resolve?: boolean; | ||
}): Promise<ContextItem<Record<string, unknown>>>; | ||
queryContext(search: string): Observable<Array<ContextItem>>; | ||
queryContextAsync(search: string): Promise<Array<ContextItem>>; | ||
validateContext(item: ContextItem<Record<string, unknown>>): boolean; | ||
resolveContext(item: ContextItem<Record<string, unknown>>): Observable<ContextItem<Record<string, unknown>>>; | ||
resolveContextAsync(item: ContextItem<Record<string, unknown>>): Promise<ContextItem<Record<string, unknown>>>; | ||
relatedContexts(args: RelatedContextParameters): Observable<Array<ContextItem<Record<string, unknown>>>>; | ||
relatedContextsAsync(args: RelatedContextParameters): Promise<Array<ContextItem<Record<string, unknown>>>>; | ||
clearCurrentContext(): void; | ||
@@ -46,3 +60,13 @@ dispose(): void; | ||
}, IContextProvider>>; | ||
onParentContextChanged: FrameworkEvent<FrameworkEventInit<{ | ||
context: ContextItem | undefined; | ||
}, IContextProvider>>; | ||
onSetContextResolve: FrameworkEvent<FrameworkEventInit<{ | ||
context: ContextItem; | ||
}, IContextProvider>>; | ||
onSetContextResolved: FrameworkEvent<FrameworkEventInit<{ | ||
input: ContextItem; | ||
result?: ContextItem | null; | ||
}, IContextProvider>>; | ||
} | ||
} |
import type { ContextItem } from './types'; | ||
export declare const getContextSelector: (response: Response) => Promise<ContextItem>; | ||
export declare const queryContextSelector: (response: Response) => Promise<ContextItem[]>; | ||
export declare const relatedContextSelector: (response: Response) => Promise<ContextItem[]>; |
@@ -26,2 +26,8 @@ export type ContextItem<TType extends Record<string, unknown> = Record<string, unknown>> = { | ||
}; | ||
export type RelatedContextParameters = { | ||
item: ContextItem; | ||
filter?: { | ||
type?: string[]; | ||
}; | ||
}; | ||
export type ContextFilterFn = (items: ContextItem[]) => ContextItem[]; |
{ | ||
"name": "@equinor/fusion-framework-module-context", | ||
"version": "2.0.15", | ||
"version": "3.0.0", | ||
"description": "", | ||
@@ -34,9 +34,9 @@ "main": "./dist/esm/index.js", | ||
"dependencies": { | ||
"@equinor/fusion-query": "^2.0.7", | ||
"@equinor/fusion-query": "^3.0.0", | ||
"fast-deep-equal": "^3.1.3" | ||
}, | ||
"devDependencies": { | ||
"@equinor/fusion-framework-module": "^2.0.1", | ||
"@equinor/fusion-framework-module-event": "^2.0.1", | ||
"@equinor/fusion-framework-module-services": "^2.6.0", | ||
"@equinor/fusion-framework-module": "^3.0.0", | ||
"@equinor/fusion-framework-module-event": "^3.0.0", | ||
"@equinor/fusion-framework-module-services": "^3.0.0", | ||
"rxjs": "^7.5.7" | ||
@@ -48,3 +48,3 @@ }, | ||
}, | ||
"gitHead": "58e2b934160c8bfe90be1e8de7706bf1d339e303" | ||
"gitHead": "3462d21feac22290eec2dce3b00602da22a7a74d" | ||
} |
import { ModuleInitializerArgs, ModulesInstanceType } from '@equinor/fusion-framework-module'; | ||
import { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services'; | ||
import { getContextSelector, queryContextSelector } from './selectors'; | ||
import { getContextSelector, queryContextSelector, relatedContextSelector } from './selectors'; | ||
import { QueryCtorOptions } from '@equinor/fusion-query'; | ||
import { ContextFilterFn, ContextItem, QueryContextParameters } from './types'; | ||
import { | ||
ContextFilterFn, | ||
ContextItem, | ||
QueryContextParameters, | ||
RelatedContextParameters, | ||
} from './types'; | ||
import { GetContextParameters } from './client/ContextClient'; | ||
import { ContextConfigBuilder, ContextConfigBuilderCallback } from './ContextConfigBuilder'; | ||
import { IContextProvider } from 'ContextProvider'; | ||
@@ -13,2 +19,3 @@ export interface ContextModuleConfig { | ||
query: QueryCtorOptions<ContextItem[], QueryContextParameters>; | ||
related?: QueryCtorOptions<ContextItem[], RelatedContextParameters>; | ||
}; | ||
@@ -25,2 +32,12 @@ contextType?: string[]; | ||
}) => string | QueryContextParameters; | ||
resolveContext?: ( | ||
this: IContextProvider, | ||
item: ContextItem | null | ||
) => ReturnType<IContextProvider['resolveContext']>; | ||
validateContext?: ( | ||
this: IContextProvider, | ||
item: ContextItem | null | ||
) => ReturnType<IContextProvider['validateContext']>; | ||
} | ||
@@ -92,2 +109,16 @@ | ||
}, | ||
related: { | ||
client: { | ||
fn: (args) => { | ||
return contextClient.related( | ||
'v1', | ||
{ id: args.item.id, query: { filter: args.filter } }, | ||
{ selector: relatedContextSelector } | ||
); | ||
}, | ||
}, | ||
// TODO - might cast to checksum | ||
key: (args) => JSON.stringify(args), | ||
expire: this.defaultExpireTime, | ||
}, | ||
}; | ||
@@ -94,0 +125,0 @@ })(); |
@@ -18,3 +18,3 @@ import type { | ||
import type { ContextItem, QueryContextParameters } from './types'; | ||
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types'; | ||
@@ -61,2 +61,10 @@ export type ContextConfigBuilderCallback = <TDeps extends Array<AnyModule> = []>( | ||
setValidateContext(fn: ContextModuleConfig['validateContext']) { | ||
this.config.validateContext = fn; | ||
} | ||
setResolveContext(fn: ContextModuleConfig['resolveContext']) { | ||
this.config.resolveContext = fn; | ||
} | ||
setContextClient( | ||
@@ -70,2 +78,5 @@ client: { | ||
| QueryCtorOptions<ContextItem[], QueryContextParameters>; | ||
related?: | ||
| QueryFn<ContextItem[], RelatedContextParameters> | ||
| QueryCtorOptions<ContextItem[], RelatedContextParameters>; | ||
}, | ||
@@ -97,3 +108,17 @@ expire = 1 * 60 * 1000 | ||
}; | ||
if (client.related) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
this.config.client!.related = | ||
typeof client.related === 'function' | ||
? { | ||
// TODO - might cast to checksum | ||
key: (args) => JSON.stringify(args), | ||
client: { | ||
fn: client.related, | ||
}, | ||
expire, | ||
} | ||
: client.related; | ||
} | ||
} | ||
} |
@@ -1,3 +0,3 @@ | ||
import { lastValueFrom, Observable, Subscription } from 'rxjs'; | ||
import { map, pairwise } from 'rxjs/operators'; | ||
import { lastValueFrom, Observable, Subscription, throwError } from 'rxjs'; | ||
import { filter, map, pairwise, switchMap } from 'rxjs/operators'; | ||
@@ -7,3 +7,3 @@ import { ContextModuleConfig } from './configurator'; | ||
import { ContextClient } from './client/ContextClient'; | ||
import { ContextItem, QueryContextParameters } from './types'; | ||
import { ContextItem, QueryContextParameters, RelatedContextParameters } from './types'; | ||
import { ModuleType } from '@equinor/fusion-framework-module'; | ||
@@ -31,2 +31,11 @@ import { | ||
queryContextAsync(search: string): Promise<Array<ContextItem>>; | ||
validateContext(item: ContextItem<Record<string, unknown>>): boolean; | ||
resolveContext: (current: ContextItem) => Observable<ContextItem>; | ||
resolveContextAsync: (current: ContextItem) => Promise<ContextItem>; | ||
relatedContexts: ( | ||
args: RelatedContextParameters | ||
) => Observable<Array<ContextItem<Record<string, unknown>>>>; | ||
relatedContextsAsync: ( | ||
args: RelatedContextParameters | ||
) => Promise<Array<ContextItem<Record<string, unknown>>>>; | ||
clearCurrentContext: VoidFunction; | ||
@@ -38,2 +47,3 @@ } | ||
#contextQuery: Query<Array<ContextItem>, QueryContextParameters>; | ||
#contextRelated?: Query<Array<ContextItem>, RelatedContextParameters>; | ||
@@ -65,20 +75,3 @@ #event?: ModuleType<EventModule>; | ||
set currentContext(context: ContextItem | undefined) { | ||
if (this.#event) { | ||
/** notify listeners that context is about to change */ | ||
this.#event | ||
.dispatchEvent('onCurrentContextChange', { | ||
source: this, | ||
canBubble: true, | ||
cancelable: true, | ||
detail: { context }, | ||
}) | ||
.then((e) => { | ||
/** check if setting context was prevented by listener */ | ||
if (!e.canceled) { | ||
this.#contextClient.setCurrentContext(context); | ||
} | ||
}); | ||
} else { | ||
this.#contextClient.setCurrentContext(context); | ||
} | ||
context ? this.setCurrentContext(context) : this.clearCurrentContext(); | ||
} | ||
@@ -93,2 +86,5 @@ | ||
config.resolveContext && (this.resolveContext = config.resolveContext?.bind(this)); | ||
config.validateContext && (this.validateContext = config.validateContext?.bind(this)); | ||
this.#contextType = config.contextType; | ||
@@ -99,2 +95,7 @@ this.#contextFilter = config.contextFilter; | ||
this.#contextQuery = new Query(config.client.query); | ||
if (config.client.related) { | ||
this.#contextRelated = new Query(config.client.related); | ||
} | ||
this.#contextParameterFn = | ||
@@ -136,5 +137,35 @@ config.contextParameterFn ?? | ||
this.#subscriptions.add( | ||
parentContext.contextClient.currentContext$.subscribe( | ||
(next) => (this.currentContext = next) | ||
) | ||
parentContext.contextClient.currentContext$ | ||
.pipe( | ||
switchMap(async (next) => { | ||
if (next) { | ||
const onParentContextChanged = await this.#event?.dispatchEvent( | ||
'onParentContextChanged', | ||
{ | ||
source: this, | ||
detail: next, | ||
cancelable: true, | ||
} | ||
); | ||
return { next, canceled: onParentContextChanged?.canceled }; | ||
} | ||
return { next }; | ||
}), | ||
filter((x) => !x.canceled), | ||
map(({ next }) => next) | ||
) | ||
.subscribe((next) => { | ||
if (next) { | ||
try { | ||
this.setCurrentContext(next, { | ||
validate: true, | ||
resolve: true, | ||
}); | ||
} catch (err) { | ||
console.warn('ContextProvider::onParentContextChanged', err); | ||
} | ||
} else { | ||
this.clearCurrentContext(); | ||
} | ||
}) | ||
); | ||
@@ -144,2 +175,63 @@ } | ||
public async setCurrentContext( | ||
context: ContextItem<Record<string, unknown>>, | ||
opt?: { validate?: boolean; resolve?: boolean } | ||
): Promise<ContextItem<Record<string, unknown>>> { | ||
if (context === this.currentContext) { | ||
return context; | ||
} | ||
if (opt?.validate && !this.validateContext(context)) { | ||
if (opt.resolve) { | ||
/** notify listeners about to resolve invalid context */ | ||
const onSetContextResolve = await this.#event?.dispatchEvent( | ||
'onSetContextResolve', | ||
{ | ||
source: this, | ||
cancelable: true, | ||
detail: { context }, | ||
} | ||
); | ||
if (onSetContextResolve?.canceled) { | ||
throw Error('resolving of context was canceled'); | ||
} | ||
try { | ||
const resolvedContext = await this.resolveContextAsync(context); | ||
/** notify listeners about to resolved invalid context */ | ||
const onSetContextResolved = await this.#event?.dispatchEvent( | ||
'onSetContextResolved', | ||
{ | ||
source: this, | ||
cancelable: true, | ||
detail: { input: context, result: resolvedContext }, | ||
} | ||
); | ||
if (onSetContextResolved?.canceled) { | ||
throw Error('resolving of context was canceled'); | ||
} | ||
return this.setCurrentContext(resolvedContext); | ||
} catch (err) { | ||
console.error('failed to resolve context', context, err); | ||
this.clearCurrentContext(); | ||
} | ||
} | ||
throw Error('failed to validate provided context'); | ||
} | ||
const onCurrentContextChange = await this.#event?.dispatchEvent('onCurrentContextChange', { | ||
source: this, | ||
canBubble: true, | ||
cancelable: true, | ||
detail: { context: context }, | ||
}); | ||
if (onCurrentContextChange?.canceled) { | ||
throw Error('change of context was aborted'); | ||
} | ||
this.#contextClient.setCurrentContext(context); | ||
return context; | ||
} | ||
public queryContext(search: string): Observable<Array<ContextItem>> { | ||
@@ -162,4 +254,56 @@ const query$ = this.queryClient | ||
public validateContext(item: ContextItem<Record<string, unknown>>): boolean { | ||
if (!this.#contextType) return true; | ||
return this.#contextType.map((x) => x.toLowerCase()).includes(item.type.id.toLowerCase()); | ||
} | ||
public resolveContext( | ||
item: ContextItem<Record<string, unknown>> | ||
): Observable<ContextItem<Record<string, unknown>>> { | ||
return this.relatedContexts({ item, filter: { type: this.#contextType } }).pipe( | ||
map((x) => x.filter((item) => this.validateContext(item))), | ||
map((values) => { | ||
const value = values.shift(); | ||
if (!value) { | ||
throw Error('failed to resolve context'); | ||
} | ||
if (values.length) { | ||
console.warn( | ||
'ContextProvider::relatedContext', | ||
'multiple items found 🤣', | ||
values | ||
); | ||
} | ||
return value; | ||
}) | ||
); | ||
} | ||
public resolveContextAsync( | ||
item: ContextItem<Record<string, unknown>> | ||
): Promise<ContextItem<Record<string, unknown>>> { | ||
return lastValueFrom(this.resolveContext(item)); | ||
} | ||
public relatedContexts( | ||
args: RelatedContextParameters | ||
): Observable<Array<ContextItem<Record<string, unknown>>>> { | ||
if (!this.#contextRelated) { | ||
return throwError(() => | ||
Error( | ||
'ContextProvider::relatedContexts - no client defined for resolving related context' | ||
) | ||
); | ||
} | ||
return this.#contextRelated.query(args).pipe(map(({ value }) => value)); | ||
} | ||
public relatedContextsAsync( | ||
args: RelatedContextParameters | ||
): Promise<Array<ContextItem<Record<string, unknown>>>> { | ||
return lastValueFrom(this.relatedContexts(args)); | ||
} | ||
public clearCurrentContext() { | ||
this.currentContext = undefined; | ||
this.#contextClient.setCurrentContext(undefined); | ||
} | ||
@@ -195,3 +339,29 @@ | ||
>; | ||
onParentContextChanged: FrameworkEvent< | ||
FrameworkEventInit< | ||
{ | ||
context: ContextItem | undefined; | ||
}, | ||
IContextProvider | ||
> | ||
>; | ||
onSetContextResolve: FrameworkEvent< | ||
FrameworkEventInit< | ||
{ | ||
context: ContextItem; | ||
}, | ||
IContextProvider | ||
> | ||
>; | ||
onSetContextResolved: FrameworkEvent< | ||
FrameworkEventInit< | ||
{ | ||
input: ContextItem; | ||
result?: ContextItem | null; | ||
}, | ||
IContextProvider | ||
> | ||
>; | ||
} | ||
} |
import { ApiVersion, ApiContextEntity } from '@equinor/fusion-framework-module-services/context'; | ||
import type { GetContextResponse } from '@equinor/fusion-framework-module-services/context/get'; | ||
import type { QueryContextResponse } from '@equinor/fusion-framework-module-services/context/query'; | ||
import type { RelatedContextResponse } from '@equinor/fusion-framework-module-services/context/related'; | ||
import type { ContextItem, ContextItemType } from './types'; | ||
@@ -36,1 +37,6 @@ | ||
}; | ||
export const relatedContextSelector = async (response: Response): Promise<ContextItem[]> => { | ||
const result = (await response.json()) as RelatedContextResponse<'v1'>; | ||
return result.map(parseContextItem); | ||
}; |
@@ -29,2 +29,4 @@ export type ContextItem<TType extends Record<string, unknown> = Record<string, unknown>> = { | ||
export type RelatedContextParameters = { item: ContextItem; filter?: { type?: string[] } }; | ||
export type ContextFilterFn = (items: ContextItem[]) => ContextItem[]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
230500
1447
+ Added@equinor/fusion-observable@8.4.3(transitive)
+ Added@equinor/fusion-query@3.0.7(transitive)
+ Addeduuid@11.0.59.0.1(transitive)
- Removed@equinor/fusion-observable@7.0.3(transitive)
- Removed@equinor/fusion-query@2.0.7(transitive)
- Removeduuid@8.3.2(transitive)
Updated@equinor/fusion-query@^3.0.0