feat: add StroyBot app shell with URL routing
One web part replaces the separate stroy-bot-* web parts. The section and its parameters live in the page query string (?page=objects&projectId=5), so links can be copied, opened in a new tab and the browser Back button works. - shared config: site/Dataverse/API addresses, list and table names - HTTP layer behind IHttpClient so repositories can be unit-tested - ui-kit AppLayout + Sidebar, placeholder for sections not ported yet - migration checklist with every feature of the old web parts - design tokens mirrored from ui-kit instead of 8 copies Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
// Минимальный контракт HTTP-клиента. Репозитории зависят только от него,
|
||||
// а не от SPHttpClient/AadHttpClient напрямую — поэтому в тестах их можно
|
||||
// проверить заглушкой и убедиться, что URL, фильтры и тела запросов 1:1
|
||||
// совпадают со старыми web parts.
|
||||
|
||||
export type HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
||||
|
||||
export interface IHttpRequest {
|
||||
method?: HttpMethod;
|
||||
headers?: Record<string, string>;
|
||||
body?: string | ArrayBuffer;
|
||||
}
|
||||
|
||||
export interface IHttpResponse {
|
||||
ok: boolean;
|
||||
status: number;
|
||||
statusText: string;
|
||||
json(): Promise<unknown>;
|
||||
text(): Promise<string>;
|
||||
blob(): Promise<Blob>;
|
||||
}
|
||||
|
||||
export interface IHttpClient {
|
||||
fetch(url: string, request?: IHttpRequest): Promise<IHttpResponse>;
|
||||
}
|
||||
|
||||
/** Ошибка HTTP-ответа: сохраняет статус, чтобы страницы показывали те же тексты, что и раньше. */
|
||||
export class HttpError extends Error {
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
|
||||
constructor(status: number, statusText: string, details?: string) {
|
||||
super(details ? `${status} ${statusText}: ${details}` : `${status} ${statusText}`);
|
||||
this.status = status;
|
||||
this.statusText = statusText;
|
||||
}
|
||||
}
|
||||
|
||||
/** Бросает HttpError, если ответ не успешный. Текст ответа добавляется в сообщение. */
|
||||
export async function ensureOk(response: IHttpResponse): Promise<IHttpResponse> {
|
||||
if (response.ok) {
|
||||
return response;
|
||||
}
|
||||
let details = '';
|
||||
try {
|
||||
details = await response.text();
|
||||
} catch {
|
||||
// Тело ошибки может быть недоступно — статуса достаточно.
|
||||
}
|
||||
throw new HttpError(response.status, response.statusText, details);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import { SPHttpClient, AadHttpClient } from '@microsoft/sp-http';
|
||||
import { IHttpClient, IHttpRequest, IHttpResponse } from './IHttpClient';
|
||||
|
||||
// Адаптеры SPFx-клиентов к IHttpClient. Вся работа с авторизацией — здесь,
|
||||
// остальной код о SPFx не знает.
|
||||
|
||||
export function createSharePointClient(context: WebPartContext): IHttpClient {
|
||||
return {
|
||||
fetch: (url: string, request: IHttpRequest = {}): Promise<IHttpResponse> =>
|
||||
context.spHttpClient.fetch(url, SPHttpClient.configurations.v1, {
|
||||
method: request.method ?? 'GET',
|
||||
headers: request.headers,
|
||||
body: request.body
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Клиент к ресурсу под AAD (Dataverse или корпоративный API).
|
||||
* AadHttpClient создаётся лениво и один раз — получение клиента запрашивает токен.
|
||||
*/
|
||||
export function createAadClient(context: WebPartContext, resource: string): IHttpClient {
|
||||
let clientPromise: Promise<AadHttpClient> | undefined;
|
||||
|
||||
const getClient = (): Promise<AadHttpClient> => {
|
||||
if (!clientPromise) {
|
||||
clientPromise = context.aadHttpClientFactory.getClient(resource);
|
||||
}
|
||||
return clientPromise;
|
||||
};
|
||||
|
||||
return {
|
||||
fetch: async (url: string, request: IHttpRequest = {}): Promise<IHttpResponse> => {
|
||||
const client = await getClient();
|
||||
return client.fetch(url, AadHttpClient.configurations.v1, {
|
||||
method: request.method ?? 'GET',
|
||||
headers: request.headers,
|
||||
body: request.body
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user