import type { DataProvider } from "react-admin"; import { env } from "./config/env"; import { getAccessToken } from "./auth/keycloak"; async function fetchJson( url: string, options: RequestInit = {}, ): Promise<{ json: any; headers: Headers; status: number }> { const headers = new Headers( options.headers ?? { Accept: "application/json" }, ); const token = await getAccessToken(); if (token) { headers.set('Authorization', `Bearer ${token}`); } if (!headers.has("Content-Type") && options.body) { headers.set("Content-Type", "application/json"); } const response = await fetch(url, { ...options, headers }); if (!response.ok) { const error = new Error( "Request failed with status " + response.status, ) as Error & { status?: number; body?: unknown }; error.status = response.status; try { error.body = await response.json(); } catch { error.body = null; } throw error; } if (response.status === 204) { return { json: null, headers: response.headers, status: response.status }; } const json = await response.json(); return { json, headers: response.headers, status: response.status }; } function appendSearchParam( searchParams: URLSearchParams, key: string, value: unknown, ): void { if (Array.isArray(value)) { value.forEach((entry) => appendSearchParam(searchParams, key, entry)); return; } if (value === undefined || value === null || value === "") { return; } searchParams.append(key, String(value)); } function parseListBody(json: unknown): { rows: unknown[]; totalHint?: number } { if (Array.isArray(json)) { return { rows: json }; } if ( json !== null && typeof json === "object" && "data" in json && Array.isArray((json as { data: unknown }).data) ) { const body = json as { data: unknown[]; total?: unknown }; const totalHint = typeof body.total === "number" && Number.isFinite(body.total) ? body.total : undefined; return { rows: body.data, totalHint }; } return { rows: [] }; } function buildListUrl(resource: string, params: any): string { const resourcePath = resource === "equipment" ? "equipments" : resource; const searchParams = new URLSearchParams(); searchParams.set( "_start", String((params.pagination.page - 1) * params.pagination.perPage), ); searchParams.set( "_end", String(params.pagination.page * params.pagination.perPage), ); searchParams.set("_sort", params.sort.field); searchParams.set("_order", params.sort.order); Object.entries(params.filter ?? {}).forEach(([key, value]) => { appendSearchParam(searchParams, key, value); }); const queryString = searchParams.toString(); return ( env.apiUrl + "/" + resourcePath + (queryString ? "?" + queryString : "") ); } export const dataProvider: DataProvider = { getList: async (resource, params) => { if (resource === "price-list") { const { json } = await fetchJson(env.apiUrl + "/price-list"); return { data: [json], total: 1 }; } const { json, headers } = await fetchJson(buildListUrl(resource, params)); const { rows, totalHint } = parseListBody(json); const contentRange = headers.get("Content-Range"); const total = contentRange ? Number( contentRange.split("/").pop() ?? totalHint ?? rows.length, ) : (totalHint ?? rows.length); return { data: rows as any[], total }; }, getOne: async (resource, params) => { const resourcePath = resource === "equipment" ? "equipments" : resource; const url = resource === "price-list" ? env.apiUrl + "/price-list" : env.apiUrl + "/" + resourcePath + "/" + params.id; const { json } = await fetchJson(url); return { data: json }; }, getMany: async (resource, params) => { if (resource === "price-list") { const { json } = await fetchJson(env.apiUrl + "/price-list"); return { data: params.ids.includes("price-list") ? [json] : [] }; } const records = await Promise.all( params.ids.map((id) => dataProvider.getOne(resource, { id, meta: params.meta } as any), ), ); return { data: records.map((result) => result.data) }; }, getManyReference: async (resource, params) => dataProvider.getList(resource, { pagination: params.pagination, sort: params.sort, filter: { ...(params.filter ?? {}), [params.target]: params.id }, meta: params.meta, } as any), create: async (resource, params) => { const resourcePath = resource === "equipment" ? "equipments" : resource; const { json } = await fetchJson(env.apiUrl + "/" + resourcePath, { method: "POST", body: JSON.stringify(params.data), }); return { data: json }; }, update: async (resource, params) => { const resourcePath = resource === "equipment" ? "equipments" : resource; const { json } = await fetchJson( env.apiUrl + "/" + resourcePath + "/" + params.id, { method: "PATCH", body: JSON.stringify(params.data) }, ); return { data: json }; }, updateMany: async (resource, params) => { const results = await Promise.all( params.ids.map((id) => dataProvider.update(resource, { id, data: params.data, previousData: {}, meta: params.meta, } as any), ), ); return { data: results.map((result) => result.data.id) }; }, delete: async (resource, params) => { const resourcePath = resource === "equipment" ? "equipments" : resource; const { json } = await fetchJson( env.apiUrl + "/" + resourcePath + "/" + params.id, { method: "DELETE" }, ); return { data: json ?? { id: params.id } }; }, deleteMany: async (resource, params) => { const results = await Promise.all( params.ids.map((id) => dataProvider.delete(resource, { id, previousData: {}, meta: params.meta, } as any), ), ); return { data: results.map((result) => result.data.id) }; }, };