Skip to content

Commit c9d7bd4

Browse files
authored
feat(module-services): add App State API client (#5214)
* feat(module-services): add App State API client Implements a versioned domain client for the App State API (https://appstate.ci.api.fusion-dev.net/openapi/v1.json), following the same versioned-endpoint pattern as bookmarks/context/notification. - AppStateApiClient exposed via new ./app-state subpath export - ApiProvider.createAppStateClient() factory method - 8 operations: list/get/wipe own app state, GDPR full erasure, and admin list/get/wipe per-user or per-app state * fix(module-services): preserve caller headers on App State wipe, fix TSDoc example - me.delete.ts / admin-app.delete.ts: merge caller headers via Headers instead of\n Object.assign shallow-replacing the headers object, so callers can add headers\n without stripping the mandatory X-Confirm-Wipe header\n- client.ts / changeset: fix TSDoc/example importing HttpClient from the wrong\n path and constructing it with an unsupported { baseUri } argument\n- client.ts: remove inaccurate "defaults to 'json'" claim on @template TMethod
1 parent 0332b80 commit c9d7bd4

18 files changed

Lines changed: 1592 additions & 2 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
"@equinor/fusion-framework-module-services": minor
3+
---
4+
5+
Add `AppStateApiClient` for the Fusion App State API, exposed via a new `./app-state` subpath export and `ApiProvider.createAppStateClient()`.
6+
7+
The client follows the same versioned method pattern as `bookmarks`/`context`/`notification`: every method takes an API version as its first argument (currently only `'v1'` is supported).
8+
9+
```typescript
10+
import { AppStateApiClient } from '@equinor/fusion-framework-module-services/app-state';
11+
import { HttpClient } from '@equinor/fusion-framework-module-http/client';
12+
13+
const httpClient = new HttpClient('https://app-state-api.example.com/');
14+
const client = new AppStateApiClient(httpClient, 'json');
15+
16+
const apps = await client.listMyApps('v1');
17+
await client.wipeMyAppState('v1', { appKey: 'my-app' });
18+
```
19+
20+
Supported operations: `listMyApps`, `getMyAppState`, `wipeMyAppState`, `wipeAllMyState`, `listAppUsers`, `getUserAppState`, `wipeUserAppState`, `wipeAllAppUsersState`.

packages/modules/services/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# @equinor/fusion-framework-module-services
22

3-
Typed API service clients for the Fusion Framework. Provides factory-based access to platform backend services (bookmarks, context, notification, people) with versioned endpoints, automatic HTTP client resolution, and response validation.
3+
Typed API service clients for the Fusion Framework. Provides factory-based access to platform backend services (app state, bookmarks, context, notification, people) with versioned endpoints, automatic HTTP client resolution, and response validation.
44

55
## Features
66

7-
- **Domain-specific API clients**`BookmarksApiClient`, `ContextApiClient`, `NotificationApiClient`, `PeopleApiClient`
7+
- **Domain-specific API clients**`AppStateApiClient`, `BookmarksApiClient`, `ContextApiClient`, `NotificationApiClient`, `PeopleApiClient`
88
- **Versioned endpoints** — each service exposes version-aware methods (e.g. `v1`, `v2`, `v4`) with type-safe request/response shapes
99
- **Dual consumption patterns** — every endpoint supports both `Promise` (`json`) and observable (`json$`) return types
1010
- **Automatic HTTP client resolution** — resolves named clients through the HTTP module, falling back to service-discovery
@@ -53,6 +53,11 @@ export const configure = (configurator) => {
5353
Access the services provider at runtime to create domain clients:
5454

5555
```ts
56+
// App State
57+
const appState = await provider.services.createAppStateClient('json');
58+
const apps = await appState.listMyApps('v1');
59+
await appState.wipeMyAppState('v1', { appKey: 'my-app' });
60+
5661
// Bookmarks
5762
const bookmarks = await provider.services.createBookmarksClient('json');
5863
const allBookmarks = await bookmarks.query('v1');
@@ -96,6 +101,7 @@ const photo = await people.photo('v2', 'blob', { azureId: 'azure-unique-id' });
96101

97102
| Client | Import path | Services |
98103
|---|---|---|
104+
| `AppStateApiClient` | `@equinor/fusion-framework-module-services/app-state` | Get/wipe own app state, admin get/wipe per-user or per-app state |
99105
| `BookmarksApiClient` | `@equinor/fusion-framework-module-services/bookmarks` | CRUD bookmarks, favourites |
100106
| `ContextApiClient` | `@equinor/fusion-framework-module-services/context` | Get, query, related contexts |
101107
| `NotificationApiClient` | `@equinor/fusion-framework-module-services/notification` | CRUD notifications, settings |

packages/modules/services/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
"import": "./dist/esm/people/person-photo/index.js",
6767
"types": "./dist/types/people/person-photo/index.d.ts"
6868
},
69+
"./app-state": {
70+
"import": "./dist/esm/app-state/index.js",
71+
"types": "./dist/types/app-state/index.d.ts"
72+
},
6973
"./errors": {
7074
"import": "./dist/esm/errors.js",
7175
"types": "./dist/types/errors.d.ts"
@@ -119,6 +123,9 @@
119123
"people/photo": [
120124
"./dist/types/people/person-photo/index.d.ts"
121125
],
126+
"app-state": [
127+
"./dist/types/app-state/index.d.ts"
128+
],
122129
"errors": [
123130
"./dist/types/errors.d.ts"
124131
]
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
import type { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client';
2+
import type { ClientMethod } from '../types';
3+
4+
import {
5+
type ListMyAppsVersion,
6+
type ListMyAppsResponse,
7+
type ListMyAppsResult,
8+
listMyApps,
9+
} from './endpoints/me-apps.get';
10+
import {
11+
type GetMyAppStateVersion,
12+
type GetMyAppStateArg,
13+
type GetMyAppStateResponse,
14+
type GetMyAppStateResult,
15+
getMyAppState,
16+
} from './endpoints/me-app.get';
17+
import {
18+
type WipeMyAppStateVersion,
19+
type WipeMyAppStateArg,
20+
type WipeMyAppStateResponse,
21+
type WipeMyAppStateResult,
22+
wipeMyAppState,
23+
} from './endpoints/me-app.delete';
24+
import {
25+
type WipeAllMyStateVersion,
26+
type WipeAllMyStateResponse,
27+
type WipeAllMyStateResult,
28+
wipeAllMyState,
29+
} from './endpoints/me.delete';
30+
import {
31+
type ListAppUsersVersion,
32+
type ListAppUsersArg,
33+
type ListAppUsersResponse,
34+
type ListAppUsersResult,
35+
listAppUsers,
36+
} from './endpoints/admin-app-users.get';
37+
import {
38+
type GetUserAppStateVersion,
39+
type GetUserAppStateArg,
40+
type GetUserAppStateResponse,
41+
type GetUserAppStateResult,
42+
getUserAppState,
43+
} from './endpoints/admin-app-user.get';
44+
import {
45+
type WipeUserAppStateVersion,
46+
type WipeUserAppStateArg,
47+
type WipeUserAppStateResponse,
48+
type WipeUserAppStateResult,
49+
wipeUserAppState,
50+
} from './endpoints/admin-app-user.delete';
51+
import {
52+
type WipeAllAppUsersStateVersion,
53+
type WipeAllAppUsersStateArg,
54+
type WipeAllAppUsersStateResponse,
55+
type WipeAllAppUsersStateResult,
56+
wipeAllAppUsersState,
57+
} from './endpoints/admin-app.delete';
58+
59+
/**
60+
* Provides a client interface for interacting with the App State API.
61+
*
62+
* Exposes the current user's own per-app state under `/persons/me/...`, and,
63+
* for callers holding the `Fusion.AppState.AppAdmin` (scoped to an app) or
64+
* `Fusion.AppState.Admin` role, per-user administrative state management
65+
* under `/admin/...`.
66+
*
67+
* The upstream OpenAPI spec does not publish response body schemas — only
68+
* status-code descriptions — so every method's response defaults to
69+
* `unknown`. Supply a `TResponse` type argument on the individual method call
70+
* to narrow the parsed response.
71+
*
72+
* @example
73+
* ```typescript
74+
* import { AppStateApiClient } from '@equinor/fusion-framework-module-services/app-state';
75+
* import { HttpClient } from '@equinor/fusion-framework-module-http/client';
76+
*
77+
* const httpClient = new HttpClient('https://my-app-state-api.com/');
78+
* const client = new AppStateApiClient(httpClient, 'json');
79+
*
80+
* const apps = await client.listMyApps('v1');
81+
* await client.wipeMyAppState('v1', { appKey: 'my-app' });
82+
* ```
83+
*
84+
* @template TMethod - The client method to use for the request.
85+
* @template TClient - The HTTP client to use for executing the request.
86+
*/
87+
export class AppStateApiClient<
88+
TMethod extends keyof ClientMethod<unknown> = keyof ClientMethod<unknown>,
89+
TClient extends IHttpClient = IHttpClient,
90+
> {
91+
/**
92+
* Constructs a new instance of the AppStateApiClient class.
93+
*
94+
* @param _client - The client instance to use for making API requests.
95+
* @param _method - The client method to use for API requests.
96+
*/
97+
constructor(
98+
protected _client: TClient,
99+
protected _method: TMethod,
100+
) {}
101+
102+
/**
103+
* Lists all apps that hold state for the current user, including a
104+
* document count and storage size per app.
105+
*
106+
* @template TVersion - The version of the API to call.
107+
* @template TResponse - The type of the result of the `listMyApps` function.
108+
* @param version - The API version to use.
109+
* @param init - Optional request initialization options.
110+
* @returns The list of apps with stored state for the current user.
111+
*/
112+
public listMyApps<TVersion extends ListMyAppsVersion, TResponse = ListMyAppsResponse<TVersion>>(
113+
version: TVersion,
114+
init?: ClientRequestInit<TClient, TResponse>,
115+
): ListMyAppsResult<TVersion, TMethod, TResponse> {
116+
const fn = listMyApps(version, this._client, this._method);
117+
return fn(init);
118+
}
119+
120+
/**
121+
* Gets the current user's state info for a single app.
122+
*
123+
* @template TVersion - The version of the API to call.
124+
* @template TResponse - The type of the result of the `getMyAppState` function.
125+
* @param version - The API version to use.
126+
* @param args - The app-registration key identifying the app.
127+
* @param init - Optional request initialization options.
128+
* @returns The current user's state info for the given app.
129+
*/
130+
public getMyAppState<
131+
TVersion extends GetMyAppStateVersion,
132+
TResponse = GetMyAppStateResponse<TVersion>,
133+
>(
134+
version: TVersion,
135+
args: GetMyAppStateArg<TVersion>,
136+
init?: ClientRequestInit<TClient, TResponse>,
137+
): GetMyAppStateResult<TVersion, TMethod, TResponse> {
138+
const fn = getMyAppState(version, this._client, this._method);
139+
return fn(args, init);
140+
}
141+
142+
/**
143+
* Wipes the current user's state for a single app (deletes and recreates
144+
* the user's state database for that app).
145+
*
146+
* @template TVersion - The version of the API to call.
147+
* @template TResponse - The type of the result of the `wipeMyAppState` function.
148+
* @param version - The API version to use.
149+
* @param args - The app-registration key identifying the app.
150+
* @param init - Optional request initialization options.
151+
* @returns The result of the wipe operation.
152+
*/
153+
public wipeMyAppState<
154+
TVersion extends WipeMyAppStateVersion,
155+
TResponse = WipeMyAppStateResponse<TVersion>,
156+
>(
157+
version: TVersion,
158+
args: WipeMyAppStateArg<TVersion>,
159+
init?: ClientRequestInit<TClient, TResponse>,
160+
): WipeMyAppStateResult<TVersion, TMethod, TResponse> {
161+
const fn = wipeMyAppState(version, this._client, this._method);
162+
return fn(args, init);
163+
}
164+
165+
/**
166+
* Wipes all of the current user's state across every app (GDPR erasure).
167+
*
168+
* Always sends the `X-Confirm-Wipe: true` header the API requires for
169+
* this operation; pass `init.headers` to add further headers.
170+
*
171+
* @template TVersion - The version of the API to call.
172+
* @template TResponse - The type of the result of the `wipeAllMyState` function.
173+
* @param version - The API version to use.
174+
* @param init - Optional request initialization options.
175+
* @returns The result of the wipe operation.
176+
*/
177+
public wipeAllMyState<
178+
TVersion extends WipeAllMyStateVersion,
179+
TResponse = WipeAllMyStateResponse<TVersion>,
180+
>(
181+
version: TVersion,
182+
init?: ClientRequestInit<TClient, TResponse>,
183+
): WipeAllMyStateResult<TVersion, TMethod, TResponse> {
184+
const fn = wipeAllMyState(version, this._client, this._method);
185+
return fn(init);
186+
}
187+
188+
/**
189+
* Lists the users who hold state for a given app.
190+
*
191+
* Requires the `Fusion.AppState.AppAdmin` (scoped to the app) or
192+
* `Fusion.AppState.Admin` role.
193+
*
194+
* @template TVersion - The version of the API to call.
195+
* @template TResponse - The type of the result of the `listAppUsers` function.
196+
* @param version - The API version to use.
197+
* @param args - The app-registration key identifying the app.
198+
* @param init - Optional request initialization options.
199+
* @returns The list of users with stored state for the given app.
200+
*/
201+
public listAppUsers<
202+
TVersion extends ListAppUsersVersion,
203+
TResponse = ListAppUsersResponse<TVersion>,
204+
>(
205+
version: TVersion,
206+
args: ListAppUsersArg<TVersion>,
207+
init?: ClientRequestInit<TClient, TResponse>,
208+
): ListAppUsersResult<TVersion, TMethod, TResponse> {
209+
const fn = listAppUsers(version, this._client, this._method);
210+
return fn(args, init);
211+
}
212+
213+
/**
214+
* Gets a specific user's state info for a given app.
215+
*
216+
* Requires the `Fusion.AppState.AppAdmin` (scoped to the app) or
217+
* `Fusion.AppState.Admin` role.
218+
*
219+
* @template TVersion - The version of the API to call.
220+
* @template TResponse - The type of the result of the `getUserAppState` function.
221+
* @param version - The API version to use.
222+
* @param args - The app-registration key and user object ID (OID).
223+
* @param init - Optional request initialization options.
224+
* @returns The user's state info for the given app.
225+
*/
226+
public getUserAppState<
227+
TVersion extends GetUserAppStateVersion,
228+
TResponse = GetUserAppStateResponse<TVersion>,
229+
>(
230+
version: TVersion,
231+
args: GetUserAppStateArg<TVersion>,
232+
init?: ClientRequestInit<TClient, TResponse>,
233+
): GetUserAppStateResult<TVersion, TMethod, TResponse> {
234+
const fn = getUserAppState(version, this._client, this._method);
235+
return fn(args, init);
236+
}
237+
238+
/**
239+
* Wipes a specific user's state for a given app.
240+
*
241+
* Requires the `Fusion.AppState.AppAdmin` (scoped to the app) or
242+
* `Fusion.AppState.Admin` role.
243+
*
244+
* @template TVersion - The version of the API to call.
245+
* @template TResponse - The type of the result of the `wipeUserAppState` function.
246+
* @param version - The API version to use.
247+
* @param args - The app-registration key and user object ID (OID).
248+
* @param init - Optional request initialization options.
249+
* @returns The result of the wipe operation.
250+
*/
251+
public wipeUserAppState<
252+
TVersion extends WipeUserAppStateVersion,
253+
TResponse = WipeUserAppStateResponse<TVersion>,
254+
>(
255+
version: TVersion,
256+
args: WipeUserAppStateArg<TVersion>,
257+
init?: ClientRequestInit<TClient, TResponse>,
258+
): WipeUserAppStateResult<TVersion, TMethod, TResponse> {
259+
const fn = wipeUserAppState(version, this._client, this._method);
260+
return fn(args, init);
261+
}
262+
263+
/**
264+
* Wipes all users' state for a given app.
265+
*
266+
* Requires the `Fusion.AppState.AppAdmin` (scoped to the app) or
267+
* `Fusion.AppState.Admin` role, and always sends the `X-Confirm-Wipe: true`
268+
* header the API requires for this operation.
269+
*
270+
* @template TVersion - The version of the API to call.
271+
* @template TResponse - The type of the result of the `wipeAllAppUsersState` function.
272+
* @param version - The API version to use.
273+
* @param args - The app-registration key identifying the app.
274+
* @param init - Optional request initialization options.
275+
* @returns The result of the wipe operation.
276+
*/
277+
public wipeAllAppUsersState<
278+
TVersion extends WipeAllAppUsersStateVersion,
279+
TResponse = WipeAllAppUsersStateResponse<TVersion>,
280+
>(
281+
version: TVersion,
282+
args: WipeAllAppUsersStateArg<TVersion>,
283+
init?: ClientRequestInit<TClient, TResponse>,
284+
): WipeAllAppUsersStateResult<TVersion, TMethod, TResponse> {
285+
const fn = wipeAllAppUsersState(version, this._client, this._method);
286+
return fn(args, init);
287+
}
288+
}
289+
290+
export default AppStateApiClient;

0 commit comments

Comments
 (0)