Skip to content

Commit f7f50bf

Browse files
committed
feat(people-resolver): implement person resolve API and client integration
- Added new API types for resolving person data. - Introduced client methods for resolving persons, including endpoint generation and parameter handling. - Updated the PeopleApiClient to include resolve functionality. - Enhanced PersonController to support resolving persons by Azure IDs. - Integrated resolve functionality into the createResolver method. This update expands the capabilities of the people resolver, allowing for more comprehensive person data retrieval.
1 parent 225749a commit f7f50bf

File tree

10 files changed

+145
-23
lines changed

10 files changed

+145
-23
lines changed

packages/modules/services/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@
5454
"import": "./dist/esm/people/query/index.js",
5555
"types": "./dist/types/people/query/index.d.ts"
5656
},
57+
"./people/suggest": {
58+
"import": "./dist/esm/people/suggest/index.js",
59+
"types": "./dist/types/people/suggest/index.d.ts"
60+
},
61+
"./people/resolve": {
62+
"import": "./dist/esm/people/resolve/index.js",
63+
"types": "./dist/types/people/resolve/index.d.ts"
64+
},
5765
"./people/photo": {
5866
"import": "./dist/esm/people/person-photo/index.js",
5967
"types": "./dist/types/people/person-photo/index.d.ts"

packages/modules/services/src/people/api-models.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,13 @@ export type ApiSuggestions = {
8686
'@nextPage': string | null;
8787
value: Array<ApiSuggestionValue>;
8888
};
89+
90+
export type ApiResolveItem = {
91+
success: boolean;
92+
statusCode: number;
93+
errorMessage: string | null;
94+
indentifier: string;
95+
account: ApiSuggestionValue | null;
96+
};
97+
98+
export type ApiResolved = Array<ApiResolveItem>;

packages/modules/services/src/people/client.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ import {
3333
type ApiResult as PersonSuggestResult,
3434
} from './suggest';
3535

36+
import {
37+
client as personResolveClient,
38+
type ApiResponse as PersonResolveApiResponse,
39+
type ApiResult as PersonResolveResult,
40+
} from './resolve';
41+
3642
export class PeopleApiClient<
3743
// TMethod extends keyof ClientMethod<unknown> = keyof ClientMethod<unknown>,
3844
TClient extends IHttpClient = IHttpClient,
@@ -110,16 +116,20 @@ export class PeopleApiClient<
110116
const fn = personSuggestClient<TMethod, TClient>(this._client, method);
111117
return fn<TResult>(init);
112118
}
113-
}
114119

115-
// const oo = new PeopleApiClient(null).get('v4', 'fetch', { azureId: '123' });
116-
// const o2 = await new PeopleApiClient(null).get('v4', 'fetch', {
117-
// azureId: '123',
118-
// expand: ['roles'],
119-
// });
120-
// const o3 = await new PeopleApiClient(null).get('v4', 'fetch', {
121-
// azureId: '123',
122-
// expand: ['companies', 'roles'],
123-
// });
120+
/**
121+
* Resolve person service
122+
*/
123+
public resolve<
124+
TResult = PersonResolveApiResponse,
125+
TMethod extends keyof ClientMethod<TResult> = keyof ClientMethod<TResult>,
126+
>(
127+
method: TMethod,
128+
init?: ClientRequestInit<TClient, TResult>,
129+
): PersonResolveResult<TMethod, TResult> {
130+
const fn = personResolveClient<TMethod, TClient>(this._client, method);
131+
return fn<TResult>(init);
132+
}
133+
}
124134

125135
export default PeopleApiClient;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client';
2+
3+
import { generateParameters } from './generate-parameters';
4+
5+
import type { ClientMethod } from '../../types';
6+
import type { ApiResponse, ApiResult } from './types';
7+
8+
/**
9+
* Method for fetching context item from context service
10+
* @param client - client for execution of request
11+
* @param method - client method to call
12+
*/
13+
export const client =
14+
<
15+
TMethod extends keyof ClientMethod = keyof ClientMethod,
16+
TClient extends IHttpClient = IHttpClient,
17+
>(
18+
client: TClient,
19+
method: TMethod = 'json' as TMethod,
20+
) =>
21+
<T = ApiResponse>(
22+
init?: ClientRequestInit<TClient, T>,
23+
): ApiResult<TMethod, T> =>
24+
client[method](...generateParameters<T, TClient>(init)) as ApiResult<
25+
TMethod,
26+
T
27+
>;
28+
29+
export default client;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Method for generating endpoint for getting people suggestions
3+
*/
4+
export const generateEndpoint = () => {
5+
return '/people-picker/resolve';
6+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client';
2+
3+
import { generateEndpoint } from './generate-endpoint';
4+
5+
import type { ApiClientArguments } from '../../types';
6+
7+
/** function for creating http client arguments */
8+
export const generateParameters = <
9+
TResult,
10+
TClient extends IHttpClient = IHttpClient,
11+
>(
12+
init?: ClientRequestInit<TClient, TResult>,
13+
): ApiClientArguments<TClient, TResult> => {
14+
const path = generateEndpoint();
15+
return [path, init];
16+
};
17+
18+
export default generateParameters;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export { client, default } from './client';
2+
3+
export { generateEndpoint } from './generate-endpoint';
4+
export { generateParameters } from './generate-parameters';
5+
6+
export * from './types';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { ApiResolved } from '../api-models';
2+
import type { ClientMethod } from '../../types';
3+
4+
export type ApiResponse = ApiResolved;
5+
6+
export type ApiResult<
7+
TMethod extends keyof ClientMethod<unknown> = keyof ClientMethod<unknown>,
8+
TResult = ApiResponse,
9+
> = ClientMethod<TResult>[TMethod];

packages/react/components/people-resolver/src/PersonController.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { EMPTY, type Observable, concat, from, fromEvent, of } from 'rxjs';
2-
import { catchError, filter, find, map, switchMap, take, takeUntil } from 'rxjs/operators';
2+
import { catchError, filter, find, map, switchMap, take, takeUntil, tap } from 'rxjs/operators';
33

44
import type { ApiPerson, PeopleApiClient } from '@equinor/fusion-framework-module-services/people';
55
import { isApiPerson } from '@equinor/fusion-framework-module-services/people/utils';
66
import type { ApiResponse as GetPersonApiResponse } from '@equinor/fusion-framework-module-services/people/get';
77
import type { ApiResponse as QueryPersonApiResponse } from '@equinor/fusion-framework-module-services/people/query';
88
import type { ApiResponse as SuggestPersonApiResponse } from '@equinor/fusion-framework-module-services/people/suggest';
9+
import type { ApiResponse as ResolvePersonApiResponse } from '@equinor/fusion-framework-module-services/people/resolve';
910
import { Query } from '@equinor/fusion-query';
1011
import { queryValue } from '@equinor/fusion-query/operators';
1112

@@ -47,6 +48,7 @@ export interface IPersonController {
4748
getPhoto(args: ResolverArgs<MatcherArgs>): Observable<string>;
4849
search(args: ResolverArgs<{ search: string }>): Observable<PersonSearchResult>;
4950
suggest(args: ResolverArgs<{ search: string }>): Observable<SuggestPersonApiResponse>;
51+
resolve(args: ResolverArgs<{ azureIds: string[] }>): Observable<ResolvePersonApiResponse>;
5052
}
5153

5254
export type PersonControllerOptions = {
@@ -58,6 +60,7 @@ export class PersonController implements IPersonController {
5860
#personSearchQuery: Query<PersonSearchResult, ResolverArgs<{ search: string }>>;
5961
#personPhotoQuery: Query<Blob, ResolverArgs<{ azureId: string }>>;
6062
#personSuggestQuery: Query<SuggestPersonApiResponse, ResolverArgs<{ search: string }>>;
63+
#personResolveQuery: Query<ResolvePersonApiResponse, ResolverArgs<{ azureIds: string[] }>>;
6164

6265
constructor(client: PeopleApiClient, options?: PersonControllerOptions) {
6366
const expire = 3 * 60 * 1000;
@@ -125,15 +128,29 @@ export class PersonController implements IPersonController {
125128
},
126129
},
127130
});
131+
this.#personResolveQuery = new Query({
132+
expire,
133+
queueOperator: 'merge',
134+
key: ({ azureIds }) => azureIds.join(),
135+
client: {
136+
fn: ({ azureIds }, signal) => {
137+
return client.resolve('json$', { method: 'POST', body: JSON.stringify({ azureUniqueIds: azureIds }), signal });
138+
},
139+
},
140+
});
128141
}
129142

130143
public suggest(args: ResolverArgs<{ search: string }>): Observable<SuggestPersonApiResponse> {
131144
const { search, signal } = args;
132145
return this.#personSuggestQuery.query({ search }, { signal }).pipe(queryValue);
133146
}
134147

135-
public search(args: { search: string; signal?: AbortSignal }): Observable<PersonSearchResult> {
148+
public resolve(args: ResolverArgs<{ azureIds: string[] }>): Observable<ResolvePersonApiResponse> {
149+
const { azureIds, signal } = args;
150+
return this.#personResolveQuery.query({ azureIds }, { signal }).pipe(queryValue);
151+
}
136152

153+
public search(args: { search: string; signal?: AbortSignal }): Observable<PersonSearchResult> {
137154
const { search, signal } = args;
138155
return this.#personSearchQuery.query({ search }, { signal }).pipe(queryValue);
139156
}
@@ -218,25 +235,23 @@ export class PersonController implements IPersonController {
218235
find(isApiPerson('v2')),
219236
),
220237
).pipe(
221-
/** */
222238
find(isApiPerson('v2')),
223239
filter(isApiPerson('v2')),
224-
filter(isApiPerson('v2')),
225240
takeUntil(abort$),
226241
);
227242
}
228243

229244
protected _getPersonPhotoByAzureId(azureId: string, signal?: AbortSignal): Observable<string> {
230245
return this.#personPhotoQuery.query({ azureId }, { signal }).pipe(
231-
/** */
246+
/** make subscription cold */
232247
take(1),
233248
map((result) => URL.createObjectURL(result.value)),
234249
);
235250
}
236251

237252
protected _getPersonPhotoByUpn(upn: string, signal?: AbortSignal) {
238253
return this._getPersonInfoByUpn(upn, signal).pipe(
239-
/** */
254+
/** make subscription cold */
240255
take(1),
241256
switchMap((x) => this._getPersonPhotoByAzureId(x.azureUniqueId, signal)),
242257
);
@@ -272,4 +287,18 @@ export class PersonController implements IPersonController {
272287
filter(isApiPerson('v2')),
273288
);
274289
}
290+
291+
protected _resolveCache$(args: { azureIds: string[] }): Observable<ResolvePersonApiResponse> {
292+
const { azureIds } = args;
293+
return this.#personResolveQuery.cache.state$.pipe(
294+
/** make subscription cold */
295+
take(1),
296+
switchMap((entry) => {
297+
return from(Object.values(entry)).pipe(
298+
filter(({ args }) => args.azureIds.join() === azureIds.join()),
299+
map(({ value }) => value),
300+
);
301+
}),
302+
);
303+
}
275304
}

packages/react/components/people-resolver/src/create-resolver.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,17 @@ export const createResolver = (controller: IPersonController): PersonResolver =>
3232
({
3333
...x,
3434
azureId: x.azureUniqueId,
35-
// TODO
36-
// @asbjornhaland
37-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
38-
// @ts-ignore https://github.com/equinor/fusion-web-components/issues/804
3935
}) as unknown as PersonInfo,
4036
),
4137
),
4238
),
4339
);
4440
},
4541
suggest(args) {
46-
return firstValueFrom(
47-
controller.suggest(args)
48-
);
42+
return firstValueFrom(controller.suggest(args));
43+
},
44+
resolve(args) {
45+
return firstValueFrom(controller.resolve(args));
4946
},
5047
});
5148

0 commit comments

Comments
 (0)