@equinor/fusion-framework-module-app 6.0.2 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +194 -128
  2. package/dist/esm/AppClient.js +55 -6
  3. package/dist/esm/AppClient.js.map +1 -1
  4. package/dist/esm/AppModuleProvider.js +15 -0
  5. package/dist/esm/AppModuleProvider.js.map +1 -1
  6. package/dist/esm/app/App.js +157 -5
  7. package/dist/esm/app/App.js.map +1 -1
  8. package/dist/esm/app/actions.js +17 -0
  9. package/dist/esm/app/actions.js.map +1 -1
  10. package/dist/esm/app/create-reducer.js +3 -0
  11. package/dist/esm/app/create-reducer.js.map +1 -1
  12. package/dist/esm/app/create-state.js +4 -1
  13. package/dist/esm/app/create-state.js.map +1 -1
  14. package/dist/esm/app/flows.js +47 -1
  15. package/dist/esm/app/flows.js.map +1 -1
  16. package/dist/esm/errors.js +30 -0
  17. package/dist/esm/errors.js.map +1 -1
  18. package/dist/esm/version.js +1 -1
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/dist/types/AppClient.d.ts +23 -1
  21. package/dist/types/AppModuleProvider.d.ts +12 -1
  22. package/dist/types/app/App.d.ts +58 -2
  23. package/dist/types/app/actions.d.ts +37 -1
  24. package/dist/types/app/create-reducer.d.ts +29 -0
  25. package/dist/types/app/events.d.ts +12 -1
  26. package/dist/types/app/flows.d.ts +16 -0
  27. package/dist/types/app/types.d.ts +6 -2
  28. package/dist/types/errors.d.ts +20 -0
  29. package/dist/types/types.d.ts +3 -0
  30. package/dist/types/version.d.ts +1 -1
  31. package/package.json +7 -5
  32. package/src/AppClient.ts +84 -5
  33. package/src/AppModuleProvider.ts +18 -1
  34. package/src/app/App.ts +267 -21
  35. package/src/app/actions.ts +34 -1
  36. package/src/app/create-reducer.ts +3 -0
  37. package/src/app/create-state.ts +12 -1
  38. package/src/app/events.ts +20 -1
  39. package/src/app/flows.ts +71 -1
  40. package/src/app/types.ts +6 -1
  41. package/src/errors.ts +43 -0
  42. package/src/types.ts +4 -0
  43. package/src/version.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import { Observable, ObservableInput } from 'rxjs';
2
2
  import { IHttpClient } from '@equinor/fusion-framework-module-http';
3
- import type { AppConfig, AppManifest, ConfigEnvironment } from './types';
3
+ import type { AppConfig, AppManifest, AppSettings, ConfigEnvironment } from './types';
4
4
  export interface IAppClient extends Disposable {
5
5
  /**
6
6
  * Fetch app manifest by appKey
@@ -21,6 +21,21 @@ export interface IAppClient extends Disposable {
21
21
  appKey: string;
22
22
  tag?: string;
23
23
  }) => ObservableInput<AppConfig<TType>>;
24
+ /**
25
+ * Fetch app settings by appKey
26
+ */
27
+ getAppSettings: (args: {
28
+ appKey: string;
29
+ }) => ObservableInput<AppSettings>;
30
+ /**
31
+ * Set app settings by appKey
32
+ * @param args - Object with appKey and settings
33
+ * @returns ObservableInput<AppSettings>
34
+ */
35
+ updateAppSettings: (args: {
36
+ appKey: string;
37
+ settings: AppSettings;
38
+ }) => ObservableInput<AppSettings>;
24
39
  }
25
40
  /**
26
41
  * The `AppClient` class implements the `IAppClient` interface and provides methods to query
@@ -39,6 +54,13 @@ export declare class AppClient implements IAppClient {
39
54
  appKey: string;
40
55
  tag?: string;
41
56
  }): Observable<AppConfig<TType>>;
57
+ getAppSettings(args: {
58
+ appKey: string;
59
+ }): Observable<AppSettings>;
60
+ updateAppSettings(args: {
61
+ appKey: string;
62
+ settings: AppSettings;
63
+ }): Observable<AppSettings>;
42
64
  [Symbol.dispose](): void;
43
65
  }
44
66
  export default AppClient;
@@ -1,7 +1,7 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { ModuleType } from '@equinor/fusion-framework-module';
3
3
  import { EventModule } from '@equinor/fusion-framework-module-event';
4
- import type { AppConfig, AppManifest, ConfigEnvironment, CurrentApp } from './types';
4
+ import type { AppConfig, AppManifest, AppSettings, ConfigEnvironment, CurrentApp } from './types';
5
5
  import { App, IApp } from './app/App';
6
6
  import { AppModuleConfig } from './AppConfigurator';
7
7
  import { AppBundleStateInitial } from './app/types';
@@ -39,6 +39,17 @@ export declare class AppModuleProvider {
39
39
  * @param appKey - application key
40
40
  */
41
41
  getAppConfig<TType extends ConfigEnvironment = ConfigEnvironment>(appKey: string, tag?: string): Observable<AppConfig<TType>>;
42
+ /**
43
+ * fetch user settings for an application
44
+ * @param appKey - application key
45
+ */
46
+ getAppSettings(appKey: string): Observable<AppSettings>;
47
+ /**
48
+ * Put user settings for an application
49
+ * @param appKey - application key
50
+ * @param settings - The settings to add save
51
+ */
52
+ updateAppSettings(appKey: string, settings: AppSettings): Observable<AppSettings>;
42
53
  /**
43
54
  * set the current application, will internally resolve manifest
44
55
  * @param appKey - application key
@@ -1,7 +1,7 @@
1
- import type { AppModulesInstance, AppScriptModule, AppManifest, AppConfig, ConfigEnvironment } from '../types';
1
+ import type { AppModulesInstance, AppScriptModule, AppManifest, AppConfig, AppSettings, ConfigEnvironment } from '../types';
2
2
  import { Observable } from '@equinor/fusion-observable';
3
3
  import type { AppModuleProvider } from '../AppModuleProvider';
4
- import { OperatorFunction } from 'rxjs';
4
+ import { type OperatorFunction } from 'rxjs';
5
5
  import { EventModule } from '@equinor/fusion-framework-module-event';
6
6
  import { AnyModule, ModuleType } from '@equinor/fusion-framework-module';
7
7
  import { AppBundleState, AppBundleStateInitial } from './types';
@@ -33,6 +33,16 @@ export interface IApp<TEnv extends ConfigEnvironment = ConfigEnvironment, TModul
33
33
  * @returns An observable that emits the instance of the app modules.
34
34
  */
35
35
  get instance$(): Observable<AppModulesInstance<TModules>>;
36
+ /**
37
+ * Observable that emits the settings of the app.
38
+ * @returns An Observable that emits the app settings.
39
+ */
40
+ get settings$(): Observable<AppSettings>;
41
+ /**
42
+ * Observable that emits the status of the app.
43
+ * @returns An Observable that emits the app status.
44
+ */
45
+ get status$(): Observable<AppBundleState['status']>;
36
46
  /**
37
47
  * Gets the current state of the Application.
38
48
  * @returns The current state of the Application.
@@ -108,6 +118,44 @@ export interface IApp<TEnv extends ConfigEnvironment = ConfigEnvironment, TModul
108
118
  * @returns A promise that resolves to the AppConfig.
109
119
  */
110
120
  getConfigAsync(allow_cache?: boolean): Promise<AppConfig>;
121
+ /**
122
+ * Gets the app settings.
123
+ * @param force_refresh Whether to force refreshing the settings.
124
+ * @returns An observable that emits the app settings.
125
+ */
126
+ getSettings(force_refresh?: boolean): Observable<AppSettings>;
127
+ /**
128
+ * Retrieves the app settings asynchronously.
129
+ * @param allow_cache Whether to allow loading from cache.
130
+ * @returns A promise that resolves to the AppSettings.
131
+ */
132
+ getSettingsAsync(allow_cache?: boolean): Promise<AppSettings>;
133
+ /**
134
+ * Sets the app settings.
135
+ * @param settings The settings object to save.
136
+ * @returns An observable that emits the app settings.
137
+ */
138
+ updateSettings<T extends AppSettings>(settings: T): Observable<T>;
139
+ /**
140
+ * Sets the app settings asyncronously.
141
+ * @param settings The settings object to save.
142
+ * @returns An Promise that resolves the app settings.
143
+ */
144
+ updateSettingsAsync<T extends AppSettings>(settings: T): Promise<T>;
145
+ /**
146
+ * Updates a specific setting of the app.
147
+ * @param property The property to update.
148
+ * @param value The value to set.
149
+ * @returns An observable that emits the app settings.
150
+ */
151
+ updateSetting<T extends AppSettings, P extends keyof T>(property: P, value: T[P]): Observable<T[P]>;
152
+ /**
153
+ * Updates a specific setting of the app asynchronously.
154
+ * @param property The property to update.
155
+ * @param value The value to set.
156
+ * @returns A promise that resolves to the AppSettings.
157
+ */
158
+ updateSettingAsync<T extends AppSettings, P extends keyof T>(property: P, value: T[P]): Promise<T[P]>;
111
159
  /**
112
160
  * Gets the app manifest.
113
161
  * @param force_refresh Whether to force refreshing the manifest.
@@ -139,6 +187,8 @@ export declare class App<TEnv extends ConfigEnvironment = ConfigEnvironment, TMo
139
187
  get config$(): Observable<AppConfig<TEnv>>;
140
188
  get modules$(): Observable<AppScriptModule>;
141
189
  get instance$(): Observable<AppModulesInstance<TModules>>;
190
+ get settings$(): Observable<AppSettings>;
191
+ get status$(): Observable<AppBundleState['status']>;
142
192
  get state(): Readonly<AppBundleState>;
143
193
  get appKey(): string;
144
194
  get manifest(): Readonly<AppManifest> | undefined;
@@ -161,6 +211,12 @@ export declare class App<TEnv extends ConfigEnvironment = ConfigEnvironment, TMo
161
211
  loadAppModule(allow_cache?: boolean): Promise<void>;
162
212
  getConfig(force_refresh?: boolean): Observable<AppConfig>;
163
213
  getConfigAsync(allow_cache?: boolean): Promise<AppConfig>;
214
+ getSettings<T extends AppSettings>(force_refresh?: boolean): Observable<T>;
215
+ getSettingsAsync<T extends AppSettings>(allow_cache?: boolean): Promise<T>;
216
+ updateSettings<T extends AppSettings>(settings: T): Observable<T>;
217
+ updateSettingsAsync<T extends AppSettings>(settings: T): Promise<T>;
218
+ updateSetting<T extends AppSettings, P extends keyof T>(property: P, value: T[P]): Observable<T[P]>;
219
+ updateSettingAsync<T extends AppSettings, P extends keyof T>(property: P, value: T[P]): Promise<T[P]>;
164
220
  getManifest(force_refresh?: boolean): Observable<AppManifest>;
165
221
  getManifestAsync(allow_cache?: boolean): Promise<AppManifest>;
166
222
  getAppModule(force_refresh?: boolean): Observable<AppScriptModule>;
@@ -1,5 +1,5 @@
1
1
  import { ActionInstanceMap, ActionTypes } from '@equinor/fusion-observable';
2
- import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types';
2
+ import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule, AppSettings } from '../types';
3
3
  declare const createActions: () => {
4
4
  /** Manifest loading */
5
5
  setManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[manifest: AppManifest, update?: boolean | undefined], AppManifest, "set_manifest", never, {
@@ -18,6 +18,24 @@ declare const createActions: () => {
18
18
  success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[config: AppConfig<import("../AppConfig").ConfigEnvironment>], AppConfig<import("../AppConfig").ConfigEnvironment>, "fetch_config::success", never, never>;
19
19
  failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_config::failure", never, never>;
20
20
  };
21
+ /** Settings loading */
22
+ setSettings: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[settings?: AppSettings | undefined], AppSettings | undefined, "set_settings", never, never>;
23
+ /** Fetching settings */
24
+ fetchSettings: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[appKey: string], {
25
+ appKey: string;
26
+ }, "fetch_settings::request", never, never> & {
27
+ success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[settings: AppSettings], AppSettings, "fetch_settings::success", never, never>;
28
+ failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_settings::failure", never, never>;
29
+ };
30
+ /** Updating settings */
31
+ updateSettings: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[appKey: string, settings: AppSettings], {
32
+ appKey: string;
33
+ settings: AppSettings;
34
+ }, "update_settings::request", never, never> & {
35
+ success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[settings: AppSettings], AppSettings, "update_settings::success", never, never>;
36
+ failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "update_settings::failure", never, never>;
37
+ };
38
+ updateSettingsAbort: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[id: string], string, "update_settings::abort", never, never>;
21
39
  /** App loading */
22
40
  setModule: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[module: any], any, "set_module", never, never>;
23
41
  importApp: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[entrypoint: string], string, "import_app::request", never, never> & {
@@ -48,6 +66,24 @@ export declare const actions: {
48
66
  success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[config: AppConfig<import("../AppConfig").ConfigEnvironment>], AppConfig<import("../AppConfig").ConfigEnvironment>, "fetch_config::success", never, never>;
49
67
  failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_config::failure", never, never>;
50
68
  };
69
+ /** Settings loading */
70
+ setSettings: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[settings?: AppSettings | undefined], AppSettings | undefined, "set_settings", never, never>;
71
+ /** Fetching settings */
72
+ fetchSettings: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[appKey: string], {
73
+ appKey: string;
74
+ }, "fetch_settings::request", never, never> & {
75
+ success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[settings: AppSettings], AppSettings, "fetch_settings::success", never, never>;
76
+ failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "fetch_settings::failure", never, never>;
77
+ };
78
+ /** Updating settings */
79
+ updateSettings: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[appKey: string, settings: AppSettings], {
80
+ appKey: string;
81
+ settings: AppSettings;
82
+ }, "update_settings::request", never, never> & {
83
+ success: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[settings: AppSettings], AppSettings, "update_settings::success", never, never>;
84
+ failure: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[error: unknown], unknown, "update_settings::failure", never, never>;
85
+ };
86
+ updateSettingsAbort: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[id: string], string, "update_settings::abort", never, never>;
51
87
  /** App loading */
52
88
  setModule: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[module: any], any, "set_module", never, never>;
53
89
  importApp: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[entrypoint: string], string, "import_app::request", never, never> & {
@@ -10,6 +10,23 @@ export declare const createReducer: (value: AppBundleStateInitial) => import("@e
10
10
  } | {
11
11
  payload: import("..").AppManifest;
12
12
  type: "fetch_config::request";
13
+ } | {
14
+ payload: import("..").AppSettings | undefined;
15
+ type: "set_settings";
16
+ } | {
17
+ payload: {
18
+ appKey: string;
19
+ };
20
+ type: "fetch_settings::request";
21
+ } | {
22
+ payload: {
23
+ appKey: string;
24
+ settings: import("..").AppSettings;
25
+ };
26
+ type: "update_settings::request";
27
+ } | {
28
+ payload: string;
29
+ type: "update_settings::abort";
13
30
  } | {
14
31
  payload: any;
15
32
  type: "set_module";
@@ -34,6 +51,18 @@ export declare const createReducer: (value: AppBundleStateInitial) => import("@e
34
51
  } | {
35
52
  payload: unknown;
36
53
  type: "fetch_config::failure";
54
+ } | {
55
+ payload: import("..").AppSettings;
56
+ type: "fetch_settings::success";
57
+ } | {
58
+ payload: unknown;
59
+ type: "fetch_settings::failure";
60
+ } | {
61
+ payload: import("..").AppSettings;
62
+ type: "update_settings::success";
63
+ } | {
64
+ payload: unknown;
65
+ type: "update_settings::failure";
37
66
  } | {
38
67
  payload: import("..").AppScriptModule;
39
68
  type: "import_app::success";
@@ -1,6 +1,6 @@
1
1
  import type { FrameworkEvent, FrameworkEventInit } from '@equinor/fusion-framework-module-event';
2
2
  import type { App } from './App';
3
- import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types';
3
+ import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule, AppSettings } from '../types';
4
4
  /** base event type for applications */
5
5
  export type AppEventEventInit<TDetail extends Record<string, unknown> | unknown = unknown> = FrameworkEventInit<
6
6
  /** additional event details and key of target event */
@@ -32,6 +32,17 @@ declare module '@equinor/fusion-framework-module-event' {
32
32
  config: AppConfig;
33
33
  }>;
34
34
  onAppConfigFailure: AppEventFailure;
35
+ onAppSettingsLoad: AppEvent;
36
+ /** fired when the application has loaded corresponding settings */
37
+ onAppSettingsLoaded: AppEvent<{
38
+ settings: AppSettings;
39
+ }>;
40
+ onAppSettingsFailure: AppEventFailure;
41
+ onAppSettingsUpdate: AppEvent;
42
+ onAppSettingsUpdated: AppEvent<{
43
+ settings: AppSettings;
44
+ }>;
45
+ onAppSettingsUpdateFailure: AppEventFailure;
35
46
  /** fired when the application has loaded corresponding javascript module */
36
47
  onAppScriptLoad: AppEvent;
37
48
  onAppScriptLoaded: AppEvent<{
@@ -18,6 +18,22 @@ export declare const handleFetchManifest: (provider: AppModuleProvider) => Flow<
18
18
  * @returns A Flow function that takes an Observable of actions and returns an Observable of actions.
19
19
  */
20
20
  export declare const handleFetchConfig: (provider: AppModuleProvider) => Flow<Actions, AppBundleState>;
21
+ /**
22
+ * Handles the fetch settings action by fetching the app settings from the provider,
23
+ * filtering out null values, and dispatching success or failure actions accordingly.
24
+ *
25
+ * @param provider The AppModuleProvider used to fetch the app settings.
26
+ * @returns A Flow function that takes an Observable of actions and returns an Observable of actions.
27
+ */
28
+ export declare const handleFetchSettings: (provider: AppModuleProvider) => Flow<Actions, AppBundleState>;
29
+ /**
30
+ * Handles the set settings action by setting the app settings from the provider,
31
+ * filtering out null values, and dispatching success or failure actions accordingly.
32
+ *
33
+ * @param provider The AppModuleProvider used to fetch the app settings.
34
+ * @returns A Flow function that takes an Observable of actions and returns an Observable of actions.
35
+ */
36
+ export declare const handleUpdateSettings: (provider: AppModuleProvider) => Flow<Actions, AppBundleState>;
21
37
  /**
22
38
  * Handles the import application flow.
23
39
  * @returns A flow that takes in actions and returns an observable of AppBundleState.
@@ -1,4 +1,6 @@
1
- import type { AppManifest, AppConfig, AppModulesInstance, AppScriptModule, ConfigEnvironment } from '../types';
1
+ import { ActionBaseType } from '@equinor/fusion-observable';
2
+ import type { AppManifest, AppConfig, AppModulesInstance, AppScriptModule, ConfigEnvironment, AppSettings } from '../types';
3
+ import { Actions } from './actions';
2
4
  /**
3
5
  * Represents the state of an application bundle.
4
6
  *
@@ -9,14 +11,16 @@ import type { AppManifest, AppConfig, AppModulesInstance, AppScriptModule, Confi
9
11
  * @property {Set<string>} status - A set of strings representing the status of the application.
10
12
  * @property {AppManifest} [manifest] - An optional manifest describing the application.
11
13
  * @property {AppConfig<TConfig>} [config] - An optional configuration object for the application.
14
+ * @property {AppSettings} [settings] - An optional application settings object.
12
15
  * @property {AppScriptModule} [modules] - An optional script module for the application.
13
16
  * @property {AppModulesInstance<TModules>} [instance] - An optional instance of the application modules.
14
17
  */
15
18
  export type AppBundleState<TConfig extends ConfigEnvironment = ConfigEnvironment, TModules = any> = {
16
19
  appKey: string;
17
- status: Set<string>;
20
+ status: Set<ActionBaseType<Actions>>;
18
21
  manifest?: AppManifest;
19
22
  config?: AppConfig<TConfig>;
23
+ settings?: AppSettings;
20
24
  modules?: AppScriptModule;
21
25
  instance?: AppModulesInstance<TModules>;
22
26
  };
@@ -39,6 +39,26 @@ export declare class AppConfigError extends Error {
39
39
  */
40
40
  constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
41
41
  }
42
+ /**
43
+ * Represents an error that occurs while fetching application settings.
44
+ */
45
+ export declare class AppSettingsError extends Error {
46
+ readonly type: AppErrorType;
47
+ /**
48
+ * Creates an instance of `AppSettingsError` based on the HTTP response status.
49
+ * @param response The HTTP response.
50
+ * @param options Additional error options.
51
+ * @returns An instance of `AppSettingsError` based on the HTTP response status.
52
+ */
53
+ static fromHttpResponse(response: Response, options?: ErrorOptions): AppSettingsError;
54
+ /**
55
+ * Creates an instance of `AppSettingsError`.
56
+ * @param type The type of the application error.
57
+ * @param message The error message.
58
+ * @param options Additional error options.
59
+ */
60
+ constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
61
+ }
42
62
  /**
43
63
  * Represents an error that occurs when loading the application script.
44
64
  */
@@ -15,6 +15,9 @@ export type AppEnv<TEnv extends ConfigEnvironment = ConfigEnvironment, TProps =
15
15
  props?: TProps;
16
16
  };
17
17
  export type ModuleDeps = [HttpModule, ServiceDiscoveryModule, EventModule];
18
+ export interface AppSettings {
19
+ [key: string]: unknown;
20
+ }
18
21
  export type AppType = 'standalone' | 'report' | 'launcher' | 'template';
19
22
  export type CurrentApp<TModules extends Array<AnyModule> = [], TEnv extends ConfigEnvironment = ConfigEnvironment> = IApp<TEnv, TModules> | null | undefined;
20
23
  type Nullable<T> = T | null | undefined;
@@ -1 +1 @@
1
- export declare const version = "6.0.2";
1
+ export declare const version = "6.1.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-module-app",
3
- "version": "6.0.2",
3
+ "version": "6.1.0",
4
4
  "description": "",
5
5
  "main": "dist/esm/index.js",
6
6
  "exports": {
@@ -53,19 +53,21 @@
53
53
  "directory": "packages/modules/app"
54
54
  },
55
55
  "dependencies": {
56
+ "fast-deep-equal": "^3.1.3",
56
57
  "immer": "^9.0.16",
57
58
  "rxjs": "^7.8.1",
59
+ "uuid": "^11.0.3",
58
60
  "zod": "^3.23.8",
59
- "@equinor/fusion-observable": "^8.4.2",
60
- "@equinor/fusion-query": "^5.1.4"
61
+ "@equinor/fusion-observable": "^8.4.3",
62
+ "@equinor/fusion-query": "^5.1.5"
61
63
  },
62
64
  "devDependencies": {
63
65
  "typescript": "^5.5.4",
64
66
  "@equinor/fusion-framework-module": "^4.3.5",
65
67
  "@equinor/fusion-framework-module-event": "^4.2.4",
68
+ "@equinor/fusion-framework-module-msal": "^3.1.5",
66
69
  "@equinor/fusion-framework-module-http": "^6.2.0",
67
- "@equinor/fusion-framework-module-service-discovery": "^8.0.2",
68
- "@equinor/fusion-framework-module-msal": "^3.1.5"
70
+ "@equinor/fusion-framework-module-service-discovery": "^8.0.3"
69
71
  },
70
72
  "scripts": {
71
73
  "build": "tsc -b"
package/src/AppClient.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { catchError, map, Observable, ObservableInput } from 'rxjs';
1
+ import { catchError, map, Observable, ObservableInput, tap } from 'rxjs';
2
2
 
3
3
  import { Query } from '@equinor/fusion-query';
4
4
  import { queryValue } from '@equinor/fusion-query/operators';
@@ -8,8 +8,8 @@ import { jsonSelector } from '@equinor/fusion-framework-module-http/selectors';
8
8
 
9
9
  import { ApiApplicationSchema } from './schemas';
10
10
 
11
- import type { AppConfig, AppManifest, ConfigEnvironment } from './types';
12
- import { AppConfigError, AppManifestError } from './errors';
11
+ import type { AppConfig, AppManifest, AppSettings, ConfigEnvironment } from './types';
12
+ import { AppConfigError, AppManifestError, AppSettingsError } from './errors';
13
13
  import { AppConfigSelector } from './AppClient.Selectors';
14
14
 
15
15
  export interface IAppClient extends Disposable {
@@ -30,6 +30,21 @@ export interface IAppClient extends Disposable {
30
30
  appKey: string;
31
31
  tag?: string;
32
32
  }) => ObservableInput<AppConfig<TType>>;
33
+
34
+ /**
35
+ * Fetch app settings by appKey
36
+ */
37
+ getAppSettings: (args: { appKey: string }) => ObservableInput<AppSettings>;
38
+
39
+ /**
40
+ * Set app settings by appKey
41
+ * @param args - Object with appKey and settings
42
+ * @returns ObservableInput<AppSettings>
43
+ */
44
+ updateAppSettings: (args: {
45
+ appKey: string;
46
+ settings: AppSettings;
47
+ }) => ObservableInput<AppSettings>;
33
48
  }
34
49
 
35
50
  /**
@@ -62,8 +77,12 @@ export class AppClient implements IAppClient {
62
77
  #manifest: Query<AppManifest, { appKey: string }>;
63
78
  #manifests: Query<AppManifest[], { filterByCurrentUser?: boolean } | undefined>;
64
79
  #config: Query<AppConfig, { appKey: string; tag?: string }>;
80
+ #settings: Query<AppSettings, { appKey: string; settings?: AppSettings }>;
81
+ #client: IHttpClient;
65
82
 
66
83
  constructor(client: IHttpClient) {
84
+ this.#client = client;
85
+
67
86
  const expire = 1 * 60 * 1000;
68
87
  this.#manifest = new Query<AppManifest, { appKey: string }>({
69
88
  client: {
@@ -90,8 +109,8 @@ export class AppClient implements IAppClient {
90
109
  'Api-Version': '1.0',
91
110
  },
92
111
  selector: async (res: Response) => {
93
- const response = (await jsonSelector(res)) as { value: unknown[] };
94
- return ApplicationSchema.array().parse(response.value);
112
+ const body = await res.json();
113
+ return body;
95
114
  },
96
115
  });
97
116
  },
@@ -114,7 +133,22 @@ export class AppClient implements IAppClient {
114
133
  key: (args) => JSON.stringify(args),
115
134
  expire,
116
135
  });
136
+
137
+ this.#settings = new Query<AppSettings, { appKey: string }>({
138
+ client: {
139
+ fn: ({ appKey }) => {
140
+ return client.json<AppSettings>(`/persons/me/apps/${appKey}/settings`, {
141
+ headers: {
142
+ 'Api-Version': '1.0',
143
+ },
144
+ });
145
+ },
146
+ },
147
+ key: (args) => args.appKey,
148
+ expire,
149
+ });
117
150
  }
151
+
118
152
  getAppManifest(args: { appKey: string }): Observable<AppManifest> {
119
153
  return this.#manifest.query(args).pipe(
120
154
  queryValue,
@@ -158,11 +192,56 @@ export class AppClient implements IAppClient {
158
192
  );
159
193
  }
160
194
 
195
+ getAppSettings(args: { appKey: string }): Observable<AppSettings> {
196
+ return this.#settings.query(args).pipe(
197
+ queryValue,
198
+ catchError((err) => {
199
+ /** extract cause, since error will be a `QueryError` */
200
+ const { cause } = err;
201
+ if (cause instanceof AppSettingsError) {
202
+ throw cause;
203
+ }
204
+ if (cause instanceof HttpResponseError) {
205
+ throw AppSettingsError.fromHttpResponse(cause.response, { cause });
206
+ }
207
+ throw new AppSettingsError('unknown', 'failed to load settings', { cause });
208
+ }),
209
+ );
210
+ }
211
+
212
+ updateAppSettings(args: { appKey: string; settings: AppSettings }): Observable<AppSettings> {
213
+ const { appKey, settings } = args;
214
+ return (
215
+ this.#client
216
+ // execute PUT request to update settings
217
+ .json$<AppSettings>(`/persons/me/apps/${appKey}/settings`, {
218
+ method: 'PUT',
219
+ body: settings,
220
+ headers: {
221
+ 'Api-Version': '1.0',
222
+ },
223
+ })
224
+ .pipe(
225
+ tap((value) => {
226
+ // update cache with new settings
227
+ this.#settings.mutate(
228
+ { appKey },
229
+ {
230
+ value,
231
+ updated: Date.now(),
232
+ },
233
+ );
234
+ }),
235
+ )
236
+ );
237
+ }
238
+
161
239
  [Symbol.dispose]() {
162
240
  console.warn('AppClient disposed');
163
241
  this.#manifest.complete();
164
242
  this.#manifests.complete();
165
243
  this.#config.complete();
244
+ this.#settings.complete();
166
245
  }
167
246
  }
168
247
 
@@ -12,7 +12,7 @@ import {
12
12
  import { ModuleType } from '@equinor/fusion-framework-module';
13
13
  import { EventModule } from '@equinor/fusion-framework-module-event';
14
14
 
15
- import type { AppConfig, AppManifest, ConfigEnvironment, CurrentApp } from './types';
15
+ import type { AppConfig, AppManifest, AppSettings, ConfigEnvironment, CurrentApp } from './types';
16
16
 
17
17
  import { App, filterEmpty, IApp } from './app/App';
18
18
  import { AppModuleConfig } from './AppConfigurator';
@@ -122,6 +122,23 @@ export class AppModuleProvider {
122
122
  return from(this.#appClient.getAppConfig<TType>({ appKey, tag }));
123
123
  }
124
124
 
125
+ /**
126
+ * fetch user settings for an application
127
+ * @param appKey - application key
128
+ */
129
+ public getAppSettings(appKey: string): Observable<AppSettings> {
130
+ return from(this.#appClient.getAppSettings({ appKey }));
131
+ }
132
+
133
+ /**
134
+ * Put user settings for an application
135
+ * @param appKey - application key
136
+ * @param settings - The settings to add save
137
+ */
138
+ public updateAppSettings(appKey: string, settings: AppSettings): Observable<AppSettings> {
139
+ return from(this.#appClient.updateAppSettings({ appKey, settings }));
140
+ }
141
+
125
142
  /**
126
143
  * set the current application, will internally resolve manifest
127
144
  * @param appKey - application key