Skip to content

Commit 225749a

Browse files
committed
feat(people-resolver): add person suggestion API and client integration
1 parent 60755b2 commit 225749a

File tree

9 files changed

+165
-17
lines changed

9 files changed

+165
-17
lines changed

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

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,41 @@ export type ApiPersonMap = {
4848
export type ApiPerson<T extends keyof typeof ApiVersion | ApiVersion> = T extends ApiVersion
4949
? ApiPersonMap[T]
5050
: T extends keyof typeof ApiVersion
51-
? ApiPersonMap[(typeof ApiVersion)[T]]
52-
: unknown;
51+
? ApiPersonMap[(typeof ApiVersion)[T]]
52+
: unknown;
53+
54+
55+
export type ApiSuggestionPerson = {
56+
accountType?: 'Employee' | 'Consultant' | 'Enterprise' | 'EnterpriseExternal' | 'External' | 'Local' | 'TemporaryEmployee' | 'Unknown';
57+
jobTitle?: string;
58+
department?: string;
59+
fullDepartment?: string;
60+
employeeNumber?: string;
61+
managerAzureUniqueId?: string;
62+
upn?: string;
63+
mobilePhone?: string;
64+
}
65+
66+
export type ApiSuggestionApplication = {
67+
applicationId: string;
68+
applicationName?: string;
69+
servicePrincipalType: 'Application' | 'ManagedIdentity' | 'ServicePrincipal';
70+
};
71+
72+
export type ApiSuggestionValue = {
73+
azureUniqueId: string;
74+
name?: string;
75+
accountType: 'Person' | 'SystemAccount' | 'Unknown';
76+
person?: ApiSuggestionPerson;
77+
application?: ApiSuggestionApplication;
78+
avatarColor: string;
79+
avatarUrl: string;
80+
isExpired: boolean;
81+
};
82+
83+
export type ApiSuggestions = {
84+
totalCount: number;
85+
count: number;
86+
'@nextPage': string | null;
87+
value: Array<ApiSuggestionValue>;
88+
};

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ import {
2727
type ApiRequestArgs as PersonPhotoApiRequestArgs,
2828
} from './person-photo';
2929

30+
import {
31+
client as personSuggestClient,
32+
type ApiResponse as PersonSuggestApiResponse,
33+
type ApiResult as PersonSuggestResult,
34+
} from './suggest';
35+
3036
export class PeopleApiClient<
3137
// TMethod extends keyof ClientMethod<unknown> = keyof ClientMethod<unknown>,
3238
TClient extends IHttpClient = IHttpClient,
@@ -35,7 +41,7 @@ export class PeopleApiClient<
3541
return ApiVersion;
3642
}
3743

38-
constructor(protected _client: TClient) {}
44+
constructor(protected _client: TClient) { }
3945

4046
/**
4147
* Fetch person by id
@@ -90,6 +96,20 @@ export class PeopleApiClient<
9096
const fn = personPhotoClient<TVersion, TMethod, TClient>(this._client, version, method);
9197
return fn<TResult>(args, init);
9298
}
99+
100+
/**
101+
* Suggest person service
102+
*/
103+
public suggest<
104+
TResult = PersonSuggestApiResponse,
105+
TMethod extends keyof ClientMethod<TResult> = keyof ClientMethod<TResult>,
106+
>(
107+
method: TMethod,
108+
init?: ClientRequestInit<TClient, TResult>,
109+
): PersonSuggestResult<TMethod, TResult> {
110+
const fn = personSuggestClient<TMethod, TClient>(this._client, method);
111+
return fn<TResult>(init);
112+
}
93113
}
94114

95115
// const oo = new PeopleApiClient(null).get('v4', 'fetch', { azureId: '123' });
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/suggestions';
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 { ApiSuggestions } from '../api-models';
2+
import type { ClientMethod } from '../../types';
3+
4+
export type ApiResponse = ApiSuggestions;
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: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ApiPerson, PeopleApiClient } from '@equinor/fusion-framework-modul
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';
8+
import type { ApiResponse as SuggestPersonApiResponse } from '@equinor/fusion-framework-module-services/people/suggest';
89
import { Query } from '@equinor/fusion-query';
910
import { queryValue } from '@equinor/fusion-query/operators';
1011

@@ -25,26 +26,27 @@ type ResolverArgs<T> = T extends object
2526

2627
const personMatcher =
2728
(args: MatcherArgs) =>
28-
<T extends { azureUniqueId?: string; upn?: string }>(value: T): value is T => {
29-
const { azureId, upn } = args;
30-
if (azureId && upn) {
31-
return (
32-
value.upn?.toLocaleLowerCase() === upn.toLocaleLowerCase() &&
33-
value.azureUniqueId === azureId
34-
);
35-
} else if (azureId) {
36-
return value.azureUniqueId === azureId;
37-
} else if (upn) {
38-
return value.upn?.toLocaleLowerCase() === upn.toLocaleLowerCase();
39-
}
40-
return false;
41-
};
29+
<T extends { azureUniqueId?: string; upn?: string }>(value: T): value is T => {
30+
const { azureId, upn } = args;
31+
if (azureId && upn) {
32+
return (
33+
value.upn?.toLocaleLowerCase() === upn.toLocaleLowerCase() &&
34+
value.azureUniqueId === azureId
35+
);
36+
} else if (azureId) {
37+
return value.azureUniqueId === azureId;
38+
} else if (upn) {
39+
return value.upn?.toLocaleLowerCase() === upn.toLocaleLowerCase();
40+
}
41+
return false;
42+
};
4243

4344
export interface IPersonController {
4445
getPerson(args: ResolverArgs<MatcherArgs>): Observable<GetPersonResult>;
4546
getPersonInfo(args: ResolverArgs<MatcherArgs>): Observable<ApiPerson<'v2'>>;
4647
getPhoto(args: ResolverArgs<MatcherArgs>): Observable<string>;
4748
search(args: ResolverArgs<{ search: string }>): Observable<PersonSearchResult>;
49+
suggest(args: ResolverArgs<{ search: string }>): Observable<SuggestPersonApiResponse>;
4850
}
4951

5052
export type PersonControllerOptions = {
@@ -55,6 +57,7 @@ export class PersonController implements IPersonController {
5557
#personQuery: Query<GetPersonResult, ResolverArgs<{ azureId: string }>>;
5658
#personSearchQuery: Query<PersonSearchResult, ResolverArgs<{ search: string }>>;
5759
#personPhotoQuery: Query<Blob, ResolverArgs<{ azureId: string }>>;
60+
#personSuggestQuery: Query<SuggestPersonApiResponse, ResolverArgs<{ search: string }>>;
5861

5962
constructor(client: PeopleApiClient, options?: PersonControllerOptions) {
6063
const expire = 3 * 60 * 1000;
@@ -112,9 +115,25 @@ export class PersonController implements IPersonController {
112115
},
113116
},
114117
});
118+
this.#personSuggestQuery = new Query({
119+
expire,
120+
queueOperator: 'merge',
121+
key: ({ search }) => search,
122+
client: {
123+
fn: ({ search }, signal) => {
124+
return client.suggest('json$', { method: 'POST', body: JSON.stringify({ queryString: search }), signal });
125+
},
126+
},
127+
});
128+
}
129+
130+
public suggest(args: ResolverArgs<{ search: string }>): Observable<SuggestPersonApiResponse> {
131+
const { search, signal } = args;
132+
return this.#personSuggestQuery.query({ search }, { signal }).pipe(queryValue);
115133
}
116134

117135
public search(args: { search: string; signal?: AbortSignal }): Observable<PersonSearchResult> {
136+
118137
const { search, signal } = args;
119138
return this.#personSearchQuery.query({ search }, { signal }).pipe(queryValue);
120139
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ export const createResolver = (controller: IPersonController): PersonResolver =>
4242
),
4343
);
4444
},
45+
suggest(args) {
46+
return firstValueFrom(
47+
controller.suggest(args)
48+
);
49+
},
4550
});
4651

4752
export default createResolver;

0 commit comments

Comments
 (0)