diff --git a/.changeset/spicy-bottles-tease.md b/.changeset/spicy-bottles-tease.md new file mode 100644 index 000000000..a7a6fe5e4 --- /dev/null +++ b/.changeset/spicy-bottles-tease.md @@ -0,0 +1,18 @@ +--- +'@equinor/fusion-framework-module-services': minor +--- + +Added person services + +> __for internal usage only!__ + +- add function for fetching person details +- add function for querying persons +- add function for downloading person photo + +```ts +const personApi = await modules.services.createPeopleClient(); +personApi.query('v2', 'json$', {search: 'foo@bar.com'}) +personApi.get('v4', 'json$', {azureId: '1234'}) +personApi.photo('v2', 'blob$', {azureId: '123'}) +`` \ No newline at end of file diff --git a/packages/modules/services/src/people/api-models.ts b/packages/modules/services/src/people/api-models.ts new file mode 100644 index 000000000..8e38cbf42 --- /dev/null +++ b/packages/modules/services/src/people/api-models.ts @@ -0,0 +1,59 @@ +import { ApiVersion } from './static'; + +// TODO +type ApiPersonDetailEntity_vX = { + azureId: string; + name?: string; + pictureSrc?: string; + jobTitle?: string; + department?: string; + mail?: string; + company?: string; + mobilePhone?: string; + // TODO + accountType?: ApiPersonAccountType_vX; + officeLocation?: string; + // TODO + positions?: ApiPersonPosition_vX[]; + // TODO + manager?: ApiPersonManager_vX; + managerAzureUniqueId?: string; +}; + +// TODO +enum ApiPersonAccountType_vX { + Employee = 'Employee', + Consultant = 'Consultant', + Enterprise = 'Enterprise', + External = 'External', + ExternalHire = 'External Hire', +} + +// TODO +type ApiPersonPosition_vX = { + id: string; + name: string; + project: { + id: string; + name: string; + }; +}; + +// TODO +type ApiPersonManager_vX = { + azureUniqueId: string; + name?: string; + pictureSrc?: string; + department?: string; + // TODO + accountType?: ApiPersonAccountType_vX; +}; + +type ApiPersonDetailTypes = { + // TODO + [ApiVersion.v2]: ApiPersonDetailEntity_vX; + // TODO + [ApiVersion.v4]: ApiPersonDetailEntity_vX; +}; + +export type ApiPersonDetailType = ApiPersonDetailTypes[T]; diff --git a/packages/modules/services/src/people/client.ts b/packages/modules/services/src/people/client.ts new file mode 100644 index 000000000..b39935c0a --- /dev/null +++ b/packages/modules/services/src/people/client.ts @@ -0,0 +1,89 @@ +import { IHttpClient } from '@equinor/fusion-framework-module-http'; + +import type { ClientDataMethod, ClientMethod } from '../types'; +import { ApiVersion } from './static'; + +import { + client as getClient, + ApiResponse as GetPersonApiResponse, + ApiRequestFn as GetPersonApiRequestFn, + ApiResult as GetPersonResult, + SupportedApiVersion as SupportedGetPersonApiVersion, +} from './person-details'; + +import { + client as queryClient, + ApiResponse as QueryPersonApiResponse, + ApiRequestFn as QueryPersonApiRequestFn, + ApiResult as QueryPersonResult, + SupportedApiVersion as SupportedQueryApiVersion, +} from './query'; + +import { + client as photoClient, + ApiResponse as PhotoPersonApiResponse, + ApiRequestFn as PhotoPersonApiRequestFn, + ApiResult as PhotoPersonResult, + SupportedApiVersion as SupportedPhotoApiVersion, +} from './person-photo'; + +export class PeopleApiClient< + // TMethod extends keyof ClientMethod = keyof ClientMethod, + TClient extends IHttpClient = IHttpClient, +> { + get Version(): typeof ApiVersion { + return ApiVersion; + } + + constructor(protected _client: TClient) {} + + /** + * Fetch person by id + */ + public get< + TVersion extends SupportedGetPersonApiVersion, + TResult = GetPersonApiResponse, + TMethod extends keyof ClientMethod = keyof ClientMethod, + >( + version: TVersion, + method: TMethod, + ...args: Parameters> + ): GetPersonResult { + const fn = getClient(this._client, version, method); + return fn(...args); + } + + /** + * Query person service + */ + public query< + TVersion extends SupportedQueryApiVersion, + TResult = QueryPersonApiResponse, + TMethod extends keyof ClientMethod = keyof ClientMethod, + >( + version: TVersion, + method: TMethod, + ...args: Parameters> + ): QueryPersonResult { + const fn = queryClient(this._client, version, method); + return fn(...args); + } + + /** + * Photo person service + */ + public photo< + TVersion extends SupportedPhotoApiVersion, + TResult extends Blob = PhotoPersonApiResponse, + TMethod extends keyof ClientDataMethod = keyof ClientDataMethod, + >( + version: TVersion, + method: TMethod, + ...args: Parameters> + ): PhotoPersonResult { + const fn = photoClient(this._client, version, method); + return fn(...args); + } +} + +export default PeopleApiClient; diff --git a/packages/modules/services/src/people/person-details/client.ts b/packages/modules/services/src/people/person-details/client.ts new file mode 100644 index 000000000..732dff65f --- /dev/null +++ b/packages/modules/services/src/people/person-details/client.ts @@ -0,0 +1,32 @@ +import { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client'; + +import { generateParameters } from './generate-parameters'; + +import type { ClientMethod } from '../../types'; +import type { ApiResponse, ApiResult, ApiRequestArgs, SupportedApiVersion } from './types'; + +/** + * Method for fetching context item from context service + * @param client - client for execution of request + * @param version - version of API to call + * @param method - client method to call + */ +export const client = + < + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientMethod = keyof ClientMethod, + TClient extends IHttpClient = IHttpClient, + >( + client: TClient, + version: TVersion, + method: TMethod = 'json' as TMethod, + ) => + >( + args: ApiRequestArgs, + init?: ClientRequestInit, + ): ApiResult => + client[method]( + ...generateParameters(version, args, init), + ) as ApiResult; + +export default client; diff --git a/packages/modules/services/src/people/person-details/generate-endpoint.ts b/packages/modules/services/src/people/person-details/generate-endpoint.ts new file mode 100644 index 000000000..194215c81 --- /dev/null +++ b/packages/modules/services/src/people/person-details/generate-endpoint.ts @@ -0,0 +1,25 @@ +import { UnsupportedApiVersion } from '../../errors'; +import { ApiVersion } from '../static'; + +import type { ApiRequestArgs, SupportedApiVersion } from './types'; + +/** + * Method for generating endpoint for getting context by id + */ +export const generateEndpoint = ( + version: TVersion, + args: ApiRequestArgs, +) => { + const apiVersion = ApiVersion[version as keyof typeof ApiVersion] ?? version; + switch (apiVersion) { + case ApiVersion.v4: { + const { azureId } = args; + const params = new URLSearchParams(); + params.append('api-version', apiVersion); + return `/persons/${azureId}?${String(params)}`; + } + default: { + throw new UnsupportedApiVersion(version); + } + } +}; diff --git a/packages/modules/services/src/people/person-details/generate-parameters.ts b/packages/modules/services/src/people/person-details/generate-parameters.ts new file mode 100644 index 000000000..90988f6df --- /dev/null +++ b/packages/modules/services/src/people/person-details/generate-parameters.ts @@ -0,0 +1,22 @@ +import type { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client'; + +import { generateEndpoint } from './generate-endpoint'; + +import type { ApiClientArguments } from '../../types'; +import type { ApiRequestArgs, SupportedApiVersion } from './types'; + +/** function for creating http client arguments */ +export const generateParameters = < + TResult, + TVersion extends SupportedApiVersion, + TClient extends IHttpClient = IHttpClient, +>( + version: TVersion, + args: ApiRequestArgs, + init?: ClientRequestInit, +): ApiClientArguments => { + const path = generateEndpoint(version, args); + return [path, init]; +}; + +export default generateParameters; diff --git a/packages/modules/services/src/people/person-details/index.ts b/packages/modules/services/src/people/person-details/index.ts new file mode 100644 index 000000000..a653a23bb --- /dev/null +++ b/packages/modules/services/src/people/person-details/index.ts @@ -0,0 +1,6 @@ +export { client, default } from './client'; + +export { generateEndpoint } from './generate-endpoint'; +export { generateParameters } from './generate-parameters'; + +export * from './types'; diff --git a/packages/modules/services/src/people/person-details/types.ts b/packages/modules/services/src/people/person-details/types.ts new file mode 100644 index 000000000..80371ef36 --- /dev/null +++ b/packages/modules/services/src/people/person-details/types.ts @@ -0,0 +1,41 @@ +import { IHttpClient, ClientRequestInit } from '@equinor/fusion-framework-module-http/client'; + +import { ApiVersion } from '../static'; +import { ApiPersonDetailType } from '../api-models'; +import { ClientMethod } from '../../types'; + +export type SupportedApiVersion = Extract; + +type ApiRequestArgsMap = { + [ApiVersion.v4]: { + azureId: string; + }; +}; + +export type ApiRequestArgs = T extends SupportedApiVersion + ? ApiRequestArgsMap[(typeof ApiVersion)[T]] + : never; + +type ApiResponseTypes = { + [ApiVersion.v4]: ApiPersonDetailType; +}; + +export type ApiResponse = T extends SupportedApiVersion + ? ApiResponseTypes[(typeof ApiVersion)[T]] + : never; + +export type ApiRequestFn< + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientMethod = keyof ClientMethod, + TClient extends IHttpClient = IHttpClient, + TResult = ApiResponse, +> = ( + args: ApiRequestArgs, + init?: ClientRequestInit, +) => ApiResult; + +export type ApiResult< + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientMethod = keyof ClientMethod, + TResult = ApiResponse, +> = ClientMethod[TMethod]; diff --git a/packages/modules/services/src/people/person-photo/client.ts b/packages/modules/services/src/people/person-photo/client.ts new file mode 100644 index 000000000..d62c176af --- /dev/null +++ b/packages/modules/services/src/people/person-photo/client.ts @@ -0,0 +1,32 @@ +import { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client'; + +import { generateParameters } from './generate-parameters'; + +import type { ClientDataMethod } from '../../types'; +import type { ApiResponse, ApiRequestArgs, SupportedApiVersion } from './types'; + +/** + * Method for fetching context item from context service + * @param client - client for execution of request + * @param version - version of API to call + * @param method - client method to call + */ +export const client = + < + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientDataMethod = keyof ClientDataMethod, + TClient extends IHttpClient = IHttpClient, + >( + client: TClient, + version: TVersion, + method: TMethod = 'blob' as TMethod, + ) => + >( + args: ApiRequestArgs, + init?: ClientRequestInit, + ): ClientDataMethod[TMethod] => + client[method]( + ...generateParameters(version, args, init), + ) as ClientDataMethod[TMethod]; + +export default client; diff --git a/packages/modules/services/src/people/person-photo/generate-endpoint.ts b/packages/modules/services/src/people/person-photo/generate-endpoint.ts new file mode 100644 index 000000000..074e4b8a0 --- /dev/null +++ b/packages/modules/services/src/people/person-photo/generate-endpoint.ts @@ -0,0 +1,25 @@ +import { UnsupportedApiVersion } from '../../errors'; +import { ApiVersion } from '../static'; + +import type { ApiRequestArgs, SupportedApiVersion } from './types'; + +/** + * Method for generating endpoint for getting context by id + */ +export const generateEndpoint = ( + version: TVersion, + args: ApiRequestArgs, +) => { + const apiVersion = ApiVersion[version as keyof typeof ApiVersion] ?? version; + switch (apiVersion) { + case ApiVersion.v2: { + const { azureId } = args; + const params = new URLSearchParams(); + params.append('api-version', apiVersion); + return `/persons/${azureId}?${String(params)}`; + } + default: { + throw new UnsupportedApiVersion(version); + } + } +}; diff --git a/packages/modules/services/src/people/person-photo/generate-parameters.ts b/packages/modules/services/src/people/person-photo/generate-parameters.ts new file mode 100644 index 000000000..90988f6df --- /dev/null +++ b/packages/modules/services/src/people/person-photo/generate-parameters.ts @@ -0,0 +1,22 @@ +import type { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client'; + +import { generateEndpoint } from './generate-endpoint'; + +import type { ApiClientArguments } from '../../types'; +import type { ApiRequestArgs, SupportedApiVersion } from './types'; + +/** function for creating http client arguments */ +export const generateParameters = < + TResult, + TVersion extends SupportedApiVersion, + TClient extends IHttpClient = IHttpClient, +>( + version: TVersion, + args: ApiRequestArgs, + init?: ClientRequestInit, +): ApiClientArguments => { + const path = generateEndpoint(version, args); + return [path, init]; +}; + +export default generateParameters; diff --git a/packages/modules/services/src/people/person-photo/index.ts b/packages/modules/services/src/people/person-photo/index.ts new file mode 100644 index 000000000..a653a23bb --- /dev/null +++ b/packages/modules/services/src/people/person-photo/index.ts @@ -0,0 +1,6 @@ +export { client, default } from './client'; + +export { generateEndpoint } from './generate-endpoint'; +export { generateParameters } from './generate-parameters'; + +export * from './types'; diff --git a/packages/modules/services/src/people/person-photo/types.ts b/packages/modules/services/src/people/person-photo/types.ts new file mode 100644 index 000000000..5d1fd08e0 --- /dev/null +++ b/packages/modules/services/src/people/person-photo/types.ts @@ -0,0 +1,37 @@ +import { IHttpClient, ClientRequestInit } from '@equinor/fusion-framework-module-http/client'; + +import { ApiVersion } from '../static'; +import { ClientDataMethod } from '../../types'; + +export type SupportedApiVersion = Extract; + +type ApiRequestArgsMap = { + [ApiVersion.v2]: { + azureId: string; + }; +}; + +export type ApiRequestArgs = T extends SupportedApiVersion + ? ApiRequestArgsMap[(typeof ApiVersion)[T]] + : never; + +type ApiResponseTypes = { + [ApiVersion.v2]: Blob; +}; + +export type ApiResponse = T extends SupportedApiVersion + ? ApiResponseTypes[(typeof ApiVersion)[T]] + : never; + +export type ApiRequestFn< + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientDataMethod = keyof ClientDataMethod, + TClient extends IHttpClient = IHttpClient, + TResult = ApiResponse, +> = ( + args: ApiRequestArgs, + init?: ClientRequestInit, +) => ClientDataMethod[TMethod]; + +export type ApiResult = + ClientDataMethod[TMethod]; diff --git a/packages/modules/services/src/people/query/client.ts b/packages/modules/services/src/people/query/client.ts new file mode 100644 index 000000000..732dff65f --- /dev/null +++ b/packages/modules/services/src/people/query/client.ts @@ -0,0 +1,32 @@ +import { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client'; + +import { generateParameters } from './generate-parameters'; + +import type { ClientMethod } from '../../types'; +import type { ApiResponse, ApiResult, ApiRequestArgs, SupportedApiVersion } from './types'; + +/** + * Method for fetching context item from context service + * @param client - client for execution of request + * @param version - version of API to call + * @param method - client method to call + */ +export const client = + < + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientMethod = keyof ClientMethod, + TClient extends IHttpClient = IHttpClient, + >( + client: TClient, + version: TVersion, + method: TMethod = 'json' as TMethod, + ) => + >( + args: ApiRequestArgs, + init?: ClientRequestInit, + ): ApiResult => + client[method]( + ...generateParameters(version, args, init), + ) as ApiResult; + +export default client; diff --git a/packages/modules/services/src/people/query/generate-endpoint.ts b/packages/modules/services/src/people/query/generate-endpoint.ts new file mode 100644 index 000000000..7beff73f4 --- /dev/null +++ b/packages/modules/services/src/people/query/generate-endpoint.ts @@ -0,0 +1,26 @@ +import { UnsupportedApiVersion } from '../../errors'; +import { ApiVersion } from '../static'; + +import type { ApiRequestArgs, SupportedApiVersion } from './types'; + +/** + * Method for generating endpoint for getting context by id + */ +export const generateEndpoint = ( + version: TVersion, + args: ApiRequestArgs, +) => { + const apiVersion = ApiVersion[version as keyof typeof ApiVersion] ?? version; + switch (apiVersion) { + case ApiVersion.v2: { + const { search } = args; + const params = new URLSearchParams(); + params.append('api-version', apiVersion); + params.append('search', search); + return `/persons?${String(params)}`; + } + default: { + throw new UnsupportedApiVersion(version); + } + } +}; diff --git a/packages/modules/services/src/people/query/generate-parameters.ts b/packages/modules/services/src/people/query/generate-parameters.ts new file mode 100644 index 000000000..90988f6df --- /dev/null +++ b/packages/modules/services/src/people/query/generate-parameters.ts @@ -0,0 +1,22 @@ +import type { ClientRequestInit, IHttpClient } from '@equinor/fusion-framework-module-http/client'; + +import { generateEndpoint } from './generate-endpoint'; + +import type { ApiClientArguments } from '../../types'; +import type { ApiRequestArgs, SupportedApiVersion } from './types'; + +/** function for creating http client arguments */ +export const generateParameters = < + TResult, + TVersion extends SupportedApiVersion, + TClient extends IHttpClient = IHttpClient, +>( + version: TVersion, + args: ApiRequestArgs, + init?: ClientRequestInit, +): ApiClientArguments => { + const path = generateEndpoint(version, args); + return [path, init]; +}; + +export default generateParameters; diff --git a/packages/modules/services/src/people/query/index.ts b/packages/modules/services/src/people/query/index.ts new file mode 100644 index 000000000..a653a23bb --- /dev/null +++ b/packages/modules/services/src/people/query/index.ts @@ -0,0 +1,6 @@ +export { client, default } from './client'; + +export { generateEndpoint } from './generate-endpoint'; +export { generateParameters } from './generate-parameters'; + +export * from './types'; diff --git a/packages/modules/services/src/people/query/types.ts b/packages/modules/services/src/people/query/types.ts new file mode 100644 index 000000000..a36017522 --- /dev/null +++ b/packages/modules/services/src/people/query/types.ts @@ -0,0 +1,41 @@ +import { IHttpClient, ClientRequestInit } from '@equinor/fusion-framework-module-http/client'; + +import { ApiVersion } from '../static'; +import { ApiPersonDetailType } from '../api-models'; +import { ClientMethod } from '../../types'; + +export type SupportedApiVersion = Extract; + +type ApiRequestArgsMap = { + [ApiVersion.v2]: { + search: string; + }; +}; + +export type ApiRequestArgs = T extends SupportedApiVersion + ? ApiRequestArgsMap[(typeof ApiVersion)[T]] + : never; + +type ApiResponseTypes = { + [ApiVersion.v2]: Array>; +}; + +export type ApiResponse = T extends SupportedApiVersion + ? ApiResponseTypes[(typeof ApiVersion)[T]] + : never; + +export type ApiRequestFn< + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientMethod = keyof ClientMethod, + TClient extends IHttpClient = IHttpClient, + TResult = ApiResponse, +> = ( + args: ApiRequestArgs, + init?: ClientRequestInit, +) => ApiResult; + +export type ApiResult< + TVersion extends SupportedApiVersion, + TMethod extends keyof ClientMethod = keyof ClientMethod, + TResult = ApiResponse, +> = ClientMethod[TMethod]; diff --git a/packages/modules/services/src/people/static.ts b/packages/modules/services/src/people/static.ts new file mode 100644 index 000000000..f286dd8e2 --- /dev/null +++ b/packages/modules/services/src/people/static.ts @@ -0,0 +1,4 @@ +export enum ApiVersion { + 'v2' = '2.0', + 'v4' = '4.0', +} diff --git a/packages/modules/services/src/people/types.ts b/packages/modules/services/src/people/types.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/modules/services/src/provider.ts b/packages/modules/services/src/provider.ts index f265d0354..15d151991 100644 --- a/packages/modules/services/src/provider.ts +++ b/packages/modules/services/src/provider.ts @@ -5,6 +5,7 @@ import { ApiClientFactory } from './types'; import { ContextApiClient } from './context'; import BookmarksApiClient from './bookmarks/client'; import { NotificationApiClient } from './notification'; +import { PeopleApiClient } from './people/client'; export interface IApiProvider { /** @@ -26,6 +27,10 @@ export interface IApiProvider { createNotificationClient( method: TMethod, ): Promise>; + /** + * @param method - Version of the service to use + */ + createPeopleClient(): Promise>; } type ApiProviderCtorArgs = { @@ -102,4 +107,9 @@ export class ApiProvider httpClient.responseHandler.add('validate_api_request', validateResponse); return new ContextApiClient(httpClient, method); } + public async createPeopleClient(): Promise> { + const httpClient = await this._createClientFn('people'); + httpClient.responseHandler.add('validate_api_request', validateResponse); + return new PeopleApiClient(httpClient); + } } diff --git a/packages/modules/services/src/types.ts b/packages/modules/services/src/types.ts index a6901938b..df63f4041 100644 --- a/packages/modules/services/src/types.ts +++ b/packages/modules/services/src/types.ts @@ -38,4 +38,9 @@ export type ClientMethod = { json$: StreamResponse; }; +export type ClientDataMethod = { + blob: Blob; + blob$: StreamResponse; +}; + export type ClientMethodType = keyof ClientMethod;