Open IbrahemHadidy opened 1 month ago
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;
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):
if there is a fix/solution to this problem or mention any other package that suits this docs I would be grateful.