Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/5
This commit was merged in pull request #5.
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { AadHttpClient, HttpClientResponse } from '@microsoft/sp-http';
|
||||
import { Company, Department, Employee, Project } from '../models';
|
||||
import { ApiClientError } from './httpClient';
|
||||
|
||||
// Справочники берём напрямую из api.tnsu.kz — тем же AAD-токеном, что и наш API:
|
||||
// в appsettings.json бэкенда Entra:Audience и TnsuPortal:ClientId — один и тот же
|
||||
// GUID, то есть обе службы живут на одной регистрации Entra. Прокси не нужен.
|
||||
//
|
||||
// Два отличия от нашего API, из-за которых здесь отдельный клиент:
|
||||
// 1) портал отдаёт «голый» массив, без конверта { data: ... };
|
||||
// 2) ключи в PascalCase (Id/Name/CompanyId/Company).
|
||||
|
||||
interface IPortalCompany {
|
||||
Id: string;
|
||||
Name: string;
|
||||
}
|
||||
|
||||
interface IPortalDepartment {
|
||||
Id: string;
|
||||
Name: string;
|
||||
CompanyId: string;
|
||||
Company: string;
|
||||
}
|
||||
|
||||
// GET /Project?name=&page=&pageSize= — в отличие от /Dictionary/*, здесь портал
|
||||
// уже отдаёт camelCase и постраничный конверт (данные идут из своей БД, а не
|
||||
// из сырых 1С-справочников).
|
||||
interface IPortalProject {
|
||||
oid: string;
|
||||
name: string;
|
||||
code: string | null;
|
||||
status: string | null;
|
||||
}
|
||||
|
||||
interface IPortalProjectPage {
|
||||
items: IPortalProject[];
|
||||
totalCount: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface IProjectSearchResult {
|
||||
items: Project[];
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
// GET /Employee/All — уже camelCase, как /Project (не PascalCase, как /Dictionary/*).
|
||||
// Строка на каждого сотрудника в каждом его отделе, поэтому один email может
|
||||
// повторяться несколько раз; сотрудники без email в подборе исполнителя бесполезны.
|
||||
interface IPortalEmployee {
|
||||
fio: string;
|
||||
email: string | null;
|
||||
}
|
||||
|
||||
async function getJson<T>(client: AadHttpClient, baseUrl: string, path: string): Promise<T> {
|
||||
const normalizedBase = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
||||
const url = new URL(path.replace(/^\//, ''), normalizedBase).toString();
|
||||
|
||||
const response: HttpClientResponse = await client.get(url, AadHttpClient.configurations.v1);
|
||||
const text = await response.text();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ApiClientError(response.status, [
|
||||
`Справочник api.tnsu.kz вернул HTTP ${response.status}${text ? `: ${text}` : ''}`
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(text) as T;
|
||||
} catch {
|
||||
throw new ApiClientError(response.status, ['Справочник api.tnsu.kz вернул не-JSON ответ.']);
|
||||
}
|
||||
}
|
||||
|
||||
export class PortalDictionaryClient {
|
||||
constructor(private readonly client: AadHttpClient, private readonly baseUrl: string) {}
|
||||
|
||||
public async companies(): Promise<Company[]> {
|
||||
const raw = await getJson<IPortalCompany[]>(this.client, this.baseUrl, '/Dictionary/Companies');
|
||||
return (raw ?? []).map(x => ({ id: x.Id, name: x.Name }));
|
||||
}
|
||||
|
||||
public async departments(): Promise<Department[]> {
|
||||
const raw = await getJson<IPortalDepartment[]>(this.client, this.baseUrl, '/Dictionary/Departments');
|
||||
return (raw ?? []).map(x => ({
|
||||
id: x.Id,
|
||||
name: x.Name,
|
||||
companyId: x.CompanyId,
|
||||
company: x.Company
|
||||
}));
|
||||
}
|
||||
|
||||
public async searchProjects(name: string, page: number, pageSize = 20): Promise<IProjectSearchResult> {
|
||||
const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize) });
|
||||
if (name.trim()) params.set('name', name.trim());
|
||||
|
||||
const raw = await getJson<IPortalProjectPage>(this.client, this.baseUrl, `/Project?${params.toString()}`);
|
||||
return {
|
||||
items: (raw.items ?? []).map(x => ({ oid: x.oid, name: x.name, code: x.code, status: x.status })),
|
||||
hasMore: raw.page < raw.totalPages
|
||||
};
|
||||
}
|
||||
|
||||
public async employees(): Promise<Employee[]> {
|
||||
const raw = await getJson<IPortalEmployee[]>(this.client, this.baseUrl, '/Employee/All');
|
||||
|
||||
const byEmail = new Map<string, Employee>();
|
||||
(raw ?? []).forEach(x => {
|
||||
if (x.email && !byEmail.has(x.email)) byEmail.set(x.email, { fio: x.fio, email: x.email });
|
||||
});
|
||||
return Array.from(byEmail.values());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user