Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it compatible with react-admin 5.1.0 and @dataui/crud? #51

Open
IbrahemHadidy opened this issue Jul 27, 2024 · 1 comment
Open

Is it compatible with react-admin 5.1.0 and @dataui/crud? #51

IbrahemHadidy opened this issue Jul 27, 2024 · 1 comment

Comments

@IbrahemHadidy
Copy link

IbrahemHadidy commented Jul 27, 2024

I am using react-admin 5.1.0, @dataui/crud 5.3.4, @dataui/crud-typeorm 5.3.4
but the problem is the sorting and filters doesn't seem to work

what i noticed is the sent syntax is:
http://localhost:4000/api/admin/developers?&filter%5B0%5D=s%7C%7C%24contL%7C%7Csomename&filter%5B1%5D=name%7C%7C%24contL%7C%7Cas&limit=10&page=1&sort%5B0%5D=id%2CDESC&offset=0

decoded:
http://localhost:4000/api/admin/developers?&filter[0]=s||$contL||somename&filter[1]=name||$contL||as&limit=10&page=1&sort[0]=id,DESC&offset=0

this is swagger documentation of the GET request (using @dataui/crud):
image

if there is a fix/solution to this problem or mention any other package that suits this docs I would be grateful.

@IbrahemHadidy
Copy link
Author

IbrahemHadidy commented Jul 28, 2024

I made my own edit/solution (not sure if it is stable yet)

import { CondOperator } from '@dataui/crud-request';
import omitBy from 'lodash.omitby';
import { fetchUtils } from 'react-admin';

import type {
 DataProvider as AdminDataProvider,
 CreateParams,
 CreateResult,
 DeleteManyParams,
 DeleteManyResult,
 DeleteParams,
 DeleteResult,
 GetListParams,
 GetListResult,
 GetManyParams,
 GetManyReferenceParams,
 GetManyReferenceResult,
 GetManyResult,
 GetOneParams,
 GetOneResult,
 UpdateManyParams,
 UpdateManyResult,
 UpdateParams,
 UpdateResult,
} from 'react-admin';

class DataProvider implements AdminDataProvider {
 private apiUrl: string;
 private httpClient: typeof fetchUtils.fetchJson;

 constructor(apiUrl: string, httpClient = fetchUtils.fetchJson) {
   this.apiUrl = apiUrl;
   this.httpClient = httpClient;
 }

 private countDiff = (
   o1: Record<string, unknown>,
   o2: Record<string, unknown>
 ): Record<string, unknown> => omitBy(o1, (v: unknown, k: string | number) => o2[k] === v);

 private composeFilter = (paramsFilter: unknown): string => {
   const flatFilter = fetchUtils.flattenObject(paramsFilter);
   return Object.keys(flatFilter)
     .map((key) => {
       const splitKey = key.split(/\|\||:/);
       const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/gi;

       let field = splitKey[0];
       let ops = splitKey[1];
       if (!ops) {
         if (
           typeof flatFilter[key] === 'boolean' ||
           typeof flatFilter[key] === 'number' ||
           (typeof flatFilter[key] === 'string' && flatFilter[key].match(/^\d+$/)) ||
           flatFilter[key].match(uuidRegex)
         ) {
           ops = CondOperator.EQUALS;
         } else {
           ops = CondOperator.CONTAINS_LOW;
         }
       }

       if (field.startsWith('_') && field.includes('.')) {
         field = field.split(/\.(.+)/)[1];
       }
       return `filter=${field}||${ops}||${flatFilter[key]}`;
     })
     .join('&');
 };

 private composeQueryParams = (params: GetListParams): string => {
   const queryParams: { [key: string]: string | number } = {};

   if (params.pagination) {
     queryParams.limit = params.pagination.perPage;
     queryParams.offset = (params.pagination.page - 1) * params.pagination.perPage;
   }

   if (params.sort) {
     queryParams.sort = `${params.sort.field},${params.sort.order}`;
   }

   const filterString = this.composeFilter(params.filter || {});
   const queryString = Object.entries(queryParams)
     .map(([key, value]) => `${key}=${value}`)
     .join('&');

   return filterString ? `${queryString}&${filterString}` : queryString;
 };

 public getList = async (resource: string, params: GetListParams): Promise<GetListResult> => {
   const queryStringParams = this.composeQueryParams(params);

   const url = `${this.apiUrl}/${resource}?${queryStringParams}`;
   const { json } = await this.httpClient(url);
   return {
     data: json.data,
     total: json.total,
   };
 };

 public getOne = async (resource: string, params: GetOneParams): Promise<GetOneResult> => {
   const { json } = await this.httpClient(`${this.apiUrl}/${resource}/${params.id}`);
   return { data: json };
 };

 public getMany = async (resource: string, params: GetManyParams): Promise<GetManyResult> => {
   const query = `filter=id||${CondOperator.IN}||${params.ids.join(',')}`;
   const url = `${this.apiUrl}/${resource}?${query}`;

   const { json } = await this.httpClient(url);
   return { data: json.data || json };
 };

 public getManyReference = async (
   resource: string,
   params: GetManyReferenceParams
 ): Promise<GetManyReferenceResult> => {
   const queryParams = this.composeQueryParams(params);
   const queryStringParams =
     queryParams + `&filter=${params.target}||${CondOperator.EQUALS}||${params.id}`;

   const url = `${this.apiUrl}/${resource}?${queryStringParams}`;
   const { json } = await this.httpClient(url);
   return {
     data: json.data,
     total: json.total,
   };
 };

 public update = async (resource: string, params: UpdateParams): Promise<UpdateResult> => {
   const data = this.countDiff(params.data, params.previousData);
   const { json } = await this.httpClient(`${this.apiUrl}/${resource}/${params.id}`, {
     method: 'PATCH',
     body: JSON.stringify(data),
   });
   return { data: json };
 };

 public updateMany = async (
   resource: string,
   params: UpdateManyParams
 ): Promise<UpdateManyResult> => {
   const responses = await Promise.all(
     params.ids.map(async (id) => {
       const { json } = await this.httpClient(`${this.apiUrl}/${resource}/${id}`, {
         method: 'PUT',
         body: JSON.stringify(params.data),
       });
       return json;
     })
   );

   return {
     data: responses,
   };
 };

 public create = async (resource: string, params: CreateParams): Promise<CreateResult> => {
   const { json } = await this.httpClient(`${this.apiUrl}/${resource}`, {
     method: 'POST',
     body: JSON.stringify(params.data),
   });
   return {
     data: { ...json },
   };
 };

 public delete = async (resource: string, params: DeleteParams): Promise<DeleteResult> => {
   const { json } = await this.httpClient(`${this.apiUrl}/${resource}/${params.id}`, {
     method: 'DELETE',
   });
   return { data: { ...json, id: params.id } };
 };

 public deleteMany = async (
   resource: string,
   params: DeleteManyParams
 ): Promise<DeleteManyResult> => {
   const responses = await Promise.all(
     params.ids.map(async (id) => {
       const { json } = await this.httpClient(`${this.apiUrl}/${resource}/${id}`, {
         method: 'DELETE',
       });
       return json;
     })
   );
   return { data: responses };
 };
}

const dataProvider = (apiUrl: string, httpClient = fetchUtils.fetchJson) =>
 new DataProvider(apiUrl, httpClient);
export default dataProvider;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant