From 0dd0f74c78e40deeed100bdd28c846b775a08617 Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 28 Feb 2022 17:18:28 +0300 Subject: [PATCH 01/27] update CrudFilter type and add "or" operator to CrudOperators --- packages/core/src/contexts/data/IDataContext.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index eb946dddddf6..e2d3930cc04f 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -44,10 +44,11 @@ export type CrudOperators = | "between" | "nbetween" | "null" - | "nnull"; + | "nnull" + | "or"; export type CrudFilter = { - field: string; + field?: string; operator: CrudOperators; value: any; }; From b0b7350ed19ddbef60125b37ff8d805d754f4a4c Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 28 Feb 2022 17:19:22 +0300 Subject: [PATCH 02/27] add "or" operator support to strapi-v4 genereteFilters --- packages/strapi-v4/src/dataProvider.ts | 37 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/strapi-v4/src/dataProvider.ts b/packages/strapi-v4/src/dataProvider.ts index 7ccde3af1b32..c2feeae76d3b 100644 --- a/packages/strapi-v4/src/dataProvider.ts +++ b/packages/strapi-v4/src/dataProvider.ts @@ -5,6 +5,7 @@ import { CrudFilters, CrudSorting, CrudOperators, + CrudFilter, } from "@pankod/refine-core"; import { stringify, parse } from "qs"; @@ -59,14 +60,40 @@ const generateFilter = (filters?: CrudFilters) => { if (filters) { filters.map(({ field, operator, value }) => { - const mapedOperator = mapOperator(operator); + if (operator === "or") { + if (!Array.isArray(value)) { + console.error( + `The value of the "or" operator must be of type [CrudFilters](https://refine.dev/docs/core/interfaceReferences/#crudfilters).`, + ); + } + + value.map((item: CrudFilter, index: number) => { + const mapedOperator = mapOperator(item.operator); - if (Array.isArray(value)) { - value.map((val: string) => { - rawQuery += `&filters${field}[$${mapedOperator}]=${val}`; + if (!item.field) { + console.error( + `Please enter a "field" in the filters inside the operator "or"`, + ); + } + + rawQuery += `&filters[$or][${index}][${item.field}][$${mapedOperator}]=${item.value}`; }); } else { - rawQuery += `&filters[${field}][$${mapedOperator}]=${value}`; + if (!field) { + console.error( + `Please enter a "field" for filters whose operator is not "or"`, + ); + } + + const mapedOperator = mapOperator(operator); + + if (Array.isArray(value)) { + value.map((val: string) => { + rawQuery += `&filters${field}[$${mapedOperator}]=${val}`; + }); + } else { + rawQuery += `&filters[${field}][$${mapedOperator}]=${value}`; + } } }); } From 4c9892c8fab77ace41479dffa370e796b17ec038 Mon Sep 17 00:00:00 2001 From: Salih Date: Tue, 1 Mar 2022 16:26:39 +0300 Subject: [PATCH 03/27] update CrudFilter with discriminated unions --- packages/core/src/contexts/data/IDataContext.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index e2d3930cc04f..7a70398337a8 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -47,12 +47,18 @@ export type CrudOperators = | "nnull" | "or"; -export type CrudFilter = { - field?: string; - operator: CrudOperators; +export type LogicalFilter = { + field: string; + operator: Exclude; value: any; }; +export type ConditionalFilter = { + operator: "or"; + value: LogicalFilter[]; +}; + +export type CrudFilter = LogicalFilter | ConditionalFilter; export type CrudSort = { field: string; order: "asc" | "desc"; From af4b1c73e1020c5b23da998d6e0a9bc3ce817b68 Mon Sep 17 00:00:00 2001 From: Salih Date: Tue, 1 Mar 2022 16:27:24 +0300 Subject: [PATCH 04/27] add "or" operator support to strapi-v4 data provider --- packages/strapi-v4/src/dataProvider.ts | 39 +++++++++----------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/packages/strapi-v4/src/dataProvider.ts b/packages/strapi-v4/src/dataProvider.ts index c2feeae76d3b..63d03d367696 100644 --- a/packages/strapi-v4/src/dataProvider.ts +++ b/packages/strapi-v4/src/dataProvider.ts @@ -5,7 +5,6 @@ import { CrudFilters, CrudSorting, CrudOperators, - CrudFilter, } from "@pankod/refine-core"; import { stringify, parse } from "qs"; @@ -59,31 +58,9 @@ const generateFilter = (filters?: CrudFilters) => { let rawQuery = ""; if (filters) { - filters.map(({ field, operator, value }) => { - if (operator === "or") { - if (!Array.isArray(value)) { - console.error( - `The value of the "or" operator must be of type [CrudFilters](https://refine.dev/docs/core/interfaceReferences/#crudfilters).`, - ); - } - - value.map((item: CrudFilter, index: number) => { - const mapedOperator = mapOperator(item.operator); - - if (!item.field) { - console.error( - `Please enter a "field" in the filters inside the operator "or"`, - ); - } - - rawQuery += `&filters[$or][${index}][${item.field}][$${mapedOperator}]=${item.value}`; - }); - } else { - if (!field) { - console.error( - `Please enter a "field" for filters whose operator is not "or"`, - ); - } + filters.map((filter) => { + if (filter.operator !== "or") { + const { field, operator, value } = filter; const mapedOperator = mapOperator(operator); @@ -94,6 +71,16 @@ const generateFilter = (filters?: CrudFilters) => { } else { rawQuery += `&filters[${field}][$${mapedOperator}]=${value}`; } + } else { + const { value } = filter; + + value.map((item, index) => { + const { field, operator, value } = item; + + const mapedOperator = mapOperator(operator); + + rawQuery += `&filters[$or][${index}][${field}][$${mapedOperator}]=${value}`; + }); } }); } From 35be0224f29f1cd275a5d1831557b3e1e23cf402 Mon Sep 17 00:00:00 2001 From: Salih Date: Wed, 2 Mar 2022 09:53:45 +0300 Subject: [PATCH 05/27] update airtable data provider --- packages/airtable/src/index.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/airtable/src/index.ts b/packages/airtable/src/index.ts index c8f3d9af3838..f31954b41f9f 100644 --- a/packages/airtable/src/index.ts +++ b/packages/airtable/src/index.ts @@ -1,9 +1,9 @@ -import { DataProvider } from "@pankod/refine-core"; import { + DataProvider, CrudFilters, CrudOperators, CrudSorting, -} from "@pankod/refine-core/dist/interfaces"; +} from "@pankod/refine-core"; import { compile, Formula } from "@qualifyze/airtable-formulator"; import Airtable from "airtable"; @@ -27,8 +27,9 @@ const simpleOperators: Partial> = { const generateFilter = (filters?: CrudFilters): string | undefined => { if (filters) { - const parsedFilter = filters.map( - ({ field, operator, value }): Formula => { + const parsedFilter = filters.map((filter): Formula => { + if (filter.operator !== "or") { + const { field, operator, value } = filter; if (Object.keys(simpleOperators).includes(operator)) { const mappedOperator = simpleOperators[ @@ -70,10 +71,10 @@ const generateFilter = (filters?: CrudFilters): string | undefined => { return [value ? "=" : "!=", { field }, ["BLANK"]]; } + } - throw Error(`Operator ${operator} is not supported`); - }, - ); + throw Error(`Operator ${filter.operator} is not supported`); + }); return compile(["AND", ...parsedFilter]); } From 54d905f64666c248c46e5775d742dccec3110e40 Mon Sep 17 00:00:00 2001 From: Salih Date: Wed, 2 Mar 2022 10:04:24 +0300 Subject: [PATCH 06/27] update altogic data provider --- packages/altogic/src/index.ts | 38 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/altogic/src/index.ts b/packages/altogic/src/index.ts index 3a52f85352f4..1af2aa19e0b3 100644 --- a/packages/altogic/src/index.ts +++ b/packages/altogic/src/index.ts @@ -68,23 +68,27 @@ const generateSort = (sort?: CrudSorting) => { const generateFilter = (filters?: CrudFilters) => { const queryFilters: string[] = []; if (filters) { - filters.map(({ field, operator, value }) => { - const mappedOperator = mapOperator(operator); - - switch (mappedOperator) { - case "IN": - case "NIN": - queryFilters.push( - `${mappedOperator}(${JSON.stringify( - value, - )}, this.${field})`, - ); - break; - - default: - queryFilters.push( - `this.${field} ${mappedOperator} "${value}"`, - ); + filters.map((filter) => { + const mappedOperator = mapOperator(filter.operator); + + if (filter.operator !== "or") { + const { field, value } = filter; + + switch (mappedOperator) { + case "IN": + case "NIN": + queryFilters.push( + `${mappedOperator}(${JSON.stringify( + value, + )}, this.${field})`, + ); + break; + + default: + queryFilters.push( + `this.${field} ${mappedOperator} "${value}"`, + ); + } } }); } From 0735a9cd493021c0f0f18497d217690b5d6304ad Mon Sep 17 00:00:00 2001 From: Salih Date: Wed, 2 Mar 2022 10:22:59 +0300 Subject: [PATCH 07/27] update appwrite data provider --- packages/appwrite/src/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/appwrite/src/index.ts b/packages/appwrite/src/index.ts index bdd581fb31c2..639f2d529ae6 100644 --- a/packages/appwrite/src/index.ts +++ b/packages/appwrite/src/index.ts @@ -21,6 +21,7 @@ const operators = { between: undefined, nbetween: undefined, nnull: undefined, + or: undefined, }; const appwriteEventToRefineEvent = { @@ -61,13 +62,16 @@ export const getAppwriteFilters: GetAppwriteFiltersType = (filters) => { for (const filter of filters) { const operator = operators[filter.operator]; - const filterField = filter.field === "id" ? "$id" : filter.field; if (!operator) { throw new Error(`Operator ${filter.operator} is not supported`); } - appwriteFilters.push(`${filterField}${operator}${filter.value}`); + if (filter.operator !== "or") { + const filterField = filter.field === "id" ? "$id" : filter.field; + + appwriteFilters.push(`${filterField}${operator}${filter.value}`); + } } return appwriteFilters; From ee7ae3a54a2b71fb909afdedea40793b060a68da Mon Sep 17 00:00:00 2001 From: Salih Date: Wed, 2 Mar 2022 15:33:41 +0300 Subject: [PATCH 08/27] refactoring stringify and parse functions --- .../table/__snapshots__/index.spec.ts.snap | 4 +- .../core/src/definitions/table/index.spec.ts | 46 +++++++++++-- packages/core/src/definitions/table/index.ts | 69 +++++-------------- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/packages/core/src/definitions/table/__snapshots__/index.spec.ts.snap b/packages/core/src/definitions/table/__snapshots__/index.spec.ts.snap index 17cd79114818..58e1dd002313 100644 --- a/packages/core/src/definitions/table/__snapshots__/index.spec.ts.snap +++ b/packages/core/src/definitions/table/__snapshots__/index.spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`definitions/table stringify table params correctly 1`] = `"current=1&pageSize=10&categoryId__in[]=1&categoryId__in[]=2&sort[]=id&sort[]=title&order[]=desc&order[]=desc"`; +exports[`definitions/table stringify table params correctly 1`] = `"current=1&pageSize=10&sorter[0][field]=id&sorter[0][order]=desc&sorter[1][field]=title&sorter[1][order]=desc&filters[0][field]=categoryId&filters[0][operator]=in&filters[0][value][0]=1&filters[0][value][1]=2"`; -exports[`definitions/table stringify table single sort params correctly 1`] = `"current=1&pageSize=10&categoryId__in[]=1&categoryId__in[]=2&sort[]=id&order[]=desc"`; +exports[`definitions/table stringify table single sort params correctly 1`] = `"current=1&pageSize=10&sorter[0][field]=id&sorter[0][order]=desc&filters[0][field]=categoryId&filters[0][operator]=in&filters[0][value][0]=1&filters[0][value][1]=2"`; diff --git a/packages/core/src/definitions/table/index.spec.ts b/packages/core/src/definitions/table/index.spec.ts index 0c8d108a0c25..72ec4d7dfff1 100644 --- a/packages/core/src/definitions/table/index.spec.ts +++ b/packages/core/src/definitions/table/index.spec.ts @@ -60,7 +60,7 @@ describe("definitions/table", () => { it("parse table params with single sorter correctly", async () => { const url = - "?current=1&pageSize=10&categoryId__in[]=1&categoryId__in[]=2&sort[]=id&order[]=desc"; + "?current=1&pageSize=10&sorter[0][field]=id&sorter[0][order]=desc&filters[0][operator]=in&filters[0][field]=categoryId&filters[0][value][0]=1&filters[0][value][1]=2"; const { parsedCurrent, parsedPageSize, parsedSorter, parsedFilters } = parseTableParams(url); @@ -75,11 +75,30 @@ describe("definitions/table", () => { it("parse table params with advanced query object", async () => { const query = { - current: "1", - pageSize: "10", - id__eq: "1", - sort: ["id", "firstName"], - order: ["asc", "desc"], + current: 1, + pageSize: 10, + sorter: [ + { field: "id", order: "asc" }, + { field: "firstName", order: "desc" }, + ], + filters: [ + { field: "id", operator: "eq", value: "1" }, + { + operator: "or", + value: [ + { + field: "age", + operator: "lt", + value: "18", + }, + { + field: "age", + operator: "gt", + value: "20", + }, + ], + }, + ], }; const { parsedCurrent, parsedPageSize, parsedSorter, parsedFilters } = @@ -93,6 +112,21 @@ describe("definitions/table", () => { ]); expect(parsedFilters).toStrictEqual([ { field: "id", operator: "eq", value: "1" }, + { + operator: "or", + value: [ + { + field: "age", + operator: "lt", + value: "18", + }, + { + field: "age", + operator: "gt", + value: "20", + }, + ], + }, ]); }); diff --git a/packages/core/src/definitions/table/index.ts b/packages/core/src/definitions/table/index.ts index 825666a2403a..beed67622881 100644 --- a/packages/core/src/definitions/table/index.ts +++ b/packages/core/src/definitions/table/index.ts @@ -5,47 +5,21 @@ import differenceWith from "lodash/differenceWith"; import { CrudFilters, - CrudOperators, CrudSorting, CrudFilter, CrudSort, } from "../../interfaces"; export const parseTableParams = (url: string) => { - const { current, pageSize, sort, order, ...filters } = qs.parse( + const { current, pageSize, sorter, filters } = qs.parse( url.substring(1), // remove first ? character ); - const parsedSorter: CrudSorting = []; - if (Array.isArray(sort) && Array.isArray(order)) { - sort.forEach((item: unknown, index: number) => { - const sortOrder = order[index] as "asc" | "desc"; - - parsedSorter.push({ - field: `${item}`, - order: sortOrder, - }); - }); - } - - const parsedFilters: CrudFilters = []; - Object.keys(filters).map((item) => { - const [field, operator] = item.split("__"); - const value = filters[item]; - if (operator) { - parsedFilters.push({ - field, - operator: operator as CrudOperators, - value, - }); - } - }); - return { parsedCurrent: current && Number(current), parsedPageSize: pageSize && Number(pageSize), - parsedSorter, - parsedFilters, + parsedSorter: sorter ?? [], + parsedFilters: filters ?? [], }; }; @@ -61,40 +35,35 @@ export const stringifyTableParams = (params: { }): string => { const options: IStringifyOptions = { skipNulls: true, - arrayFormat: "brackets", + arrayFormat: "indices", encode: false, }; - const { pagination, sorter, filters } = params; - const sortFields = sorter.map((item) => item.field); - const sortOrders = sorter.map((item) => item.order); - - const qsSortFields = qs.stringify({ sort: sortFields }, options); - const qsSortOrders = qs.stringify({ order: sortOrders }, options); - let queryString = `current=${pagination.current}&pageSize=${pagination.pageSize}`; - const qsFilterItems: { [key: string]: string } = {}; - filters.map((filterItem) => { - qsFilterItems[`${filterItem.field}__${filterItem.operator}`] = - filterItem.value; - }); - - const qsFilters = qs.stringify(qsFilterItems, options); - if (qsFilters) { - queryString = `${queryString}&${qsFilters}`; + const qsSorters = qs.stringify({ sorter }, options); + if (qsSorters) { + queryString += `&${qsSorters}`; } - if (qsSortFields && qsSortOrders) { - queryString = `${queryString}&${qsSortFields}&${qsSortOrders}`; + const qsFilters = qs.stringify({ filters }, options); + if (qsFilters) { + queryString += `&${qsFilters}`; } return queryString; }; -export const compareFilters = (left: CrudFilter, right: CrudFilter): boolean => - left.field == right.field && left.operator == right.operator; +export const compareFilters = ( + left: CrudFilter, + right: CrudFilter, +): boolean => { + if (left.operator !== "or" && right.operator !== "or") { + return left.field == right.field && left.operator == right.operator; + } + return false; +}; export const compareSorters = (left: CrudSort, right: CrudSort): boolean => left.field == right.field; From e6e9a5d8069f5892c03feddf4a697d19c02333e2 Mon Sep 17 00:00:00 2001 From: Salih Date: Wed, 2 Mar 2022 16:19:50 +0300 Subject: [PATCH 09/27] fix strapi-v4 in operator bug --- packages/strapi-v4/src/dataProvider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/strapi-v4/src/dataProvider.ts b/packages/strapi-v4/src/dataProvider.ts index 63d03d367696..537d70fd9ea8 100644 --- a/packages/strapi-v4/src/dataProvider.ts +++ b/packages/strapi-v4/src/dataProvider.ts @@ -65,8 +65,8 @@ const generateFilter = (filters?: CrudFilters) => { const mapedOperator = mapOperator(operator); if (Array.isArray(value)) { - value.map((val: string) => { - rawQuery += `&filters${field}[$${mapedOperator}]=${val}`; + value.map((val, index) => { + rawQuery += `&filters[${field}][$${mapedOperator}][${index}]=${val}`; }); } else { rawQuery += `&filters[${field}][$${mapedOperator}]=${value}`; From 1d0116a139348fc35703b056b1c25687cd868d4f Mon Sep 17 00:00:00 2001 From: Salih Date: Wed, 2 Mar 2022 17:54:32 +0300 Subject: [PATCH 10/27] fix getDefaultFilter and mapAntdFilterToCrudFilter functions in antd package --- packages/antd/src/definitions/table/index.ts | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/antd/src/definitions/table/index.ts b/packages/antd/src/definitions/table/index.ts index cc416b5c1e2b..4a9644bd0b89 100644 --- a/packages/antd/src/definitions/table/index.ts +++ b/packages/antd/src/definitions/table/index.ts @@ -4,6 +4,7 @@ import { CrudSorting, CrudFilter, } from "@pankod/refine-core"; +import { LogicalFilter } from "@pankod/refine-core/dist/interfaces"; import { SortOrder, SorterResult } from "antd/lib/table/interface"; export const getDefaultSortOrder = ( @@ -28,10 +29,13 @@ export const getDefaultFilter = ( filters?: CrudFilters, operatorType: CrudOperators = "eq", ): CrudFilter["value"] | undefined => { - const filter = filters?.find( - ({ field, operator }) => - field === columnName && operator === operatorType, - ); + const filter = filters?.find((filter) => { + if (filter.operator !== "or") { + const { operator, field } = filter; + return field === columnName && operator === operatorType; + } + return undefined; + }); if (filter) { return filter.value || []; @@ -84,13 +88,17 @@ export const mapAntdFilterToCrudFilter = ( Object.keys(tableFilters).map((field) => { const value = tableFilters[field]; - const operator = prevFilters.find((p) => p.field === field)?.operator; + const operator = prevFilters + .filter((i) => i.operator !== "or") + .find((p: LogicalFilter) => p.field === field)?.operator; - crudFilters.push({ - field, - operator: operator ?? (Array.isArray(value) ? "in" : "eq"), - value, - }); + if (operator !== "or") { + crudFilters.push({ + field, + operator: operator ?? (Array.isArray(value) ? "in" : "eq"), + value, + }); + } }); return crudFilters; From 20019545e98b5e592bc3ab9ec51af16ab32e2467 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 10:35:09 +0300 Subject: [PATCH 11/27] add or operator support to strapi data provider --- packages/strapi/src/dataProvider.ts | 38 ++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/strapi/src/dataProvider.ts b/packages/strapi/src/dataProvider.ts index 6cef7e90c93f..f6844303bab0 100644 --- a/packages/strapi/src/dataProvider.ts +++ b/packages/strapi/src/dataProvider.ts @@ -39,18 +39,36 @@ const generateSort = (sort?: CrudSorting) => { }; const generateFilter = (filters?: CrudFilters) => { - const queryFilters: { [key: string]: string } = {}; + let rawQuery = ""; + if (filters) { - filters.map(({ field, operator, value }) => { - if (operator === "eq") { - queryFilters[`${field}`] = value; + filters.map((filter) => { + if (filter.operator !== "or") { + const { field, operator, value } = filter; + if (operator === "eq") { + rawQuery += `&${field}=${value}`; + } else { + if (Array.isArray(value)) { + value.map((val) => { + rawQuery += `&[${field}_${operator}]=${val}`; + }); + } else { + rawQuery += `&[${field}_${operator}]=${value}`; + } + } } else { - queryFilters[`${field}_${operator}`] = value; + const { value } = filter; + + value.map((item, index) => { + const { field, operator, value } = item; + + rawQuery += `&_where[_or][${index}][${field}_${operator}]=${value}`; + }); } }); } - return queryFilters; + return rawQuery; }; export const DataProvider = ( @@ -73,10 +91,8 @@ export const DataProvider = ( }; const response = await Promise.all([ - httpClient.get( - `${url}?${stringify(query)}&${stringify(queryFilters)}`, - ), - httpClient.get(`${url}/count?${stringify(queryFilters)}`), + httpClient.get(`${url}?${stringify(query)}&${queryFilters}`), + httpClient.get(`${url}/count?${queryFilters}`), ]); return { @@ -185,7 +201,7 @@ export const DataProvider = ( if (filters) { const filterQuery = generateFilter(filters); - requestUrl = `${requestUrl}&${stringify(filterQuery)}`; + requestUrl = `${requestUrl}&${filterQuery}`; } if (query) { From 4ddbda13862583df3f66750e5f5b042b7f54f927 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 11:13:18 +0300 Subject: [PATCH 12/27] add nullish control to strapi graphql example --- .../dataProvider/strapi-graphql/src/interfaces/index.d.ts | 2 +- examples/dataProvider/strapi-graphql/src/pages/posts/edit.tsx | 4 ++-- examples/dataProvider/strapi-graphql/src/pages/posts/list.tsx | 4 ++-- examples/dataProvider/strapi-graphql/src/pages/posts/show.tsx | 2 +- packages/strapi/src/dataProvider.ts | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/dataProvider/strapi-graphql/src/interfaces/index.d.ts b/examples/dataProvider/strapi-graphql/src/interfaces/index.d.ts index 1bf6b6472725..5697a56ebc54 100644 --- a/examples/dataProvider/strapi-graphql/src/interfaces/index.d.ts +++ b/examples/dataProvider/strapi-graphql/src/interfaces/index.d.ts @@ -7,5 +7,5 @@ export interface IPost { id: string; title: string; content: string; - category: ICategory; + category?: ICategory; } diff --git a/examples/dataProvider/strapi-graphql/src/pages/posts/edit.tsx b/examples/dataProvider/strapi-graphql/src/pages/posts/edit.tsx index 190bb3b372ef..512e02a0fa70 100644 --- a/examples/dataProvider/strapi-graphql/src/pages/posts/edit.tsx +++ b/examples/dataProvider/strapi-graphql/src/pages/posts/edit.tsx @@ -41,7 +41,7 @@ export const PostEdit: React.FC = () => { const postData = queryResult?.data?.data; const { selectProps: categorySelectProps } = useSelect({ resource: "categories", - defaultValue: postData?.category.id, + defaultValue: postData?.category?.id, metaData: { fields: ["id", "title"], }, @@ -68,7 +68,7 @@ export const PostEdit: React.FC = () => { onFinish={(values) => formProps.onFinish?.({ ...values, - category: values.category.id, + category: values.category?.id, } as any) } > diff --git a/examples/dataProvider/strapi-graphql/src/pages/posts/list.tsx b/examples/dataProvider/strapi-graphql/src/pages/posts/list.tsx index 95b04d67b181..8faa6e0515e0 100644 --- a/examples/dataProvider/strapi-graphql/src/pages/posts/list.tsx +++ b/examples/dataProvider/strapi-graphql/src/pages/posts/list.tsx @@ -52,7 +52,7 @@ export const PostList: React.FC = () => { id: item.id, title: item.title, content: item.content, - category: item.category.id, + category: item.category?.id, }; }, metaData: { @@ -113,7 +113,7 @@ export const PostList: React.FC = () => { /> )} - render={(_, record) => record.category.title} + render={(_, record) => record.category?.title} /> title="Actions" diff --git a/examples/dataProvider/strapi-graphql/src/pages/posts/show.tsx b/examples/dataProvider/strapi-graphql/src/pages/posts/show.tsx index c6914c0ae329..9cb41f4d37bb 100644 --- a/examples/dataProvider/strapi-graphql/src/pages/posts/show.tsx +++ b/examples/dataProvider/strapi-graphql/src/pages/posts/show.tsx @@ -41,7 +41,7 @@ export const PostShow: React.FC = () => { {record?.title} Category - {record?.category.title} + {record?.category?.title} Content diff --git a/packages/strapi/src/dataProvider.ts b/packages/strapi/src/dataProvider.ts index f6844303bab0..8a94704f754a 100644 --- a/packages/strapi/src/dataProvider.ts +++ b/packages/strapi/src/dataProvider.ts @@ -45,6 +45,7 @@ const generateFilter = (filters?: CrudFilters) => { filters.map((filter) => { if (filter.operator !== "or") { const { field, operator, value } = filter; + if (operator === "eq") { rawQuery += `&${field}=${value}`; } else { From 234343af07f06eae9b55435ae651baccb70103a3 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 12:30:36 +0300 Subject: [PATCH 13/27] add or operator support to strapi graphql data provider --- packages/strapi-graphql/src/index.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/strapi-graphql/src/index.ts b/packages/strapi-graphql/src/index.ts index 2411c5b81cc9..cb7db666b26c 100644 --- a/packages/strapi-graphql/src/index.ts +++ b/packages/strapi-graphql/src/index.ts @@ -3,7 +3,6 @@ import { GraphQLClient } from "graphql-request"; import * as gql from "gql-query-builder"; import pluralize from "pluralize"; import camelCase from "camelcase"; -import { stringify } from "query-string"; const genereteSort = (sort?: CrudSorting) => { if (sort && sort.length > 0) { @@ -18,14 +17,29 @@ const genereteSort = (sort?: CrudSorting) => { }; const generateFilter = (filters?: CrudFilters) => { - const queryFilters: { [key: string]: string } = {}; + const queryFilters: { [key: string]: any } = {}; if (filters) { - filters.map(({ field, operator, value }) => { - if (operator === "eq") { - queryFilters[`${field}`] = value; + filters.map((filter) => { + if (filter.operator !== "or") { + const { field, operator, value } = filter; + + if (operator === "eq") { + queryFilters[`${field}`] = value; + } else { + queryFilters[`${field}_${operator}`] = value; + } } else { - queryFilters[`${field}_${operator}`] = value; + const { value } = filter; + + const orFilters: any[] = []; + value.map((val) => { + orFilters.push({ + [`${val.field}_${val.operator}`]: val.value, + }); + }); + + queryFilters["_or"] = orFilters; } }); } From 606bba28920504e78e9e426d67570a2ebd5cc6d5 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 15:26:14 +0300 Subject: [PATCH 14/27] add or operator support to supabase data provider --- packages/supabase/src/index.ts | 56 +++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/packages/supabase/src/index.ts b/packages/supabase/src/index.ts index aee0b22509ab..aec97e34c9d8 100644 --- a/packages/supabase/src/index.ts +++ b/packages/supabase/src/index.ts @@ -4,6 +4,7 @@ import { CrudFilter, LiveEvent, HttpError, + CrudOperators, } from "@pankod/refine-core"; import { createClient, @@ -30,25 +31,66 @@ const supabaseTypes: Record = { "*": "*", }; +const mapOperator = (operator: CrudOperators) => { + switch (operator) { + case "ne": + return "neq"; + case "nin": + return "not.in"; + case "contains": + return "ilike"; + case "ncontains": + return "not.ilike"; + case "containss": + return "like"; + case "ncontainss": + return "not.like"; + case "null": + return "is"; + case "nnull": + return "not.is"; + case "between": + case "nbetween": + throw Error(`Operator ${operator} is not supported`); + default: + return operator; + } +}; + const generateFilter = (filter: CrudFilter, query: any) => { switch (filter.operator) { + case "eq": + return query.eq(filter.field, filter.value); case "ne": - return query.filter(filter.field, "neq", filter.value); + return query.neq(filter.field, filter.value); case "in": return query.in(filter.field, filter.value); + case "gt": + return query.gt(filter.field, filter.value); + case "gte": + return query.gte(filter.field, filter.value); + case "lt": + return query.lt(filter.field, filter.value); + case "lte": + return query.lte(filter.field, filter.value); case "contains": return query.ilike(filter.field, `%${filter.value}%`); case "containss": return query.like(filter.field, `%${filter.value}%`); - case "ncontainss": - case "ncontains": - case "nin": - throw Error(`Operator ${filter.operator} is not supported`); case "null": return query.is(filter.field, null); + case "or": + const orSyntax = filter.value + .map((item) => `${item.field}.${item.operator}.${item.value}`) + .join(","); + return query.or(orSyntax); + default: + return query.filter( + filter.field, + mapOperator(filter.operator), + filter.value, + ); } - - return query.filter(filter.field, filter.operator, filter.value); }; const handleError = (error: PostgrestError) => { From a793ca34f280e24f41bfed7ed2feba2638487538 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 16:10:58 +0300 Subject: [PATCH 15/27] update simple rest data provider --- packages/simple-rest/src/index.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/simple-rest/src/index.ts b/packages/simple-rest/src/index.ts index 9be85aaffc71..9e85c4083612 100644 --- a/packages/simple-rest/src/index.ts +++ b/packages/simple-rest/src/index.ts @@ -61,14 +61,18 @@ const generateSort = (sort?: CrudSorting) => { const generateFilter = (filters?: CrudFilters) => { const queryFilters: { [key: string]: string } = {}; if (filters) { - filters.map(({ field, operator, value }) => { - if (field === "q") { - queryFilters[field] = value; - return; - } + filters.map((filter) => { + if (filter.operator !== "or") { + const { field, operator, value } = filter; + + if (field === "q") { + queryFilters[field] = value; + return; + } - const mappedOperator = mapOperator(operator); - queryFilters[`${field}${mappedOperator}`] = value; + const mappedOperator = mapOperator(operator); + queryFilters[`${field}${mappedOperator}`] = value; + } }); } From 24753029005d369257e9a3d50d4c25a0bb7806ea Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 16:17:13 +0300 Subject: [PATCH 16/27] update graphql data provider --- packages/graphql/src/index.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/graphql/src/index.ts b/packages/graphql/src/index.ts index d3c62a9fd966..9f14adcf6423 100644 --- a/packages/graphql/src/index.ts +++ b/packages/graphql/src/index.ts @@ -17,11 +17,30 @@ const genereteSort = (sort?: CrudSorting) => { }; const generateFilter = (filters?: CrudFilters) => { - const queryFilters: { [key: string]: string } = {}; + const queryFilters: { [key: string]: any } = {}; if (filters) { - filters.map(({ field, operator, value }) => { - queryFilters[`${field}_${operator}`] = value; + filters.map((filter) => { + if (filter.operator !== "or") { + const { field, operator, value } = filter; + + if (operator === "eq") { + queryFilters[`${field}`] = value; + } else { + queryFilters[`${field}_${operator}`] = value; + } + } else { + const { value } = filter; + + const orFilters: any[] = []; + value.map((val) => { + orFilters.push({ + [`${val.field}_${val.operator}`]: val.value, + }); + }); + + queryFilters["_or"] = orFilters; + } }); } From 0421d1c167009f2f80d82e858f7e7be4f1e820fd Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 16:51:31 +0300 Subject: [PATCH 17/27] add or operator support to hasura data provider --- packages/hasura/src/index.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/hasura/src/index.ts b/packages/hasura/src/index.ts index 2f902df2245a..93372814c6fb 100644 --- a/packages/hasura/src/index.ts +++ b/packages/hasura/src/index.ts @@ -60,6 +60,7 @@ const hasuraFilters: Record = containss: "_like", ncontainss: "_nlike", null: "_is_null", + or: "_or", between: undefined, nbetween: undefined, nnull: undefined, @@ -72,15 +73,34 @@ export const generateFilters: any = (filters?: CrudFilters) => { const resultFilter: any = {}; - filters.forEach((filter) => { - resultFilter[filter.field] = {}; + filters.map((filter) => { const operator = hasuraFilters[filter.operator]; - if (!operator) { throw new Error(`Operator ${filter.operator} is not supported`); } - resultFilter[filter.field][operator] = filter.value; + if (filter.operator !== "or") { + resultFilter[filter.field] = {}; + resultFilter[filter.field][operator] = filter.value; + } else { + const orFilter: any = []; + + filter.value.map((val) => { + const filterObject: any = {}; + const mapedOperator = hasuraFilters[val.operator]; + + if (!mapedOperator) { + throw new Error( + `Operator ${val.operator} is not supported`, + ); + } + filterObject[val.field] = {}; + filterObject[val.field][mapedOperator] = val.value; + orFilter.push(filterObject); + }); + + resultFilter[operator] = orFilter; + } }); return resultFilter; From 06f71c9b46f93b7c7693c3725b8fa1008783a7c5 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 17:32:53 +0300 Subject: [PATCH 18/27] add or operator support to nhost data provider --- packages/nhost/src/index.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/nhost/src/index.ts b/packages/nhost/src/index.ts index 0bfe319aba8b..87e717fac4a4 100644 --- a/packages/nhost/src/index.ts +++ b/packages/nhost/src/index.ts @@ -60,6 +60,7 @@ const hasuraFilters: Record = containss: "_like", ncontainss: "_nlike", null: "_is_null", + or: "_or", between: undefined, nbetween: undefined, nnull: undefined, @@ -72,15 +73,34 @@ export const generateFilters: any = (filters?: CrudFilters) => { const resultFilter: any = {}; - filters.forEach((filter) => { - resultFilter[filter.field] = {}; + filters.map((filter) => { const operator = hasuraFilters[filter.operator]; - if (!operator) { throw new Error(`Operator ${filter.operator} is not supported`); } - resultFilter[filter.field][operator] = filter.value; + if (filter.operator !== "or") { + resultFilter[filter.field] = {}; + resultFilter[filter.field][operator] = filter.value; + } else { + const orFilter: any = []; + + filter.value.map((val) => { + const filterObject: any = {}; + const mapedOperator = hasuraFilters[val.operator]; + + if (!mapedOperator) { + throw new Error( + `Operator ${val.operator} is not supported`, + ); + } + filterObject[val.field] = {}; + filterObject[val.field][mapedOperator] = val.value; + orFilter.push(filterObject); + }); + + resultFilter[operator] = orFilter; + } }); return resultFilter; From cf372cc12b8d5d1d441376bf25cbfb9ea8ca1bbb Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 17:53:55 +0300 Subject: [PATCH 19/27] add or operator support to nestjsx crud data provider --- packages/nestjsx-crud/src/index.ts | 41 ++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/nestjsx-crud/src/index.ts b/packages/nestjsx-crud/src/index.ts index 7d7d18d37ccb..b54b2a723e0a 100644 --- a/packages/nestjsx-crud/src/index.ts +++ b/packages/nestjsx-crud/src/index.ts @@ -91,19 +91,33 @@ const generateSort = (sort?: CrudSorting): SortBy | undefined => { return; }; -const generateFilter = (filters?: RefineCrudFilter): CrudFilters => { +const generateFilter = ( + filters?: RefineCrudFilter, +): { crudFilters: CrudFilters; orFilters: CrudFilters } => { const crudFilters: CrudFilters = []; + const orFilters: CrudFilters = []; + if (filters) { - filters.map(({ field, operator, value }) => { - crudFilters.push({ - field, - operator: mapOperator(operator), - value, - }); + filters.map((filter) => { + if (filter.operator !== "or") { + crudFilters.push({ + field: filter.field, + operator: mapOperator(filter.operator), + value: filter.value, + }); + } else { + filter.value.map((orFilter) => { + orFilters.push({ + field: orFilter.field, + operator: mapOperator(orFilter.operator), + value: orFilter.value, + }); + }); + } }); } - return crudFilters; + return { crudFilters, orFilters }; }; const NestsxCrud = ( @@ -115,10 +129,11 @@ const NestsxCrud = ( const current = pagination?.current || 1; const pageSize = pagination?.pageSize || 10; - const generetedFilters = generateFilter(filters); + const { crudFilters, orFilters } = generateFilter(filters); const query = RequestQueryBuilder.create() - .setFilter(generetedFilters) + .setFilter(crudFilters) + .setOr(orFilters) .setLimit(pageSize) .setPage(current) .setOffset((current - 1) * pageSize); @@ -235,9 +250,9 @@ const NestsxCrud = ( }, custom: async ({ url, method, filters, sort, payload, query, headers }) => { - const requestQueryBuilder = RequestQueryBuilder.create().setFilter( - generateFilter(filters), - ); + const { crudFilters } = generateFilter(filters); + const requestQueryBuilder = + RequestQueryBuilder.create().setFilter(crudFilters); const sortBy = generateSort(sort); if (sortBy) { From 80d4d3541f7651ee0d8251ef578217419306cb63 Mon Sep 17 00:00:00 2001 From: Salih Date: Thu, 3 Mar 2022 17:54:43 +0300 Subject: [PATCH 20/27] add types for parsed sorters and parsed filters --- packages/core/src/definitions/table/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/definitions/table/index.ts b/packages/core/src/definitions/table/index.ts index beed67622881..69550bcc2689 100644 --- a/packages/core/src/definitions/table/index.ts +++ b/packages/core/src/definitions/table/index.ts @@ -18,8 +18,8 @@ export const parseTableParams = (url: string) => { return { parsedCurrent: current && Number(current), parsedPageSize: pageSize && Number(pageSize), - parsedSorter: sorter ?? [], - parsedFilters: filters ?? [], + parsedSorter: (sorter as CrudSorting) ?? [], + parsedFilters: (filters as CrudFilters) ?? [], }; }; From df36a4e1eee311cc6197c1e785bbb04282ff62d0 Mon Sep 17 00:00:00 2001 From: Salih Date: Fri, 4 Mar 2022 12:58:37 +0300 Subject: [PATCH 21/27] export logical filter and conditional filter types --- packages/antd/src/definitions/table/index.ts | 2 +- packages/core/src/index.tsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/antd/src/definitions/table/index.ts b/packages/antd/src/definitions/table/index.ts index 4a9644bd0b89..95d10254812b 100644 --- a/packages/antd/src/definitions/table/index.ts +++ b/packages/antd/src/definitions/table/index.ts @@ -3,8 +3,8 @@ import { CrudOperators, CrudSorting, CrudFilter, + LogicalFilter, } from "@pankod/refine-core"; -import { LogicalFilter } from "@pankod/refine-core/dist/interfaces"; import { SortOrder, SorterResult } from "antd/lib/table/interface"; export const getDefaultSortOrder = ( diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 1a40a107697f..f2f49afdf04a 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -20,6 +20,8 @@ export { TitleProps, CrudFilter, CrudFilters, + LogicalFilter, + ConditionalFilter, CrudOperators, CrudSorting, CrudSort, From ea09e535056f3f3b7e63e389d96735f69fa89445 Mon Sep 17 00:00:00 2001 From: Salih Date: Fri, 4 Mar 2022 12:59:05 +0300 Subject: [PATCH 22/27] fix react table package --- packages/react-table/src/useTable/index.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/react-table/src/useTable/index.ts b/packages/react-table/src/useTable/index.ts index 43aece9ef48b..a4d8da9ce48e 100644 --- a/packages/react-table/src/useTable/index.ts +++ b/packages/react-table/src/useTable/index.ts @@ -1,9 +1,9 @@ import { useEffect, useMemo } from "react"; import { BaseRecord, - CrudFilters, CrudOperators, HttpError, + LogicalFilter, useTable as useTableCore, useTableProps as useTablePropsCore, } from "@pankod/refine-core"; @@ -43,6 +43,13 @@ export const useTable = < setFilters, } = useTableResult; + const logicalFilters: LogicalFilter[] = []; + filtersCore.map((filter) => { + if (filter.operator !== "or") { + logicalFilters.push(filter); + } + }); + const memoizedData = useMemo(() => data?.data ?? [], [data]); const reactTableResult = useTableRT( { @@ -54,7 +61,7 @@ export const useTable = < id: sorting.field, desc: sorting.order === "desc", })), - filters: filtersCore.map((filter) => ({ + filters: logicalFilters.map((filter) => ({ id: filter.field, value: filter.value, })), @@ -90,12 +97,12 @@ export const useTable = < }, [sortBy]); useEffect(() => { - const crudFilters: CrudFilters = []; + const crudFilters: LogicalFilter[] = []; filters?.map((filter) => { const operator = reactTableResult.columns.find( (c) => c.id === filter.id, - )?.filter as CrudOperators; + )?.filter as Exclude; crudFilters.push({ field: filter.id, @@ -105,7 +112,7 @@ export const useTable = < }); }); - const filteredArray = filtersCore.filter( + const filteredArray = logicalFilters.filter( (value) => !crudFilters.some( (b) => From b3faceaa547942e61a26cd3e450bea3634267937 Mon Sep 17 00:00:00 2001 From: Salih Date: Fri, 4 Mar 2022 14:51:54 +0300 Subject: [PATCH 23/27] fix graphql package tests --- packages/graphql/test/getList/index.mock.ts | 87 ++++++++++++--------- packages/graphql/test/getList/index.spec.ts | 14 ++-- packages/graphql/test/gqlClient.ts | 2 +- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/packages/graphql/test/getList/index.mock.ts b/packages/graphql/test/getList/index.mock.ts index 55e709a2e81b..7ca375975c0f 100644 --- a/packages/graphql/test/getList/index.mock.ts +++ b/packages/graphql/test/getList/index.mock.ts @@ -81,61 +81,78 @@ nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true }) nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true }) .post("/graphql", { query: "query ($sort: String, $where: JSON, $start: Int, $limit: Int) { posts (sort: $sort, where: $where, start: $start, limit: $limit) { title } }", - variables: { where: { title_eq: "GraphQl 3" }, start: 0, limit: 10 }, + variables: { where: { id: "907" }, start: 0, limit: 10 }, }) - .reply(200, { data: { posts: [{ title: "GraphQl 3" }] } }, [ - "Server", - "nginx/1.17.10", - "Date", - "Fri, 17 Sep 2021 07:34:57 GMT", - "Content-Type", - "application/json", - "Content-Length", - "43", - "Connection", - "close", - "Vary", - "Origin", - "Strict-Transport-Security", - "max-age=31536000; includeSubDomains", - "X-Frame-Options", - "SAMEORIGIN", - "X-Powered-By", - "Strapi ", - "X-Response-Time", - "39ms", - ]); + .reply( + 200, + { + data: { + posts: [ + { + title: "Molestias iste voluptatem velit sed voluptate aut voluptatibus explicabo.", + }, + ], + }, + }, + [ + "Server", + "nginx/1.17.10", + "Date", + "Fri, 04 Mar 2022 11:33:59 GMT", + "Content-Type", + "application/json", + "Content-Length", + "107", + "Connection", + "close", + "Vary", + "Origin", + "Strict-Transport-Security", + "max-age=31536000; includeSubDomains", + "X-Frame-Options", + "SAMEORIGIN", + "X-Powered-By", + "Strapi ", + "X-Response-Time", + "24ms", + ], + ); nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true }) .post("/graphql", { query: "query ($sort: String, $where: JSON, $start: Int, $limit: Int) { posts (sort: $sort, where: $where, start: $start, limit: $limit) { id, title, category { id, title } } }", variables: { sort: "title:asc", - where: { category_eq: "2" }, + where: { category: "8" }, start: 0, limit: 10, }, }) .reply( 200, - [ - "1f8b0800000000000003aa564a492c4954b2aa562ac82f2e2956b28aae56ca4c51b252323254d2512ac92cc94905721cf3f24b32528b14fc52cb150280ea8052c98925a9e9f9459520ad100d48ea5d5273f3956a6b756052265434cb948a665950cf2c63032a9a654445b38ca9689619f5cc32c46796426801a9c621077f62710a101169426c6d2d17000000ffff", - "0300efcf3e6706030000", - ], + { + data: { + posts: [ + { + id: "349", + title: "Illo non iusto rem distinctio sequi dolores nobis.", + category: { id: "8", title: "Test" }, + }, + ], + }, + }, [ "Server", "nginx/1.17.10", "Date", - "Fri, 17 Sep 2021 07:45:46 GMT", + "Fri, 04 Mar 2022 11:50:17 GMT", "Content-Type", "application/json", - "Transfer-Encoding", - "chunked", + "Content-Length", + "132", "Connection", "close", "Vary", - "Accept-Encoding", - "Vary", "Origin", "Strict-Transport-Security", "max-age=31536000; includeSubDomains", @@ -144,8 +161,6 @@ nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true }) "X-Powered-By", "Strapi ", "X-Response-Time", - "64ms", - "Content-Encoding", - "gzip", + "37ms", ], ); diff --git a/packages/graphql/test/getList/index.spec.ts b/packages/graphql/test/getList/index.spec.ts index 00e33059fd08..700ebbd74b26 100644 --- a/packages/graphql/test/getList/index.spec.ts +++ b/packages/graphql/test/getList/index.spec.ts @@ -37,9 +37,9 @@ describe("getList", () => { resource: "posts", filters: [ { - field: "title", + field: "id", operator: "eq", - value: "GraphQl 3", + value: "907", }, ], metaData: { @@ -47,7 +47,9 @@ describe("getList", () => { }, }); - expect(data[0]["title"]).toBe("GraphQl 3"); + expect(data[0]["title"]).toBe( + "Molestias iste voluptatem velit sed voluptate aut voluptatibus explicabo.", + ); }); it("correct filter and sort response", async () => { @@ -57,7 +59,7 @@ describe("getList", () => { { field: "category", operator: "eq", - value: "2", + value: "8", }, ], sort: [ @@ -71,7 +73,7 @@ describe("getList", () => { }, }); - expect(response.data[0]["id"]).toBe("21"); - expect(response.data[0]["category"].title).toBe("Demo"); + expect(response.data[0]["id"]).toBe("349"); + expect(response.data[0]["category"].title).toBe("Test"); }); }); diff --git a/packages/graphql/test/gqlClient.ts b/packages/graphql/test/gqlClient.ts index 60a2af854984..d87338356efd 100644 --- a/packages/graphql/test/gqlClient.ts +++ b/packages/graphql/test/gqlClient.ts @@ -6,7 +6,7 @@ const client = new GraphQLClient(API_URL); client.setHeader( "Authorization", - "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjMxNzk1ODUyLCJleHAiOjE2MzQzODc4NTJ9.d7-y7lmdWv_duYJ7kEvupXnu6k9N7zWmX4UDBrTaT2I", + "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjQ2MzkzNDY2LCJleHAiOjE2NDg5ODU0NjZ9.5TjmTOLL7x7kcNKpq9MFwI_w1fReF4f-wlir2rocvi8", ); export default client; From cdddc76d926c461a844d0c63d498d260ba892d4c Mon Sep 17 00:00:00 2001 From: Salih Date: Fri, 4 Mar 2022 14:57:31 +0300 Subject: [PATCH 24/27] remove some supabase tests beacause these test are supported, now --- packages/supabase/test/getList/index.spec.ts | 53 -------------------- 1 file changed, 53 deletions(-) diff --git a/packages/supabase/test/getList/index.spec.ts b/packages/supabase/test/getList/index.spec.ts index 96b4a396cf73..94201bc9e8a9 100644 --- a/packages/supabase/test/getList/index.spec.ts +++ b/packages/supabase/test/getList/index.spec.ts @@ -153,23 +153,6 @@ describe("filtering", () => { expect(total).toBe(2); }); - it("nin operator should throw error", async () => { - try { - await dataProvider(supabaseClient).getList({ - resource: "posts", - filters: [ - { - field: "id", - operator: "nin", - value: ["2", "3"], - }, - ], - }); - } catch (error) { - expect(error).toEqual(Error("Operator nin is not supported")); - } - }); - it("contains operator should work correctly", async () => { const { data, total } = await dataProvider(supabaseClient).getList({ resource: "posts", @@ -186,23 +169,6 @@ describe("filtering", () => { expect(total).toBe(0); }); - it("ncontains operator should throw error", async () => { - try { - await dataProvider(supabaseClient).getList({ - resource: "posts", - filters: [ - { - field: "id", - operator: "ncontains", - value: "world", - }, - ], - }); - } catch (error) { - expect(error).toEqual(Error("Operator ncontains is not supported")); - } - }); - it("containss operator should work correctly", async () => { const { data, total } = await dataProvider(supabaseClient).getList({ resource: "posts", @@ -219,25 +185,6 @@ describe("filtering", () => { expect(total).toBe(1); }); - it("ncontainss operator should throw error", async () => { - try { - await dataProvider(supabaseClient).getList({ - resource: "posts", - filters: [ - { - field: "id", - operator: "ncontainss", - value: "world", - }, - ], - }); - } catch (error) { - expect(error).toEqual( - Error("Operator ncontainss is not supported"), - ); - } - }); - it("null operator should work correctly", async () => { const { data, total } = await dataProvider(supabaseClient).getList({ resource: "posts", From 98215aa251dab1ec096ebebaffbda68c421e4eea Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 7 Mar 2022 15:14:52 +0300 Subject: [PATCH 25/27] add handling filters doc --- .../data-provider/handling-filters.md | 136 ++++++++++++++++++ documentation/sidebars.js | 1 + 2 files changed, 137 insertions(+) create mode 100644 documentation/docs/guides-and-concepts/data-provider/handling-filters.md diff --git a/documentation/docs/guides-and-concepts/data-provider/handling-filters.md b/documentation/docs/guides-and-concepts/data-provider/handling-filters.md new file mode 100644 index 000000000000..cd715e0f7e13 --- /dev/null +++ b/documentation/docs/guides-and-concepts/data-provider/handling-filters.md @@ -0,0 +1,136 @@ +--- +id: handling-filters +title: Handling Filters +--- + +**refine** expects an array of type `CrudFilters` to filter results based on some field’s values. So you can use more than one filter. Even the `or` operator can be used to combine multiple filters. + +## `CrudFilters` + +`CrudFilters` is an array of objects with the following properties: + +```ts +// Supported operators: +type CrudOperators = + | "eq" + | "ne" + | "lt" + | "gt" + | "lte" + | "gte" + | "in" + | "nin" + | "contains" + | "ncontains" + | "containss" + | "ncontainss" + | "between" + | "nbetween" + | "null" + | "nnull" + | "or"; + +// Supported filter types: +type LogicalFilter = { + field: string; + operator: Exclude; + value: any; +}; + +type ConditionalFilter = { + operator: "or"; + value: LogicalFilter[]; +}; + +type CrudFilter = LogicalFilter | ConditionalFilter; +//highlight-next-line +type CrudFilters = CrudFilter[]; +``` + +## `LogicalFilters` + +`LogicalFilter` works with `AND` logic. For example, if you want to filter by `name` field and `age` field, you can use the following filter: + +```ts +const filter = [ + { + field: "name", + operator: "eq", + value: "John", + }, + { + field: "age", + operator: "lt", + value: 30, + }, +]; +``` + +Here the query will look like: `"name" = "John" AND "age" < 30` + +## `ConditionalFilters` + +`ConditionalFilter` works only with `or` operator and expects an array of `LogicalFilter` objects in the `value` property. For example, if you want to filter by `age` field or `createdAt` field, you can use the following filter: + +```ts +const filter = [ + { + field: "age", + operator: "eq", + value: 30, + }, + { + field: "createdAt", + operator: "gte", + value: "2018-01-01", + }, +]; +``` + +Here the query will look like: `"age" = 30 OR "createdAt" <= "2018-01-01"` + +## Combining Filters + +You can group multiple parameters in the same query using the logical filters or the conditional filters operators to filter results based on more than one criteria at the same time. This allows you to create more complex queries. + +Example query: Find posts with 2 possible dates & a specific status + +```ts +filter = [ + { + operator: "or", + value: [ + { + field: "createdAt", + operator: "eq", + value: "2022-01-01", + }, + { + field: "createdAt", + operator: "eq", + value: "2022-01-02", + }, + ], + }, + { + operator: "eq", + field: "status", + value: "published", + }, +]; +``` + +Here the query will look like: +`"status" == "published" AND ("createdAt" == "2022-01-01" OR "createdAt" == "2022-01-02")` + +### Handle Filters in a Data Provider + +```tsx title="dataProvider.ts" +import { DataProvider } from "@pankod/refine-core"; + +const dataProvider = (): DataProvider => ({ + getList: async ({ resource, pagination, filters, sort }) => { + + }, +} +``` diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 5694e3238b03..d3c2f0d15ad2 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -306,6 +306,7 @@ module.exports = { type: "category", label: "Data Provider", items: [ + "guides-and-concepts/data-provider/handling-filters", "guides-and-concepts/data-provider/graphql", "guides-and-concepts/data-provider/strapi-v4", "guides-and-concepts/data-provider/appwrite", From 8815276193db95065153b36c7b3061601001ea9a Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 7 Mar 2022 17:02:34 +0300 Subject: [PATCH 26/27] add handle in a data provider section --- .../data-provider/handling-filters.md | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/documentation/docs/guides-and-concepts/data-provider/handling-filters.md b/documentation/docs/guides-and-concepts/data-provider/handling-filters.md index cd715e0f7e13..38894fe4d97a 100644 --- a/documentation/docs/guides-and-concepts/data-provider/handling-filters.md +++ b/documentation/docs/guides-and-concepts/data-provider/handling-filters.md @@ -123,14 +123,36 @@ filter = [ Here the query will look like: `"status" == "published" AND ("createdAt" == "2022-01-01" OR "createdAt" == "2022-01-02")` -### Handle Filters in a Data Provider +## Handle filters in a data provider ```tsx title="dataProvider.ts" import { DataProvider } from "@pankod/refine-core"; const dataProvider = (): DataProvider => ({ getList: async ({ resource, pagination, filters, sort }) => { - + if (filters) { + filters.map((filter) => { + if (filter.operator !== "or") { + // Handle your logical filters here + // console.log(typeof filter); // LogicalFilter + } else { + // Handle your conditional filters here + // console.log(typeof filter); // ConditionalFilter + } + }); + } }, -} +}); ``` + +:::tip +Data providers that support `or` and `and` filtering logic are as follows: + +- [NestJS CRUD](https://github.com/pankod/refine/tree/master/packages/nestjsx-crud) +- [Strapi](https://github.com/pankod/refine/tree/master/packages/strapi) - [Strapi v4](https://github.com/pankod/refine/tree/master/packages/strapi-v4) +- [Strapi GraphQL](https://github.com/pankod/refine/tree/master/packages/strapi-graphql) +- [Supabase](https://github.com/pankod/refine/tree/master/packages/supabase) +- [Hasura](https://github.com/pankod/refine/tree/master/packages/hasura) +- [Nhost](https://github.com/pankod/refine/tree/master/packages/nhost) + +::: From d90406a5de0080691dc9ffb482a6f160b231841c Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 7 Mar 2022 22:40:48 +0300 Subject: [PATCH 27/27] fix conditional filter example --- .../data-provider/handling-filters.md | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/documentation/docs/guides-and-concepts/data-provider/handling-filters.md b/documentation/docs/guides-and-concepts/data-provider/handling-filters.md index 38894fe4d97a..45013dd84261 100644 --- a/documentation/docs/guides-and-concepts/data-provider/handling-filters.md +++ b/documentation/docs/guides-and-concepts/data-provider/handling-filters.md @@ -75,14 +75,19 @@ Here the query will look like: `"name" = "John" AND "age" < 30` ```ts const filter = [ { - field: "age", - operator: "eq", - value: 30, - }, - { - field: "createdAt", - operator: "gte", - value: "2018-01-01", + operator: "or", + value: [ + { + field: "age", + operator: "eq", + value: 30, + }, + { + field: "createdAt", + operator: "gte", + value: "2018-01-01", + }, + ], }, ]; ```