import type { DataProvider } from '../types'; import { fetchJSON, HTTPClient } from './request'; import qs from 'qs'; import { flattenObject } from './utils'; /** * Fork from `ra-data-json-server` because we dont need `ra-core` */ /** * Maps tushan queries to a json-server powered REST API * * @see https://github.com/typicode/json-server * * @example * * getList => GET http://my.api.url/posts?_sort=title&_order=ASC&_start=0&_end=24 * getOne => GET http://my.api.url/posts/123 * getManyReference => GET http://my.api.url/posts?author_id=345 * getMany => GET http://my.api.url/posts?id=123&id=456&id=789 * create => POST http://my.api.url/posts/123 * update => PUT http://my.api.url/posts/123 * updateMany => PUT http://my.api.url/posts/123, PUT http://my.api.url/posts/456, PUT http://my.api.url/posts/789 * delete => DELETE http://my.api.url/posts/123 * * @example * * import * as React from "react"; * import { Tushan, jsonServerProvider } from 'tushan'; * * const App = () => ( * * ... * * ); * * export default App; */ export function jsonServerProvider( apiUrl: string, httpClient: HTTPClient = fetchJSON ): DataProvider { return { getList: (resource, params) => { const { page, perPage } = params.pagination; const { field, order } = params.sort; const meta = params.meta; const query = { ...flattenObject(params.filter), _sort: field, _order: order, _start: (page - 1) * perPage, _end: page * perPage, meta, }; const url = `${apiUrl}/${resource}?${qs.stringify(query)}`; return httpClient(url).then(({ headers, json }) => { if (!headers.has('x-total-count')) { throw new Error( 'The X-Total-Count header is missing in the HTTP Response. The jsonServer Data Provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?' ); } return { data: json, total: parseInt( headers.get('x-total-count')?.split('/').pop() || '0', 10 ), }; }); }, getOne: (resource, params) => httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({ data: json, })), getMany: (resource, params) => { const query = { id: params.ids, meta: params.meta, }; const url = `${apiUrl}/${resource}?${qs.stringify(query)}`; return httpClient(url).then(({ json }) => ({ data: json })); }, getManyReference: (resource, params) => { const { page, perPage } = params.pagination; const { field, order } = params.sort; const meta = params.meta; const query = { ...flattenObject(params.filter), [params.target]: params.id, _sort: field, _order: order, _start: (page - 1) * perPage, _end: page * perPage, meta, }; const url = `${apiUrl}/${resource}?${qs.stringify(query)}`; return httpClient(url).then(({ headers, json }) => { if (!headers.has('x-total-count')) { throw new Error( 'The X-Total-Count header is missing in the HTTP Response. The jsonServer Data Provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?' ); } return { data: json, total: parseInt( headers.get('x-total-count')?.split('/').pop() || '0', 10 ), }; }); }, update: (resource, params) => httpClient(`${apiUrl}/${resource}/${params.id}`, { method: 'PUT', body: JSON.stringify(params.data), }).then(({ json }) => ({ data: json })), // json-server doesn't handle filters on UPDATE route, so we fallback to calling UPDATE n times instead updateMany: (resource, params) => Promise.all( params.ids.map((id) => httpClient(`${apiUrl}/${resource}/${id}`, { method: 'PUT', body: JSON.stringify(params.data), }) ) ).then((responses) => ({ data: responses.map(({ json }) => json.id) })), create: (resource, params) => httpClient(`${apiUrl}/${resource}`, { method: 'POST', body: JSON.stringify(params.data), }).then(({ json }) => ({ data: { ...params.data, id: json.id }, })), delete: (resource, params) => httpClient(`${apiUrl}/${resource}/${params.id}`, { method: 'DELETE', }).then(({ json }) => ({ data: json })), // json-server doesn't handle filters on DELETE route, so we fallback to calling DELETE n times instead deleteMany: (resource, params) => Promise.all( params.ids.map((id) => httpClient(`${apiUrl}/${resource}/${id}`, { method: 'DELETE', }) ) ).then((responses) => ({ data: responses.map(({ json }) => json.id) })), }; }