import type { DataProvider, DeleteManyParams, RaRecord } from 'react-admin'; import { env } from './config/env'; import { ensureFreshToken, getAccessToken } from './auth/keycloak'; type HttpResult = { data: T; headers: Headers; }; async function withAuthHeaders(headers = new Headers()) { await ensureFreshToken(); const token = getAccessToken(); if (token) { headers.set('Authorization', `Bearer ${token}`); } headers.set('Accept', 'application/json'); headers.set('Content-Type', 'application/json'); return headers; } function appendFilterParams(searchParams: URLSearchParams, filter?: Record) { if (!filter) { return; } for (const [key, value] of Object.entries(filter)) { if (value === undefined || value === null || value === '') { continue; } if (Array.isArray(value)) { value.forEach((entry) => { if (entry !== undefined && entry !== null && entry !== '') { searchParams.append(key, String(entry)); } }); continue; } searchParams.append(key, String(value)); } } async function http(url: string, init?: RequestInit): Promise> { const headers = await withAuthHeaders(new Headers(init?.headers)); const response = await fetch(url, { ...init, headers, }); if (!response.ok) { const error = new Error(`Request failed with status ${response.status}`) as Error & { status: number }; error.status = response.status; throw error; } const text = await response.text(); return { data: text ? (JSON.parse(text) as T) : (null as T), headers: response.headers, }; } function parseTotal(headers: Headers, fallback: number) { const contentRange = headers.get('Content-Range'); if (!contentRange) { return fallback; } const match = contentRange.match(/\/(\d+)$/); return match ? Number(match[1]) : fallback; } export const dataProvider: DataProvider = { async getList(resource, params) { const searchParams = new URLSearchParams({ _start: String(params.pagination.page <= 1 ? 0 : (params.pagination.page - 1) * params.pagination.perPage), _end: String(params.pagination.page * params.pagination.perPage), _sort: params.sort.field, _order: params.sort.order, }); appendFilterParams(searchParams, params.filter); const { data, headers } = await http(`${env.apiUrl}/${resource}?${searchParams.toString()}`); return { data, total: parseTotal(headers, data.length), }; }, async getOne(resource, params) { const { data } = await http(`${env.apiUrl}/${resource}/${params.id}`); return { data }; }, async getMany(resource, params) { const results = await Promise.all(params.ids.map((id) => http(`${env.apiUrl}/${resource}/${id}`))); return { data: results.map((result) => result.data) }; }, async getManyReference(resource, params) { const searchParams = new URLSearchParams({ _start: String(params.pagination.page <= 1 ? 0 : (params.pagination.page - 1) * params.pagination.perPage), _end: String(params.pagination.page * params.pagination.perPage), _sort: params.sort.field, _order: params.sort.order, [params.target]: String(params.id), }); appendFilterParams(searchParams, params.filter); const { data, headers } = await http(`${env.apiUrl}/${resource}?${searchParams.toString()}`); return { data, total: parseTotal(headers, data.length), }; }, async create(resource, params) { const { data } = await http(`${env.apiUrl}/${resource}`, { method: 'POST', body: JSON.stringify(params.data), }); return { data }; }, async update(resource, params) { const { data } = await http(`${env.apiUrl}/${resource}/${params.id}`, { method: 'PATCH', body: JSON.stringify(params.data), }); return { data }; }, async updateMany(resource, params) { const results = await Promise.all( params.ids.map((id) => http(`${env.apiUrl}/${resource}/${id}`, { method: 'PATCH', body: JSON.stringify(params.data), }), ), ); return { data: results.map((result) => result.data.id) }; }, async delete(resource, params) { const { data } = await http(`${env.apiUrl}/${resource}/${params.id}`, { method: 'DELETE', }); return { data }; }, async deleteMany(resource, params: DeleteManyParams) { const results = await Promise.all( params.ids.map((id) => http(`${env.apiUrl}/${resource}/${id}`, { method: 'DELETE', }), ), ); return { data: results.map((result) => result.data.id) }; }, };