@equinor/fusion-framework-module-app
Advanced tools
Comparing version 5.3.11 to 5.3.12-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db
# Change Log | ||
## 5.3.12-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db | ||
### Patch Changes | ||
- [#2178](https://github.com/equinor/fusion-framework/pull/2178) [`20bf065`](https://github.com/equinor/fusion-framework/commit/20bf0650f0d578683f2310b6d96c4ce0be96b7db) Thanks [@eikeland](https://github.com/eikeland)! - TODO: write a proper changeset | ||
## 5.3.11 | ||
@@ -4,0 +10,0 @@ |
@@ -15,3 +15,3 @@ import { createAction, createAsyncAction, } from '@equinor/fusion-observable'; | ||
setConfig: createAction('set_config', (config) => ({ payload: config })), | ||
fetchConfig: createAsyncAction('fetch_config', (key) => ({ payload: key }), (config) => ({ payload: config }), (error) => ({ payload: error })), | ||
fetchConfig: createAsyncAction('fetch_config', (manifest) => ({ payload: manifest }), (config) => ({ payload: config }), (error) => ({ payload: error })), | ||
/** App loading */ | ||
@@ -18,0 +18,0 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any |
@@ -23,3 +23,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
import { Observable } from '@equinor/fusion-observable'; | ||
import { combineLatest, filter, firstValueFrom, lastValueFrom, map, Subscription, } from 'rxjs'; | ||
import { combineLatest, filter, firstValueFrom, lastValueFrom, map, of, Subscription, } from 'rxjs'; | ||
import { createState } from './create-state'; | ||
@@ -135,3 +135,8 @@ import { actions } from './actions'; | ||
loadConfig() { | ||
__classPrivateFieldGet(this, _App_state, "f").next(actions.fetchConfig(this.appKey)); | ||
// TODO - shit fix | ||
(this.manifest ? of(this.manifest) : this.getManifest()).subscribe({ | ||
next: (manifest) => { | ||
__classPrivateFieldGet(this, _App_state, "f").next(actions.fetchConfig(manifest)); | ||
}, | ||
}); | ||
} | ||
@@ -147,3 +152,8 @@ loadManifest(update) { | ||
const manifest = yield this.getManifestAsync(allow_cache); | ||
__classPrivateFieldGet(this, _App_state, "f").next(actions.importApp(manifest.entry)); | ||
if (manifest.build.entryPoint) { | ||
__classPrivateFieldGet(this, _App_state, "f").next(actions.importApp(manifest.build.entryPoint)); | ||
} | ||
else { | ||
console.log(`The ${manifest.key} is missing entryPoint, please upload a build for the app before continuing`); | ||
} | ||
}); | ||
@@ -270,4 +280,12 @@ } | ||
this.getManifest().subscribe((manifest) => { | ||
// dispatch import_app action to load the application script | ||
__classPrivateFieldGet(this, _App_state, "f").next(actions.importApp(manifest.entry)); | ||
var _a; | ||
if (manifest.build.entryPoint) { | ||
// TODO - this should come from backend | ||
const assetPath = (_a = manifest.build.assetPath) !== null && _a !== void 0 ? _a : [manifest.key, manifest.build.version].join('@'); | ||
// dispatch import_app action to load the application script | ||
__classPrivateFieldGet(this, _App_state, "f").next(actions.importApp([assetPath, manifest.build.entryPoint].join('/'))); | ||
} | ||
else { | ||
console.error(`The ${manifest.key} app is missing a entry in the manifest, upload a build for your app before continuing`); | ||
} | ||
})); | ||
@@ -274,0 +292,0 @@ }); |
@@ -8,4 +8,6 @@ import { getBaseType, createReducer as makeReducer, isCompleteAction, isRequestAction, } from '@equinor/fusion-observable'; | ||
.addCase(actions.setManifest, (state, action) => { | ||
var _a; | ||
// TODO: after legacy is removed, remove the update flag | ||
if (action.meta.update) { | ||
state.manifest = Object.assign(Object.assign({}, state.manifest), action.payload); | ||
state.manifest = Object.assign((_a = state.manifest) !== null && _a !== void 0 ? _a : {}, action.payload); | ||
} | ||
@@ -12,0 +14,0 @@ else { |
@@ -13,5 +13,5 @@ import { FlowSubject } from '@equinor/fusion-observable'; | ||
// add handler for loading application script | ||
state.addFlow(handleImportApplication()); | ||
state.addFlow(handleImportApplication(provider)); | ||
return state; | ||
}; | ||
//# sourceMappingURL=create-state.js.map |
@@ -41,5 +41,6 @@ import { from, of, concat } from 'rxjs'; | ||
// when request is received, abort any ongoing request and start new | ||
switchMap(({ payload: appKey }) => { | ||
// fetch manifest from provider | ||
const subject = from(provider.getAppConfig(appKey)).pipe( | ||
switchMap(({ payload }) => { | ||
// TODO - use the configUrl directly from the manifest | ||
// fetch config from provider | ||
const subject = from(provider.getAppConfig(payload.key, payload.version)).pipe( | ||
// filter out null values | ||
@@ -49,4 +50,4 @@ filter((x) => !!x), | ||
share()); | ||
// first load manifest and then dispatch success action | ||
return concat(subject.pipe(map((manifest) => actions.setConfig(manifest))), subject.pipe(last(), map((manifest) => actions.fetchConfig.success(manifest)))).pipe( | ||
// first load config and then dispatch success action | ||
return concat(subject.pipe(map((config) => actions.setConfig(config))), subject.pipe(last(), map((config) => actions.fetchConfig.success(config)))).pipe( | ||
// catch any error and dispatch failure action | ||
@@ -61,3 +62,3 @@ catchError((err) => { | ||
*/ | ||
export const handleImportApplication = () => (action$) => action$.pipe( | ||
export const handleImportApplication = (provider) => (action$) => action$.pipe( | ||
// only handle import script request actions | ||
@@ -67,4 +68,5 @@ filter(actions.importApp.match), | ||
switchMap(({ payload }) => { | ||
const endpoint = [provider.getBaseUri(), payload].join('/').replace(/\/{2,}/g, '/'); | ||
// dynamically import the application script | ||
return from(import(payload)).pipe( | ||
return from(import(/* @vite-ignore */ endpoint)).pipe( | ||
// dispatch success action | ||
@@ -71,0 +73,0 @@ map(actions.importApp.success), |
@@ -10,23 +10,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
}; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
var _AppConfigurator_configBuilders; | ||
import { AppConfigBuilder } from './AppConfigBuilder'; | ||
import { BaseConfigBuilder, } from '@equinor/fusion-framework-module'; | ||
import { moduleKey } from './module'; | ||
export class AppConfigurator { | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
import { map } from 'rxjs/operators'; | ||
export class AppConfigurator extends BaseConfigBuilder { | ||
constructor() { | ||
super(...arguments); | ||
this.defaultExpireTime = 1 * 60 * 1000; | ||
_AppConfigurator_configBuilders.set(this, []); | ||
} | ||
addConfigBuilder(init) { | ||
__classPrivateFieldGet(this, _AppConfigurator_configBuilders, "f").push(init); | ||
} | ||
setProxyPath(path) { | ||
this.addConfigBuilder((builder) => { | ||
builder.config.proxy = { path }; | ||
}); | ||
} | ||
/** | ||
@@ -47,22 +35,18 @@ * WARNING: this function will be remove in future | ||
/** resolve and create a client from discovery */ | ||
try { | ||
return yield serviceDiscovery.createClient('app'); | ||
} | ||
catch (_a) { | ||
return yield serviceDiscovery.createClient('portal'); | ||
} | ||
return yield serviceDiscovery.createClient('apps'); | ||
} | ||
}); | ||
} | ||
createConfig(init) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
const config = yield __classPrivateFieldGet(this, _AppConfigurator_configBuilders, "f").reduce((cur, cb) => __awaiter(this, void 0, void 0, function* () { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const builder = new AppConfigBuilder(init, yield cur); | ||
yield Promise.resolve(cb(builder)); | ||
return Object.assign(cur, builder.config); | ||
}), Promise.resolve({})); | ||
// TODO - make less lazy | ||
(_a = config.client) !== null && _a !== void 0 ? _a : (config.client = yield (() => __awaiter(this, void 0, void 0, function* () { | ||
setClient(client_or_cb) { | ||
const cb = typeof client_or_cb === 'object' ? () => client_or_cb : client_or_cb; | ||
this._set('client', cb); | ||
} | ||
// TODO - explain why, used in import of resources aka proxy url | ||
setBaseUri(base_or_cb) { | ||
const cb = typeof base_or_cb === 'string' ? () => __awaiter(this, void 0, void 0, function* () { return base_or_cb; }) : base_or_cb; | ||
this._set('baseUri', cb); | ||
} | ||
_createConfig(init, initial) { | ||
if (!this._has('client')) { | ||
this.setClient(() => __awaiter(this, void 0, void 0, function* () { | ||
const httpClient = yield this._createHttpClient(init); | ||
@@ -72,3 +56,9 @@ return { | ||
client: { | ||
fn: ({ appKey }) => httpClient.json$(`/api/apps/${appKey}`), | ||
fn: ({ appKey }) => httpClient | ||
.json$(`/apps/${appKey}`, { | ||
headers: { | ||
'Api-Version': '1.0', | ||
}, | ||
}) | ||
.pipe(map((apiApp) => new ApplicationManifest(apiApp))), | ||
}, | ||
@@ -80,6 +70,21 @@ key: ({ appKey }) => appKey, | ||
client: { | ||
fn: () => httpClient.json$(`/api/apps`), | ||
// TODO: add to config if use me or not | ||
fn: (filter) => { | ||
const path = (filter === null || filter === void 0 ? void 0 : filter.filterByCurrentUser) | ||
? '/persons/me/apps' | ||
: '/apps'; | ||
return httpClient | ||
.json$(path, { | ||
headers: { | ||
'Api-Version': '1.0', | ||
}, | ||
}) | ||
.pipe(map((x) => { | ||
const apps = x.value.map((apiApp) => new ApplicationManifest(apiApp)); | ||
return apps; | ||
})); | ||
}, | ||
}, | ||
// TODO - might cast to checksum | ||
key: () => 'manifests', | ||
key: (filter) => (filter ? JSON.stringify(filter) : 'all'), | ||
expire: this.defaultExpireTime, | ||
@@ -89,3 +94,7 @@ }, | ||
client: { | ||
fn: ({ appKey, tag }) => httpClient.json$(`/api/apps/${appKey}/config${tag ? `?tag=${tag}` : ''}`), | ||
fn: ({ appKey, tag = 'latest' }) => httpClient.json$(`/apps/${appKey}/builds/${tag}/config`, { | ||
headers: { | ||
'Api-Version': '1.0', | ||
}, | ||
}), | ||
}, | ||
@@ -96,8 +105,10 @@ key: (args) => JSON.stringify(args), | ||
}; | ||
}))()); | ||
return config; | ||
}); | ||
})); | ||
} | ||
if (!this._has('baseUri')) { | ||
this.setBaseUri('/apps-proxy'); | ||
} | ||
return super._createConfig(init, initial); | ||
} | ||
} | ||
_AppConfigurator_configBuilders = new WeakMap(); | ||
//# sourceMappingURL=AppConfigurator.js.map |
@@ -12,3 +12,3 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
}; | ||
var _AppModuleProvider_appsClient, _AppModuleProvider_configClient, _AppModuleProvider_current$, _AppModuleProvider_subscription, _AppModuleProvider_event; | ||
var _AppModuleProvider_appsClient, _AppModuleProvider_configClient, _AppModuleProvider_appBaseUri, _AppModuleProvider_current$, _AppModuleProvider_subscription, _AppModuleProvider_event; | ||
import { BehaviorSubject, catchError, distinctUntilChanged, map, pairwise, Subscription, takeWhile, } from 'rxjs'; | ||
@@ -42,5 +42,7 @@ import { HttpResponseError } from '@equinor/fusion-framework-module-http'; | ||
constructor(args) { | ||
var _a; | ||
_AppModuleProvider_appsClient.set(this, void 0); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
_AppModuleProvider_configClient.set(this, void 0); | ||
_AppModuleProvider_appBaseUri.set(this, void 0); | ||
_AppModuleProvider_current$.set(this, void 0); | ||
@@ -55,2 +57,3 @@ _AppModuleProvider_subscription.set(this, new Subscription()); | ||
__classPrivateFieldSet(this, _AppModuleProvider_configClient, new Query(config.client.getAppConfig), "f"); | ||
__classPrivateFieldSet(this, _AppModuleProvider_appBaseUri, (_a = config.baseUri) !== null && _a !== void 0 ? _a : '', "f"); | ||
__classPrivateFieldGet(this, _AppModuleProvider_subscription, "f").add(() => this.appClient.complete()); | ||
@@ -88,7 +91,11 @@ __classPrivateFieldGet(this, _AppModuleProvider_subscription, "f").add(() => __classPrivateFieldGet(this, _AppModuleProvider_appsClient, "f").complete()); | ||
} | ||
getAppManifests(filter) { | ||
return Query.extractQueryValue(__classPrivateFieldGet(this, _AppModuleProvider_appsClient, "f").query(filter)); | ||
} | ||
/** | ||
* fetch all applications | ||
* @deprecated use `getAppManifests` instead | ||
*/ | ||
getAllAppManifests() { | ||
return Query.extractQueryValue(__classPrivateFieldGet(this, _AppModuleProvider_appsClient, "f").query()); | ||
return this.getAppManifests(); | ||
} | ||
@@ -123,2 +130,5 @@ /** | ||
} | ||
getBaseUri() { | ||
return __classPrivateFieldGet(this, _AppModuleProvider_appBaseUri, "f"); | ||
} | ||
/** | ||
@@ -136,4 +146,4 @@ * This should not be used, only for legacy creation backdoor | ||
} | ||
_AppModuleProvider_appsClient = new WeakMap(), _AppModuleProvider_configClient = new WeakMap(), _AppModuleProvider_current$ = new WeakMap(), _AppModuleProvider_subscription = new WeakMap(), _AppModuleProvider_event = new WeakMap(); | ||
_AppModuleProvider_appsClient = new WeakMap(), _AppModuleProvider_configClient = new WeakMap(), _AppModuleProvider_appBaseUri = new WeakMap(), _AppModuleProvider_current$ = new WeakMap(), _AppModuleProvider_subscription = new WeakMap(), _AppModuleProvider_event = new WeakMap(); | ||
export default AppModuleProvider; | ||
//# sourceMappingURL=AppModuleProvider.js.map |
@@ -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()); | ||
}); | ||
}; | ||
import { module } from './module'; | ||
@@ -8,10 +17,12 @@ /** | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
configurator, builder) => { | ||
configurator, callback) => { | ||
configurator.addConfig({ | ||
module, | ||
configure: (appConfigurator) => { | ||
builder && appConfigurator.addConfigBuilder(builder); | ||
}, | ||
configure: (configurator) => __awaiter(void 0, void 0, void 0, function* () { | ||
if (callback) { | ||
Promise.resolve(callback(configurator)); | ||
} | ||
}), | ||
}); | ||
}; | ||
//# sourceMappingURL=enable-app-module.js.map |
export { AppConfigurator, } from './AppConfigurator'; | ||
export { AppModuleProvider } from './AppModuleProvider'; | ||
export { ApplicationManifest } from './ApplicationManifest'; | ||
export * from './events'; | ||
@@ -4,0 +5,0 @@ export * from './types'; |
@@ -34,3 +34,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
initialize: (args) => __awaiter(void 0, void 0, void 0, function* () { | ||
const config = yield args.config.createConfig(args); | ||
const config = yield args.config.createConfigAsync(args); | ||
const event = yield args.requireInstance('event').catch(() => undefined); | ||
@@ -37,0 +37,0 @@ return new AppModuleProvider({ config, event }); |
import { ActionInstanceMap, ActionTypes } from '@equinor/fusion-observable'; | ||
import { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import { ApplicationManifest } from '../ApplicationManifest'; | ||
declare const createActions: () => { | ||
/** Manifest loading */ | ||
setManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest, update?: boolean | undefined], AppManifest, "set_manifest", never, { | ||
setManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: ApplicationManifest, update?: boolean | undefined], ApplicationManifest, "set_manifest", never, { | ||
created: number; | ||
@@ -12,3 +13,3 @@ update: boolean | undefined; | ||
}> & { | ||
success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest], AppManifest, "fetch_manifest::success", never, never>; | ||
success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: ApplicationManifest], ApplicationManifest, "fetch_manifest::success", never, never>; | ||
failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_manifest::failure", never, never>; | ||
@@ -18,3 +19,3 @@ }; | ||
setConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[config: AppConfig], AppConfig, "set_config", never, never>; | ||
fetchConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[key: string], string, "fetch_config::request", never, never> & { | ||
fetchConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest], AppManifest, "fetch_config::request", never, never> & { | ||
success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[config: AppConfig], AppConfig, "fetch_config::success", never, never>; | ||
@@ -37,3 +38,3 @@ failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_config::failure", never, never>; | ||
/** Manifest loading */ | ||
setManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest, update?: boolean | undefined], AppManifest, "set_manifest", never, { | ||
setManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: ApplicationManifest, update?: boolean | undefined], ApplicationManifest, "set_manifest", never, { | ||
created: number; | ||
@@ -45,3 +46,3 @@ update: boolean | undefined; | ||
}> & { | ||
success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest], AppManifest, "fetch_manifest::success", never, never>; | ||
success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: ApplicationManifest], ApplicationManifest, "fetch_manifest::success", never, never>; | ||
failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_manifest::failure", never, never>; | ||
@@ -51,3 +52,3 @@ }; | ||
setConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[config: AppConfig], AppConfig, "set_config", never, never>; | ||
fetchConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[key: string], string, "fetch_config::request", never, never> & { | ||
fetchConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest], AppManifest, "fetch_config::request", never, never> & { | ||
success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[config: AppConfig], AppConfig, "fetch_config::success", never, never>; | ||
@@ -54,0 +55,0 @@ failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_config::failure", never, never>; |
@@ -1,2 +0,2 @@ | ||
import { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import type { AppConfig, AppModulesInstance, AppScriptModule } from '../types'; | ||
import { Observable } from '@equinor/fusion-observable'; | ||
@@ -9,2 +9,3 @@ import type { AppModuleProvider } from '../AppModuleProvider'; | ||
import './events'; | ||
import { ApplicationManifest } from '../ApplicationManifest'; | ||
export declare function filterEmpty<T>(): OperatorFunction<T | null | undefined, T>; | ||
@@ -21,3 +22,3 @@ /** | ||
*/ | ||
get manifest$(): Observable<AppManifest>; | ||
get manifest$(): Observable<ApplicationManifest>; | ||
/** | ||
@@ -52,3 +53,3 @@ * Observable that emits the configuration of the app. | ||
*/ | ||
get manifest(): AppManifest | undefined; | ||
get manifest(): Readonly<ApplicationManifest> | undefined; | ||
/** | ||
@@ -58,3 +59,3 @@ * Retrieves the manifest asynchronously. | ||
*/ | ||
get manifestAsync(): Promise<AppManifest>; | ||
get manifestAsync(): Promise<Readonly<ApplicationManifest>>; | ||
/** | ||
@@ -86,3 +87,3 @@ * Gets the configuration of the app. | ||
initialize(): Observable<{ | ||
manifest: AppManifest; | ||
manifest: ApplicationManifest; | ||
script: AppScriptModule; | ||
@@ -121,3 +122,3 @@ config: AppConfig; | ||
*/ | ||
getManifest(force_refresh?: boolean): Observable<AppManifest>; | ||
getManifest(force_refresh?: boolean): Observable<ApplicationManifest>; | ||
/** | ||
@@ -128,3 +129,3 @@ * Retrieves the app manifest asynchronously. | ||
*/ | ||
getManifestAsync(allow_cache?: boolean): Promise<AppManifest>; | ||
getManifestAsync(allow_cache?: boolean): Promise<ApplicationManifest>; | ||
/** | ||
@@ -145,3 +146,3 @@ * Gets the app module. | ||
#private; | ||
get manifest$(): Observable<AppManifest>; | ||
get manifest$(): Observable<ApplicationManifest>; | ||
get config$(): Observable<AppConfig<TEnv>>; | ||
@@ -152,4 +153,4 @@ get modules$(): Observable<AppScriptModule>; | ||
get appKey(): string; | ||
get manifest(): Readonly<AppManifest> | undefined; | ||
get manifestAsync(): Promise<Readonly<AppManifest>>; | ||
get manifest(): Readonly<ApplicationManifest> | undefined; | ||
get manifestAsync(): Promise<Readonly<ApplicationManifest>>; | ||
get config(): Readonly<AppConfig<TEnv>> | undefined; | ||
@@ -163,3 +164,3 @@ get configAsync(): Promise<Readonly<AppConfig<TEnv>>>; | ||
initialize(): Observable<{ | ||
manifest: AppManifest; | ||
manifest: ApplicationManifest; | ||
script: AppScriptModule; | ||
@@ -170,8 +171,8 @@ config: AppConfig; | ||
loadManifest(update?: boolean): void; | ||
updateManifest(manifest: AppManifest, replace?: false): void; | ||
updateManifest(manifest: ApplicationManifest, replace?: false): void; | ||
loadAppModule(allow_cache?: boolean): Promise<void>; | ||
getConfig(force_refresh?: boolean): Observable<AppConfig>; | ||
getConfigAsync(allow_cache?: boolean): Promise<AppConfig>; | ||
getManifest(force_refresh?: boolean): Observable<AppManifest>; | ||
getManifestAsync(allow_cache?: boolean): Promise<AppManifest>; | ||
getManifest(force_refresh?: boolean): Observable<ApplicationManifest>; | ||
getManifestAsync(allow_cache?: boolean): Promise<ApplicationManifest>; | ||
getAppModule(force_refresh?: boolean): Observable<AppScriptModule>; | ||
@@ -178,0 +179,0 @@ getAppModuleAsync(allow_cache?: boolean): Promise<AppScriptModule>; |
import { AppBundleState, AppBundleStateInitial } from './types'; | ||
export declare const createReducer: (value: AppBundleStateInitial) => import("@equinor/fusion-observable").ReducerWithInitialState<AppBundleState, import("@equinor/fusion-observable").PayloadAction<import("..").AppManifest, "set_manifest", { | ||
export declare const createReducer: (value: AppBundleStateInitial) => import("@equinor/fusion-observable").ReducerWithInitialState<AppBundleState, import("@equinor/fusion-observable").PayloadAction<import("..").ApplicationManifest, "set_manifest", { | ||
created: number; | ||
@@ -11,3 +11,3 @@ update: boolean | undefined; | ||
} | { | ||
payload: string; | ||
payload: import("..").AppManifest; | ||
type: "fetch_config::request"; | ||
@@ -27,3 +27,3 @@ } | { | ||
} | { | ||
payload: import("..").AppManifest; | ||
payload: import("..").ApplicationManifest; | ||
type: "fetch_manifest::success"; | ||
@@ -30,0 +30,0 @@ } | { |
@@ -25,2 +25,2 @@ import type { Flow } from '@equinor/fusion-observable'; | ||
*/ | ||
export declare const handleImportApplication: () => Flow<Actions, AppBundleState>; | ||
export declare const handleImportApplication: (provider: AppModuleProvider) => Flow<Actions, AppBundleState>; |
@@ -1,6 +0,7 @@ | ||
import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import type { AppConfig, AppModulesInstance, AppScriptModule } from '../types'; | ||
import { ApplicationManifest } from '../ApplicationManifest'; | ||
export type AppBundleState<TConfig = any, TModules = any> = { | ||
appKey: string; | ||
status: Set<string>; | ||
manifest?: AppManifest; | ||
manifest?: ApplicationManifest; | ||
config?: AppConfig<TConfig>; | ||
@@ -7,0 +8,0 @@ modules?: AppScriptModule; |
@@ -1,16 +0,15 @@ | ||
import type { ModuleInitializerArgs } from '@equinor/fusion-framework-module'; | ||
import { BaseConfigBuilder, ConfigBuilderCallback, type ModuleInitializerArgs } from '@equinor/fusion-framework-module'; | ||
import type { HttpModule, IHttpClient } from '@equinor/fusion-framework-module-http'; | ||
import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery'; | ||
import type { QueryCtorOptions } from '@equinor/fusion-query'; | ||
import { AppConfigBuilderCallback } from './AppConfigBuilder'; | ||
import type { AppConfig, AppManifest } from './types'; | ||
import type { AppConfig } from './types'; | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
export interface AppModuleConfig { | ||
proxy: { | ||
path: string; | ||
}; | ||
client: { | ||
getAppManifest: QueryCtorOptions<AppManifest, { | ||
getAppManifest: QueryCtorOptions<ApplicationManifest, { | ||
appKey: string; | ||
}>; | ||
getAppManifests: QueryCtorOptions<AppManifest[], void>; | ||
getAppManifests: QueryCtorOptions<ApplicationManifest[], { | ||
filterByCurrentUser?: boolean; | ||
} | undefined>; | ||
getAppConfig: QueryCtorOptions<AppConfig, { | ||
@@ -21,11 +20,10 @@ appKey: string; | ||
}; | ||
baseUri?: string; | ||
} | ||
export interface IAppConfigurator { | ||
addConfigBuilder: (init: AppConfigBuilderCallback) => void; | ||
setClient: (client_or_cb: Promise<AppModuleConfig['client']> | ConfigBuilderCallback<AppModuleConfig['client']>) => void; | ||
setBaseUri: (base_or_cb: string | ConfigBuilderCallback<string>) => void; | ||
} | ||
export declare class AppConfigurator implements IAppConfigurator { | ||
#private; | ||
export declare class AppConfigurator extends BaseConfigBuilder<AppModuleConfig> implements IAppConfigurator { | ||
defaultExpireTime: number; | ||
addConfigBuilder(init: AppConfigBuilderCallback): void; | ||
setProxyPath(path: string): void; | ||
/** | ||
@@ -35,3 +33,5 @@ * WARNING: this function will be remove in future | ||
protected _createHttpClient(init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>): Promise<IHttpClient>; | ||
createConfig(init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>): Promise<AppModuleConfig>; | ||
setClient(client_or_cb: Promise<AppModuleConfig['client']> | ConfigBuilderCallback<AppModuleConfig['client']>): void; | ||
setBaseUri(base_or_cb: string | ConfigBuilderCallback<string>): void; | ||
protected _createConfig(init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>, initial?: Partial<AppModuleConfig>): import("rxjs").ObservableInput<AppModuleConfig>; | ||
} |
@@ -5,3 +5,4 @@ import { Observable } from 'rxjs'; | ||
import { Query } from '@equinor/fusion-query'; | ||
import type { AppConfig, AppManifest, CurrentApp } from './types'; | ||
import type { AppConfig, CurrentApp } from './types'; | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
import { App, IApp } from './app/App'; | ||
@@ -12,4 +13,4 @@ import { AppModuleConfig } from './AppConfigurator'; | ||
#private; | ||
static compareAppManifest<T extends AppManifest>(a?: T, b?: T): boolean; | ||
appClient: Query<AppManifest, { | ||
static compareAppManifest<T extends ApplicationManifest>(a?: T, b?: T): boolean; | ||
appClient: Query<ApplicationManifest, { | ||
appKey: string; | ||
@@ -34,7 +35,11 @@ }>; | ||
*/ | ||
getAppManifest(appKey: string): Observable<AppManifest>; | ||
getAppManifest(appKey: string): Observable<ApplicationManifest>; | ||
getAppManifests(filter?: { | ||
filterByCurrentUser: boolean; | ||
}): Observable<ApplicationManifest[]>; | ||
/** | ||
* fetch all applications | ||
* @deprecated use `getAppManifests` instead | ||
*/ | ||
getAllAppManifests(): Observable<AppManifest[]>; | ||
getAllAppManifests(): Observable<ApplicationManifest[]>; | ||
/** | ||
@@ -51,2 +56,3 @@ * fetch configuration for an application | ||
clearCurrentApp(): void; | ||
getBaseUri(): string; | ||
/** | ||
@@ -53,0 +59,0 @@ * This should not be used, only for legacy creation backdoor |
import type { IModulesConfigurator } from '@equinor/fusion-framework-module'; | ||
import type { AppConfigBuilderCallback } from './AppConfigBuilder'; | ||
import { AppConfigurator } from './AppConfigurator'; | ||
/** | ||
@@ -7,2 +7,2 @@ * Method for enabling the Service module | ||
*/ | ||
export declare const enableAppModule: (configurator: IModulesConfigurator<any, any>, builder?: AppConfigBuilderCallback) => void; | ||
export declare const enableAppModule: (configurator: IModulesConfigurator<any, any>, callback?: (builder: AppConfigurator) => void | Promise<void>) => void; |
export { AppModuleConfig, AppConfigurator, IAppConfigurator, AppModuleConfig as IAppModuleConfig, } from './AppConfigurator'; | ||
export { AppModuleProvider } from './AppModuleProvider'; | ||
export { IApp } from './app/App'; | ||
export { ApplicationManifest } from './ApplicationManifest'; | ||
export * from './events'; | ||
@@ -5,0 +6,0 @@ export * from './types'; |
import { Module } from '@equinor/fusion-framework-module'; | ||
import { ModuleDeps } from './types'; | ||
import { IAppConfigurator } from './AppConfigurator'; | ||
import { AppConfigurator } from './AppConfigurator'; | ||
import { AppModuleProvider } from './AppModuleProvider'; | ||
export declare const moduleKey = "app"; | ||
export type AppModule = Module<typeof moduleKey, AppModuleProvider, IAppConfigurator, ModuleDeps>; | ||
export type AppModule = Module<typeof moduleKey, AppModuleProvider, AppConfigurator, ModuleDeps>; | ||
/** | ||
@@ -8,0 +8,0 @@ * Represents a module for handling applications. |
@@ -7,6 +7,7 @@ import { AnyModule, CombinedModules, ModulesInstance } from '@equinor/fusion-framework-module'; | ||
import IApp from './app'; | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
type Fusion = any; | ||
export type AppEnv<TConfig = unknown, TProps = unknown> = { | ||
basename?: string; | ||
manifest?: AppManifest; | ||
manifest?: ApplicationManifest; | ||
config?: AppConfig<TConfig>; | ||
@@ -16,3 +17,3 @@ props?: TProps; | ||
export type ModuleDeps = [HttpModule, ServiceDiscoveryModule, EventModule]; | ||
export type AppType = 'standalone' | 'report' | 'launcher'; | ||
export type AppType = 'standalone' | 'report' | 'launcher' | 'template'; | ||
export type CurrentApp<TModules extends Array<AnyModule> = [], TEnv = any> = IApp<TEnv, TModules> | null | undefined; | ||
@@ -24,20 +25,65 @@ export type AppAuth = { | ||
type AppCategory = { | ||
id?: string; | ||
name: string | null; | ||
color: string | null; | ||
defaultIcon: string | null; | ||
id: string; | ||
name?: string | null; | ||
displayName?: string | null; | ||
color?: string | null; | ||
defaultIcon?: string | null; | ||
sortOrder: number; | ||
}; | ||
export type AppManifest = { | ||
type AppVisualization = { | ||
color?: string | null; | ||
icon?: string | null; | ||
sortOrder: number; | ||
}; | ||
export type AzureId = { | ||
azureId: string; | ||
}; | ||
export type AzureUniqueId = { | ||
azureUniqueId: string; | ||
}; | ||
export type AppBuild<TUploaderId = AzureId | AzureUniqueId> = { | ||
version?: string; | ||
entryPoint?: string; | ||
tags?: string[]; | ||
tag?: string; | ||
assetPath?: string; | ||
configUrl?: string; | ||
timestamp?: string; | ||
commitSha?: string; | ||
githubRepo?: string; | ||
projectPage?: string; | ||
uploadedBy?: { | ||
displayName?: string; | ||
mail?: string; | ||
upn?: string; | ||
accountType?: string; | ||
accountClassification?: string; | ||
} & TUploaderId; | ||
}; | ||
export type AppOwnerOrAdmin = { | ||
id: string; | ||
azureId?: string; | ||
azureUniqueId?: string; | ||
displayName?: string; | ||
mail?: string; | ||
upn?: string; | ||
accountType?: string; | ||
accountClassification?: string; | ||
}; | ||
export interface AppManifest { | ||
key: string; | ||
name: string; | ||
entry: string; | ||
version: string; | ||
name?: string; | ||
entry?: string; | ||
version?: string; | ||
shortName?: string; | ||
description?: string; | ||
keywords?: string[]; | ||
type?: AppType; | ||
isPinned?: boolean; | ||
tags?: string[]; | ||
auth?: AppAuth[]; | ||
tag?: string; | ||
auth?: AppOwnerOrAdmin[]; | ||
icon?: string; | ||
order?: number; | ||
publishedDate?: Date; | ||
publishedDate?: string; | ||
accentColor?: string; | ||
@@ -47,11 +93,14 @@ categoryId?: string; | ||
hide?: boolean; | ||
}; | ||
visualization?: AppVisualization; | ||
admins?: AppOwnerOrAdmin[]; | ||
owners?: AppOwnerOrAdmin[]; | ||
build?: AppBuild<AzureId>; | ||
} | ||
export type Endpoint = { | ||
name: string; | ||
uri: string; | ||
url: string; | ||
scopes?: string[]; | ||
}; | ||
export type AppConfig<TEnvironment = unknown> = { | ||
environment: TEnvironment; | ||
endpoints: Record<string, string | Endpoint>; | ||
environment?: TEnvironment; | ||
endpoints?: Record<string, Endpoint>; | ||
}; | ||
@@ -63,3 +112,3 @@ /** | ||
export type AppBundle<TEnvironment = unknown, TModule = unknown> = { | ||
manifest: AppManifest; | ||
manifest: ApplicationManifest; | ||
config: AppConfig<TEnvironment>; | ||
@@ -82,2 +131,27 @@ module: TModule; | ||
export type AppModulesInstance<TModules extends Array<AnyModule> | unknown = unknown> = ModulesInstance<AppModules<TModules>>; | ||
export type ApiAppVersionConfig = { | ||
environment: string; | ||
endpoints: Record<string, { | ||
url: string; | ||
scopes: string[]; | ||
}>; | ||
}; | ||
export type ApiApp = { | ||
appKey: string; | ||
displayName?: string; | ||
description?: string; | ||
type?: AppType; | ||
isPinned?: boolean; | ||
templateSource?: string; | ||
category?: AppCategory; | ||
visualization: { | ||
color: string; | ||
icon: string; | ||
sortOrder: number; | ||
}; | ||
keywords: string[]; | ||
admins: AppOwnerOrAdmin[]; | ||
owners: AppOwnerOrAdmin[]; | ||
build: AppBuild<AzureUniqueId>; | ||
}; | ||
export {}; |
{ | ||
"name": "@equinor/fusion-framework-module-app", | ||
"version": "5.3.11", | ||
"version": "5.3.12-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db", | ||
"description": "", | ||
@@ -56,7 +56,7 @@ "main": "dist/esm/index.js", | ||
"typescript": "^5.5.4", | ||
"@equinor/fusion-framework-module": "^4.3.4", | ||
"@equinor/fusion-framework-module-event": "^4.2.3", | ||
"@equinor/fusion-framework-module-http": "^6.0.3", | ||
"@equinor/fusion-framework-module-msal": "^3.1.4", | ||
"@equinor/fusion-framework-module-service-discovery": "^7.1.13" | ||
"@equinor/fusion-framework-module": "^4.3.5-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db", | ||
"@equinor/fusion-framework-module-event": "^4.2.4-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db", | ||
"@equinor/fusion-framework-module-http": "^6.1.0-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db", | ||
"@equinor/fusion-framework-module-msal": "^3.1.5-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db", | ||
"@equinor/fusion-framework-module-service-discovery": "^8.0.0-alpha-20bf0650f0d578683f2310b6d96c4ce0be96b7db" | ||
}, | ||
@@ -63,0 +63,0 @@ "scripts": { |
@@ -7,14 +7,18 @@ import { | ||
} from '@equinor/fusion-observable'; | ||
import { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import { ApplicationManifest } from '../ApplicationManifest'; | ||
const createActions = () => ({ | ||
/** Manifest loading */ | ||
setManifest: createAction('set_manifest', (manifest: AppManifest, update?: boolean) => ({ | ||
payload: manifest, | ||
meta: { | ||
// TODO when updating | ||
created: Date.now(), | ||
update, | ||
}, | ||
})), | ||
setManifest: createAction( | ||
'set_manifest', | ||
(manifest: ApplicationManifest, update?: boolean) => ({ | ||
payload: manifest, | ||
meta: { | ||
// TODO when updating | ||
created: Date.now(), | ||
update, | ||
}, | ||
}), | ||
), | ||
@@ -24,3 +28,3 @@ fetchManifest: createAsyncAction( | ||
(key: string, update?: boolean) => ({ payload: key, meta: { update } }), | ||
(manifest: AppManifest) => ({ payload: manifest }), | ||
(manifest: ApplicationManifest) => ({ payload: manifest }), | ||
(error: unknown) => ({ payload: error }), | ||
@@ -32,3 +36,3 @@ ), | ||
'fetch_config', | ||
(key: string) => ({ payload: key }), | ||
(manifest: AppManifest) => ({ payload: manifest }), | ||
(config: AppConfig) => ({ payload: config }), | ||
@@ -35,0 +39,0 @@ (error: unknown) => ({ payload: error }), |
@@ -1,2 +0,2 @@ | ||
import { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import type { AppConfig, AppModulesInstance, AppScriptModule } from '../types'; | ||
import { FlowSubject, Observable } from '@equinor/fusion-observable'; | ||
@@ -11,2 +11,3 @@ | ||
map, | ||
of, | ||
OperatorFunction, | ||
@@ -22,2 +23,3 @@ Subscription, | ||
import './events'; | ||
import { ApplicationManifest } from '../ApplicationManifest'; | ||
@@ -40,3 +42,3 @@ // TODO - move globally | ||
*/ | ||
get manifest$(): Observable<AppManifest>; | ||
get manifest$(): Observable<ApplicationManifest>; | ||
@@ -77,3 +79,3 @@ /** | ||
*/ | ||
get manifest(): AppManifest | undefined; | ||
get manifest(): Readonly<ApplicationManifest> | undefined; | ||
@@ -84,3 +86,3 @@ /** | ||
*/ | ||
get manifestAsync(): Promise<AppManifest>; | ||
get manifestAsync(): Promise<Readonly<ApplicationManifest>>; | ||
@@ -115,3 +117,3 @@ /** | ||
initialize(): Observable<{ | ||
manifest: AppManifest; | ||
manifest: ApplicationManifest; | ||
script: AppScriptModule; | ||
@@ -156,3 +158,3 @@ config: AppConfig; | ||
*/ | ||
getManifest(force_refresh?: boolean): Observable<AppManifest>; | ||
getManifest(force_refresh?: boolean): Observable<ApplicationManifest>; | ||
@@ -164,3 +166,3 @@ /** | ||
*/ | ||
getManifestAsync(allow_cache?: boolean): Promise<AppManifest>; | ||
getManifestAsync(allow_cache?: boolean): Promise<ApplicationManifest>; | ||
@@ -191,3 +193,3 @@ /** | ||
get manifest$(): Observable<AppManifest> { | ||
get manifest$(): Observable<ApplicationManifest> { | ||
return this.#state.pipe( | ||
@@ -231,7 +233,7 @@ map(({ manifest }) => manifest), | ||
get manifest(): Readonly<AppManifest> | undefined { | ||
get manifest(): Readonly<ApplicationManifest> | undefined { | ||
return this.state.manifest; | ||
} | ||
get manifestAsync(): Promise<Readonly<AppManifest>> { | ||
get manifestAsync(): Promise<Readonly<ApplicationManifest>> { | ||
return firstValueFrom(this.manifest$); | ||
@@ -415,3 +417,3 @@ } | ||
public initialize(): Observable<{ | ||
manifest: AppManifest; | ||
manifest: ApplicationManifest; | ||
script: AppScriptModule; | ||
@@ -452,3 +454,8 @@ config: AppConfig; | ||
public loadConfig() { | ||
this.#state.next(actions.fetchConfig(this.appKey)); | ||
// TODO - shit fix | ||
(this.manifest ? of(this.manifest) : this.getManifest()).subscribe({ | ||
next: (manifest) => { | ||
this.#state.next(actions.fetchConfig(manifest)); | ||
}, | ||
}); | ||
} | ||
@@ -460,3 +467,3 @@ | ||
public updateManifest(manifest: AppManifest, replace?: false) { | ||
public updateManifest(manifest: ApplicationManifest, replace?: false) { | ||
this.#state.next(actions.setManifest(manifest, !replace)); | ||
@@ -467,3 +474,9 @@ } | ||
const manifest = await this.getManifestAsync(allow_cache); | ||
this.#state.next(actions.importApp(manifest.entry)); | ||
if (manifest.build.entryPoint) { | ||
this.#state.next(actions.importApp(manifest.build.entryPoint)); | ||
} else { | ||
console.log( | ||
`The ${manifest.key} is missing entryPoint, please upload a build for the app before continuing`, | ||
); | ||
} | ||
} | ||
@@ -523,3 +536,3 @@ | ||
public getManifest(force_refresh = false): Observable<AppManifest> { | ||
public getManifest(force_refresh = false): Observable<ApplicationManifest> { | ||
return new Observable((subscriber) => { | ||
@@ -568,3 +581,3 @@ if (this.#state.value.manifest) { | ||
public getManifestAsync(allow_cache = true): Promise<AppManifest> { | ||
public getManifestAsync(allow_cache = true): Promise<ApplicationManifest> { | ||
// when allow_cache is true, use first emitted value, otherwise use last emitted value | ||
@@ -621,4 +634,16 @@ const operator = allow_cache ? firstValueFrom : lastValueFrom; | ||
this.getManifest().subscribe((manifest) => { | ||
// dispatch import_app action to load the application script | ||
this.#state.next(actions.importApp(manifest.entry)); | ||
if (manifest.build.entryPoint) { | ||
// TODO - this should come from backend | ||
const assetPath = | ||
manifest.build.assetPath ?? | ||
[manifest.key, manifest.build.version].join('@'); | ||
// dispatch import_app action to load the application script | ||
this.#state.next( | ||
actions.importApp([assetPath, manifest.build.entryPoint].join('/')), | ||
); | ||
} else { | ||
console.error( | ||
`The ${manifest.key} app is missing a entry in the manifest, upload a build for your app before continuing`, | ||
); | ||
} | ||
}), | ||
@@ -625,0 +650,0 @@ ); |
@@ -23,4 +23,5 @@ import { | ||
.addCase(actions.setManifest, (state, action) => { | ||
// TODO: after legacy is removed, remove the update flag | ||
if (action.meta.update) { | ||
state.manifest = { ...state.manifest, ...action.payload }; | ||
state.manifest = Object.assign(state.manifest ?? {}, action.payload); | ||
} else { | ||
@@ -27,0 +28,0 @@ state.manifest = action.payload; |
@@ -27,5 +27,5 @@ import { FlowSubject } from '@equinor/fusion-observable'; | ||
// add handler for loading application script | ||
state.addFlow(handleImportApplication()); | ||
state.addFlow(handleImportApplication(provider)); | ||
return state; | ||
}; |
@@ -69,5 +69,6 @@ import { from, of, concat } from 'rxjs'; | ||
// when request is received, abort any ongoing request and start new | ||
switchMap(({ payload: appKey }) => { | ||
// fetch manifest from provider | ||
const subject = from(provider.getAppConfig(appKey)).pipe( | ||
switchMap(({ payload }) => { | ||
// TODO - use the configUrl directly from the manifest | ||
// fetch config from provider | ||
const subject = from(provider.getAppConfig(payload.key, payload.version)).pipe( | ||
// filter out null values | ||
@@ -78,8 +79,8 @@ filter((x) => !!x), | ||
); | ||
// first load manifest and then dispatch success action | ||
// first load config and then dispatch success action | ||
return concat( | ||
subject.pipe(map((manifest) => actions.setConfig(manifest))), | ||
subject.pipe(map((config) => actions.setConfig(config))), | ||
subject.pipe( | ||
last(), | ||
map((manifest) => actions.fetchConfig.success(manifest)), | ||
map((config) => actions.fetchConfig.success(config)), | ||
), | ||
@@ -99,16 +100,19 @@ ).pipe( | ||
*/ | ||
export const handleImportApplication = (): Flow<Actions, AppBundleState> => (action$) => | ||
action$.pipe( | ||
// only handle import script request actions | ||
filter(actions.importApp.match), | ||
// when request is received, abort any ongoing request and start new | ||
switchMap(({ payload }) => { | ||
// dynamically import the application script | ||
return from(import(payload)).pipe( | ||
// dispatch success action | ||
map(actions.importApp.success), | ||
// catch any error and dispatch failure action | ||
catchError((err) => of(actions.importApp.failure(err))), | ||
); | ||
}), | ||
); | ||
export const handleImportApplication = | ||
(provider: AppModuleProvider): Flow<Actions, AppBundleState> => | ||
(action$) => | ||
action$.pipe( | ||
// only handle import script request actions | ||
filter(actions.importApp.match), | ||
// when request is received, abort any ongoing request and start new | ||
switchMap(({ payload }) => { | ||
const endpoint = [provider.getBaseUri(), payload].join('/').replace(/\/{2,}/g, '/'); | ||
// dynamically import the application script | ||
return from(import(/* @vite-ignore */ endpoint)).pipe( | ||
// dispatch success action | ||
map(actions.importApp.success), | ||
// catch any error and dispatch failure action | ||
catchError((err) => of(actions.importApp.failure(err))), | ||
); | ||
}), | ||
); |
@@ -1,2 +0,3 @@ | ||
import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; | ||
import type { AppConfig, AppModulesInstance, AppScriptModule } from '../types'; | ||
import { ApplicationManifest } from '../ApplicationManifest'; | ||
@@ -7,3 +8,3 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
status: Set<string>; | ||
manifest?: AppManifest; | ||
manifest?: ApplicationManifest; | ||
config?: AppConfig<TConfig>; | ||
@@ -10,0 +11,0 @@ modules?: AppScriptModule; |
@@ -1,2 +0,6 @@ | ||
import type { ModuleInitializerArgs } from '@equinor/fusion-framework-module'; | ||
import { | ||
BaseConfigBuilder, | ||
ConfigBuilderCallback, | ||
type ModuleInitializerArgs, | ||
} from '@equinor/fusion-framework-module'; | ||
import type { HttpModule, IHttpClient } from '@equinor/fusion-framework-module-http'; | ||
@@ -6,38 +10,35 @@ import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery'; | ||
import { AppConfigBuilder, AppConfigBuilderCallback } from './AppConfigBuilder'; | ||
import { moduleKey } from './module'; | ||
import type { AppConfig, AppManifest } from './types'; | ||
import type { AppConfig, ApiApp, ApiAppVersionConfig } from './types'; | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
import { map } from 'rxjs/operators'; | ||
export interface AppModuleConfig { | ||
proxy: { | ||
path: string; | ||
}; | ||
client: { | ||
getAppManifest: QueryCtorOptions<AppManifest, { appKey: string }>; | ||
getAppManifests: QueryCtorOptions<AppManifest[], void>; | ||
getAppManifest: QueryCtorOptions<ApplicationManifest, { appKey: string }>; | ||
getAppManifests: QueryCtorOptions< | ||
ApplicationManifest[], | ||
{ filterByCurrentUser?: boolean } | undefined | ||
>; | ||
getAppConfig: QueryCtorOptions<AppConfig, { appKey: string; tag?: string }>; | ||
}; | ||
baseUri?: string; | ||
} | ||
export interface IAppConfigurator { | ||
addConfigBuilder: (init: AppConfigBuilderCallback) => void; | ||
setClient: ( | ||
client_or_cb: | ||
| Promise<AppModuleConfig['client']> | ||
| ConfigBuilderCallback<AppModuleConfig['client']>, | ||
) => void; | ||
setBaseUri: (base_or_cb: string | ConfigBuilderCallback<string>) => void; | ||
} | ||
export class AppConfigurator implements IAppConfigurator { | ||
export class AppConfigurator | ||
extends BaseConfigBuilder<AppModuleConfig> | ||
implements IAppConfigurator | ||
{ | ||
defaultExpireTime = 1 * 60 * 1000; | ||
#configBuilders: Array<AppConfigBuilderCallback> = []; | ||
addConfigBuilder(init: AppConfigBuilderCallback): void { | ||
this.#configBuilders.push(init); | ||
} | ||
setProxyPath(path: string) { | ||
this.addConfigBuilder((builder) => { | ||
builder.config.proxy = { path }; | ||
}); | ||
} | ||
/** | ||
@@ -59,57 +60,95 @@ * WARNING: this function will be remove in future | ||
/** resolve and create a client from discovery */ | ||
try { | ||
return await serviceDiscovery.createClient('app'); | ||
} catch { | ||
return await serviceDiscovery.createClient('portal'); | ||
} | ||
return await serviceDiscovery.createClient('apps'); | ||
} | ||
} | ||
public async createConfig( | ||
public setClient( | ||
client_or_cb: | ||
| Promise<AppModuleConfig['client']> | ||
| ConfigBuilderCallback<AppModuleConfig['client']>, | ||
) { | ||
const cb = typeof client_or_cb === 'object' ? () => client_or_cb : client_or_cb; | ||
this._set('client', cb); | ||
} | ||
// TODO - explain why, used in import of resources aka proxy url | ||
public setBaseUri(base_or_cb: string | ConfigBuilderCallback<string>) { | ||
const cb = typeof base_or_cb === 'string' ? async () => base_or_cb : base_or_cb; | ||
this._set('baseUri', cb); | ||
} | ||
protected _createConfig( | ||
init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>, | ||
): Promise<AppModuleConfig> { | ||
const config = await this.#configBuilders.reduce( | ||
async (cur, cb) => { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const builder = new AppConfigBuilder(init, await cur); | ||
await Promise.resolve(cb(builder)); | ||
return Object.assign(cur, builder.config); | ||
}, | ||
Promise.resolve({} as Partial<AppModuleConfig>), | ||
); | ||
// TODO - make less lazy | ||
config.client ??= await (async (): Promise<AppModuleConfig['client']> => { | ||
const httpClient = await this._createHttpClient(init); | ||
return { | ||
getAppManifest: { | ||
client: { | ||
fn: ({ appKey }) => httpClient.json$<AppManifest>(`/api/apps/${appKey}`), | ||
initial?: Partial<AppModuleConfig>, | ||
) { | ||
if (!this._has('client')) { | ||
this.setClient(async () => { | ||
const httpClient = await this._createHttpClient(init); | ||
return { | ||
getAppManifest: { | ||
client: { | ||
fn: ({ appKey }) => | ||
httpClient | ||
.json$<ApiApp>(`/apps/${appKey}`, { | ||
headers: { | ||
'Api-Version': '1.0', | ||
}, | ||
}) | ||
.pipe(map((apiApp) => new ApplicationManifest(apiApp))), | ||
}, | ||
key: ({ appKey }) => appKey, | ||
expire: this.defaultExpireTime, | ||
}, | ||
key: ({ appKey }) => appKey, | ||
expire: this.defaultExpireTime, | ||
}, | ||
getAppManifests: { | ||
client: { | ||
fn: () => httpClient.json$(`/api/apps`), | ||
getAppManifests: { | ||
client: { | ||
// TODO: add to config if use me or not | ||
fn: (filter) => { | ||
const path = filter?.filterByCurrentUser | ||
? '/persons/me/apps' | ||
: '/apps'; | ||
return httpClient | ||
.json$<{ value: ApiApp[] }>(path, { | ||
headers: { | ||
'Api-Version': '1.0', | ||
}, | ||
}) | ||
.pipe( | ||
map((x) => { | ||
const apps = x.value.map( | ||
(apiApp) => new ApplicationManifest(apiApp), | ||
); | ||
return apps; | ||
}), | ||
); | ||
}, | ||
}, | ||
// TODO - might cast to checksum | ||
key: (filter) => (filter ? JSON.stringify(filter) : 'all'), | ||
expire: this.defaultExpireTime, | ||
}, | ||
// TODO - might cast to checksum | ||
key: () => 'manifests', | ||
expire: this.defaultExpireTime, | ||
}, | ||
getAppConfig: { | ||
client: { | ||
fn: ({ appKey, tag }) => | ||
httpClient.json$( | ||
`/api/apps/${appKey}/config${tag ? `?tag=${tag}` : ''}`, | ||
), | ||
getAppConfig: { | ||
client: { | ||
fn: ({ appKey, tag = 'latest' }) => | ||
httpClient.json$<ApiAppVersionConfig>( | ||
`/apps/${appKey}/builds/${tag}/config`, | ||
{ | ||
headers: { | ||
'Api-Version': '1.0', | ||
}, | ||
}, | ||
), | ||
}, | ||
key: (args) => JSON.stringify(args), | ||
expire: this.defaultExpireTime, | ||
}, | ||
key: (args) => JSON.stringify(args), | ||
expire: this.defaultExpireTime, | ||
}, | ||
}; | ||
})(); | ||
}; | ||
}); | ||
} | ||
return config as AppModuleConfig; | ||
if (!this._has('baseUri')) { | ||
this.setBaseUri('/apps-proxy'); | ||
} | ||
return super._createConfig(init, initial); | ||
} | ||
} |
@@ -18,3 +18,4 @@ import { | ||
import type { AppConfig, AppManifest, CurrentApp } from './types'; | ||
import type { AppConfig, CurrentApp } from './types'; | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
@@ -27,11 +28,13 @@ import { App, filterEmpty, IApp } from './app/App'; | ||
export class AppModuleProvider { | ||
static compareAppManifest<T extends AppManifest>(a?: T, b?: T): boolean { | ||
static compareAppManifest<T extends ApplicationManifest>(a?: T, b?: T): boolean { | ||
return JSON.stringify(a) === JSON.stringify(b); | ||
} | ||
public appClient: Query<AppManifest, { appKey: string }>; | ||
#appsClient: Query<AppManifest[], void>; | ||
public appClient: Query<ApplicationManifest, { appKey: string }>; | ||
#appsClient: Query<ApplicationManifest[], { filterByCurrentUser?: boolean } | undefined>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
#configClient: Query<AppConfig<any>, { appKey: string; tag?: string }>; | ||
#appBaseUri: string; | ||
#current$: BehaviorSubject<CurrentApp | null>; | ||
@@ -76,2 +79,4 @@ | ||
this.#appBaseUri = config.baseUri ?? ''; | ||
this.#subscription.add(() => this.appClient.complete()); | ||
@@ -109,3 +114,3 @@ this.#subscription.add(() => this.#appsClient.complete()); | ||
*/ | ||
public getAppManifest(appKey: string): Observable<AppManifest> { | ||
public getAppManifest(appKey: string): Observable<ApplicationManifest> { | ||
return Query.extractQueryValue( | ||
@@ -128,7 +133,14 @@ this.appClient.query({ appKey }).pipe( | ||
public getAppManifests(filter?: { | ||
filterByCurrentUser: boolean; | ||
}): Observable<ApplicationManifest[]> { | ||
return Query.extractQueryValue(this.#appsClient.query(filter)); | ||
} | ||
/** | ||
* fetch all applications | ||
* @deprecated use `getAppManifests` instead | ||
*/ | ||
public getAllAppManifests(): Observable<AppManifest[]> { | ||
return Query.extractQueryValue(this.#appsClient.query()); | ||
public getAllAppManifests(): Observable<ApplicationManifest[]> { | ||
return this.getAppManifests(); | ||
} | ||
@@ -175,2 +187,6 @@ | ||
public getBaseUri(): string { | ||
return this.#appBaseUri; | ||
} | ||
/** | ||
@@ -177,0 +193,0 @@ * This should not be used, only for legacy creation backdoor |
import type { IModulesConfigurator } from '@equinor/fusion-framework-module'; | ||
import type { AppConfigBuilderCallback } from './AppConfigBuilder'; | ||
import { module } from './module'; | ||
import { AppConfigurator } from './AppConfigurator'; | ||
@@ -13,10 +12,12 @@ /** | ||
configurator: IModulesConfigurator<any, any>, | ||
builder?: AppConfigBuilderCallback, | ||
callback?: (builder: AppConfigurator) => void | Promise<void>, | ||
): void => { | ||
configurator.addConfig({ | ||
module, | ||
configure: (appConfigurator) => { | ||
builder && appConfigurator.addConfigBuilder(builder); | ||
configure: async (configurator) => { | ||
if (callback) { | ||
Promise.resolve(callback(configurator)); | ||
} | ||
}, | ||
}); | ||
}; |
@@ -11,2 +11,4 @@ export { | ||
export { ApplicationManifest } from './ApplicationManifest'; | ||
export * from './events'; | ||
@@ -13,0 +15,0 @@ export * from './types'; |
import { Module } from '@equinor/fusion-framework-module'; | ||
import { ModuleDeps } from './types'; | ||
import { IAppConfigurator, AppConfigurator } from './AppConfigurator'; | ||
import { AppConfigurator } from './AppConfigurator'; | ||
import { AppModuleProvider } from './AppModuleProvider'; | ||
@@ -9,3 +9,3 @@ | ||
export type AppModule = Module<typeof moduleKey, AppModuleProvider, IAppConfigurator, ModuleDeps>; | ||
export type AppModule = Module<typeof moduleKey, AppModuleProvider, AppConfigurator, ModuleDeps>; | ||
@@ -33,3 +33,3 @@ /** | ||
initialize: async (args) => { | ||
const config = await (args.config as AppConfigurator).createConfig(args); | ||
const config = await args.config.createConfigAsync(args); | ||
const event = await args.requireInstance('event').catch(() => undefined); | ||
@@ -36,0 +36,0 @@ return new AppModuleProvider({ config, event }); |
118
src/types.ts
@@ -7,2 +7,3 @@ import { AnyModule, CombinedModules, ModulesInstance } from '@equinor/fusion-framework-module'; | ||
import IApp from './app'; | ||
import { ApplicationManifest } from './ApplicationManifest'; | ||
@@ -15,3 +16,3 @@ // TODO | ||
basename?: string; | ||
manifest?: AppManifest; | ||
manifest?: ApplicationManifest; | ||
config?: AppConfig<TConfig>; | ||
@@ -24,3 +25,3 @@ props?: TProps; | ||
export type AppType = 'standalone' | 'report' | 'launcher'; | ||
export type AppType = 'standalone' | 'report' | 'launcher' | 'template'; | ||
@@ -39,22 +40,69 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
type AppCategory = { | ||
id?: string; | ||
name: string | null; | ||
color: string | null; | ||
defaultIcon: string | null; | ||
id: string; | ||
name?: string | null; | ||
displayName?: string | null; | ||
color?: string | null; | ||
defaultIcon?: string | null; | ||
sortOrder: number; | ||
}; | ||
export type AppManifest = { | ||
type AppVisualization = { | ||
color?: string | null; | ||
icon?: string | null; | ||
sortOrder: number; | ||
}; | ||
export type AzureId = { | ||
azureId: string; | ||
}; | ||
export type AzureUniqueId = { | ||
azureUniqueId: string; | ||
}; | ||
export type AppBuild<TUploaderId = AzureId | AzureUniqueId> = { | ||
version?: string; | ||
entryPoint?: string; | ||
tags?: string[]; | ||
tag?: string; | ||
assetPath?: string; | ||
configUrl?: string; | ||
timestamp?: string; | ||
commitSha?: string; | ||
githubRepo?: string; | ||
projectPage?: string; | ||
uploadedBy?: { | ||
displayName?: string; | ||
mail?: string; | ||
upn?: string; | ||
accountType?: string; | ||
accountClassification?: string; | ||
} & TUploaderId; | ||
}; | ||
export type AppOwnerOrAdmin = { | ||
id: string; | ||
azureId?: string; | ||
azureUniqueId?: string; | ||
displayName?: string; | ||
mail?: string; | ||
upn?: string; | ||
accountType?: string; | ||
accountClassification?: string; | ||
}; | ||
export interface AppManifest { | ||
key: string; | ||
name: string; | ||
entry: string; | ||
version: string; | ||
name?: string; | ||
entry?: string; | ||
version?: string; | ||
shortName?: string; | ||
description?: string; | ||
keywords?: string[]; | ||
type?: AppType; | ||
isPinned?: boolean; | ||
tags?: string[]; | ||
// context?: ContextManifest; | ||
auth?: AppAuth[]; | ||
tag?: string; | ||
auth?: AppOwnerOrAdmin[]; | ||
icon?: string; | ||
order?: number; | ||
publishedDate?: Date; | ||
publishedDate?: string; | ||
accentColor?: string; | ||
@@ -64,9 +112,13 @@ categoryId?: string; | ||
hide?: boolean; | ||
}; | ||
visualization?: AppVisualization; | ||
admins?: AppOwnerOrAdmin[]; | ||
owners?: AppOwnerOrAdmin[]; | ||
build?: AppBuild<AzureId>; | ||
} | ||
export type Endpoint = { name: string; uri: string; scopes?: string[] }; | ||
export type Endpoint = { url: string; scopes?: string[] }; | ||
export type AppConfig<TEnvironment = unknown> = { | ||
environment: TEnvironment; | ||
endpoints: Record<string, string | Endpoint>; | ||
environment?: TEnvironment; | ||
endpoints?: Record<string, Endpoint>; | ||
}; | ||
@@ -79,3 +131,3 @@ | ||
export type AppBundle<TEnvironment = unknown, TModule = unknown> = { | ||
manifest: AppManifest; | ||
manifest: ApplicationManifest; | ||
config: AppConfig<TEnvironment>; | ||
@@ -102,1 +154,31 @@ module: TModule; | ||
ModulesInstance<AppModules<TModules>>; | ||
export type ApiAppVersionConfig = { | ||
environment: string; | ||
endpoints: Record< | ||
string, | ||
{ | ||
url: string; | ||
scopes: string[]; | ||
} | ||
>; | ||
}; | ||
export type ApiApp = { | ||
appKey: string; | ||
displayName?: string; | ||
description?: string; | ||
type?: AppType; | ||
isPinned?: boolean; | ||
templateSource?: string; | ||
category?: AppCategory; | ||
visualization: { | ||
color: string; | ||
icon: string; | ||
sortOrder: number; | ||
}; | ||
keywords: string[]; | ||
admins: AppOwnerOrAdmin[]; | ||
owners: AppOwnerOrAdmin[]; | ||
build: AppBuild<AzureUniqueId>; | ||
}; |
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
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
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
354424
3735
78
2