diff --git a/.changeset/few-turkeys-dance.md b/.changeset/few-turkeys-dance.md
new file mode 100644
index 000000000000..9513f467e7db
--- /dev/null
+++ b/.changeset/few-turkeys-dance.md
@@ -0,0 +1,9 @@
+---
+"@refinedev/supabase": patch
+---
+
+fix(supabase): issue with parsed values when using conditional filters
+
+Fixed conditional filter's parsed values while using `contains`, `containss`, `startswith` and `endswith`.
+
+[Fixes #6239](https://github.com/refinedev/refine/issues/6239)
diff --git a/.changeset/friendly-panthers-beam.md b/.changeset/friendly-panthers-beam.md
new file mode 100644
index 000000000000..cd3f9addd88f
--- /dev/null
+++ b/.changeset/friendly-panthers-beam.md
@@ -0,0 +1,9 @@
+---
+"@refinedev/antd": patch
+---
+
+fix(antd): rtl support for mobile sider trigger and drawer placement
+
+`` has RTL support but it lacks the mobile sider trigger and drawer placement. This change places the drawer depending on the preferred direction. It also adds RTL support for the styling of the mobile sider trigger.
+
+[Fixes #6263](https://github.com/refinedev/refine/issues/6263)
diff --git a/.changeset/grumpy-lions-exercise.md b/.changeset/grumpy-lions-exercise.md
new file mode 100644
index 000000000000..fb1c8ec92222
--- /dev/null
+++ b/.changeset/grumpy-lions-exercise.md
@@ -0,0 +1,8 @@
+---
+"@refinedev/devtools-server": patch
+---
+
+chore(devtools-server): replace `preferred-pm` with `package-manager-detector` #6242
+
+`preferred-pm` has 24 dependencies: https://npmgraph.js.org/?q=preferred-pm
+`package-manager-detector` has no dependencies: https://npmgraph.js.org/?q=package-manager-detector
diff --git a/.changeset/honest-peaches-deny.md b/.changeset/honest-peaches-deny.md
new file mode 100644
index 000000000000..5bffda8fce93
--- /dev/null
+++ b/.changeset/honest-peaches-deny.md
@@ -0,0 +1,9 @@
+---
+"@refinedev/core": patch
+---
+
+fix(core): `useResourceParams` not reflecting `id` prop changes immediately
+
+`useResourceParams` hook was not reflecting the changes in the `id` prop immediately. This was due to the `id` state being set in the `useEffect` hook. This PR fixes the issue by setting the `id` state properly during render rather than after the render is complete.
+
+[Fixes #6259](https://github.com/refinedev/refine/issues/6259)
diff --git a/.changeset/nasty-glasses-care.md b/.changeset/nasty-glasses-care.md
new file mode 100644
index 000000000000..9b711b6c2e87
--- /dev/null
+++ b/.changeset/nasty-glasses-care.md
@@ -0,0 +1,9 @@
+---
+"@refinedev/mui": minor
+---
+
+feat(mui): added loading spinner to ``, `` and `` components
+
+This change introduces a loading spinner to the ``, `` and `` components in the `@refinedev/mui` package. The spinner provides a visual indication that data is being loaded, improving the user experience bym giving clear feedback during loading states.
+
+[Resolves #5668](https://github.com/refinedev/refine/issues/5668)
diff --git a/.changeset/six-shrimps-hide.md b/.changeset/six-shrimps-hide.md
new file mode 100644
index 000000000000..5da638fc5a38
--- /dev/null
+++ b/.changeset/six-shrimps-hide.md
@@ -0,0 +1,8 @@
+---
+"@refinedev/hasura": patch
+"@refinedev/core": patch
+---
+
+feat: added support for meta.gqlVariables to hasura dataProvider. Updated GraphQLQueryOptions to include optional field gqlVariables
+
+[Feat #5864](https://github.com/refinedev/refine/issues/5864)
diff --git a/.changeset/wicked-cherries-grow.md b/.changeset/wicked-cherries-grow.md
new file mode 100644
index 000000000000..3a3bf44054df
--- /dev/null
+++ b/.changeset/wicked-cherries-grow.md
@@ -0,0 +1,11 @@
+---
+"@refinedev/cli": patch
+---
+
+feat: added scripts for Remix SPA Mode
+
+It is now possible to execute the Remix SPA Mode script by selecting it from the platform options.
+
+Two new project types are added `remix-vite` and `remix-spa`. `remix-vite` is Remix + Vite and `remix-spa` is Remix + Vite SPA Mode. While `remix-vite` type can be inferred from the project configuration without needing to specify it in the command, `remix-spa` type needs to be specified explicitly.
+
+[Resolves #6127](https://github.com/refinedev/refine/issues/6127)
diff --git a/.changeset/wicked-rats-breathe.md b/.changeset/wicked-rats-breathe.md
new file mode 100644
index 000000000000..35897e7bf471
--- /dev/null
+++ b/.changeset/wicked-rats-breathe.md
@@ -0,0 +1,9 @@
+---
+"@refinedev/appwrite": patch
+---
+
+feat(appwrite): add support to conditional filters and missing logical filters
+
+Add Support to `and`, `or`, `between`, `null`, `nnull`, `startswith` and `endswith` operators
+
+[Resolves #6252](https://github.com/refinedev/refine/issues/6252)
diff --git a/documentation/docs/advanced-tutorials/data-provider/handling-filters.md b/documentation/docs/advanced-tutorials/data-provider/handling-filters.md
index 95cd67e312c4..7757baaa9f76 100644
--- a/documentation/docs/advanced-tutorials/data-provider/handling-filters.md
+++ b/documentation/docs/advanced-tutorials/data-provider/handling-filters.md
@@ -275,3 +275,92 @@ Data providers that support `or` and `and` filtering logic are as follows:
- [Hasura](https://github.com/refinedev/refine/tree/master/packages/hasura)
:::
+
+## Handle Custom GraphQL Variables
+
+The [GraphQLQueryOptions](https://refine.dev/docs/core/interface-references/#graphqlqueryoptions) property `gqlVariables` enables dynamic GraphQL variables to be passed to the data provider for more advanced GraphQL queries.
+
+Packages that support custom GraphQL variables for more advanced filtering are as follows:
+
+- [Hasura](https://github.com/refinedev/refine/tree/master/packages/hasura)
+
+The following data providers do not yet support `meta.gqlVariables`;
+
+- [Nestjs-Query](https://github.com/refinedev/refine/tree/master/packages/nestjs-query)
+- [GraphQL](https://github.com/refinedev/refine/tree/master/packages/graphql)
+
+```tsx
+// Hasura Data Provider Example
+import gql from "graphql-tag";
+import { useTable } from "@refinedev/antd";
+import type { GetFieldsFromList } from "@refinedev/hasura";
+import type { GetPostsQuery } from "graphql/types";
+
+const POSTS_QUERY = gql`
+ query GetPosts(
+ $offset: Int!
+ $limit: Int!
+ $order_by: [posts_order_by!]
+ $where: posts_bool_exp
+ ) {
+ posts(
+ offset: $offset
+ limit: $limit
+ order_by: $order_by
+ where: $where
+ ) {
+ id
+ title
+ content
+ category_id
+ created_at
+ category {
+ id
+ title
+ }
+ }
+ posts_aggregate(where: $where) {
+ aggregate {
+ count
+ }
+ }
+ }
+`;
+
+export const PostList = () => {
+ const { tableProps } = useTable<
+ GetFieldsFromList
+ >({
+ meta: {
+ gqlQuery: POSTS_QUERY,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ _not: {
+ category: { title: { _eq: "ok" } },
+ },
+ },
+ {
+ title: {
+ _ilike: "%Updated%",
+ },
+ },
+ {
+ title: {
+ _ilike: "%3%",
+ },
+ },
+ {
+ created_at: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ },
+ },
+ });
+ return ( );
+}
+
+```
diff --git a/documentation/docs/core/interface-references/index.md b/documentation/docs/core/interface-references/index.md
index eaa3c81a9e93..8fc13f67fdba 100644
--- a/documentation/docs/core/interface-references/index.md
+++ b/documentation/docs/core/interface-references/index.md
@@ -207,6 +207,9 @@ import type { DocumentNode } from "graphql";
type GraphQLQueryOptions = {
gqlQuery?: DocumentNode;
gqlMutation?: DocumentNode;
+ gqlVariables?: {
+ [key: string]: any;
+ };
};
```
diff --git a/packages/antd/src/components/themedLayoutV2/sider/index.tsx b/packages/antd/src/components/themedLayoutV2/sider/index.tsx
index 87da0c55d696..df9b9d457bd2 100644
--- a/packages/antd/src/components/themedLayoutV2/sider/index.tsx
+++ b/packages/antd/src/components/themedLayoutV2/sider/index.tsx
@@ -232,7 +232,7 @@ export const ThemedSiderV2: React.FC = ({
setMobileSiderOpen(false)}
- placement="left"
+ placement={direction === "rtl" ? "right" : "left"}
closable={false}
width={200}
bodyStyle={{
diff --git a/packages/antd/src/components/themedLayoutV2/sider/styles.ts b/packages/antd/src/components/themedLayoutV2/sider/styles.ts
index 174852a9f086..33adcaa4c86b 100644
--- a/packages/antd/src/components/themedLayoutV2/sider/styles.ts
+++ b/packages/antd/src/components/themedLayoutV2/sider/styles.ts
@@ -1,8 +1,8 @@
import type { CSSProperties } from "react";
export const drawerButtonStyles: CSSProperties = {
- borderTopLeftRadius: 0,
- borderBottomLeftRadius: 0,
+ borderStartStartRadius: 0,
+ borderEndStartRadius: 0,
position: "fixed",
top: 64,
zIndex: 999,
diff --git a/packages/appwrite/src/utils/generateFilter.ts b/packages/appwrite/src/utils/generateFilter.ts
index f0d34dc68ed1..c2f16ca55c20 100644
--- a/packages/appwrite/src/utils/generateFilter.ts
+++ b/packages/appwrite/src/utils/generateFilter.ts
@@ -1,8 +1,24 @@
import type { CrudFilter } from "@refinedev/core";
import { Query } from "appwrite";
+import { replaceIdWithAppwriteId } from "./replaceIdWithAppwriteId";
+
+/**
+ * Generate a filter string for Appwrite from Refine's filter
+ * @param filter Refine's filter
+ * @param deep Max deep of the filter
+ * @returns Appwrite's filter string
+ */
+export const generateFilter = (filter: CrudFilter, deep = 10): string => {
+ const nextDeep = deep - 1;
+
+ if (nextDeep < 0) {
+ throw new Error("Max deep reached");
+ }
+
+ filter = replaceIdWithAppwriteId(filter);
-export const generateFilter = (filter: CrudFilter) => {
switch (filter.operator) {
+ // Logical operators
case "eq":
return Query.equal(filter.field, filter.value);
case "ne":
@@ -17,6 +33,36 @@ export const generateFilter = (filter: CrudFilter) => {
return Query.lessThanEqual(filter.field, filter.value);
case "contains":
return Query.search(filter.field, `%${filter.value}%`);
+ case "between":
+ if (!Array.isArray(filter.value) || filter.value.length !== 2) {
+ throw new Error(
+ `Value array must contain exactly two elements for "between" operator`,
+ );
+ }
+ return Query.between(filter.field, filter.value[0], filter.value[1]);
+ case "null":
+ return Query.isNull(filter.field);
+ case "nnull":
+ return Query.isNotNull(filter.field);
+ case "startswith":
+ return Query.startsWith(filter.field, filter.value);
+ case "endswith":
+ return Query.endsWith(filter.field, filter.value);
+
+ // Conditional operators
+ case "or":
+ if (filter.value.length === 1 && filter.value[0]) {
+ //? "OR" queries require at least two queries in Appwrite
+ return generateFilter(filter.value[0], nextDeep);
+ }
+ return Query.or(filter.value.map((f) => generateFilter(f, nextDeep)));
+ case "and":
+ if (filter.value.length === 1 && filter.value[0]) {
+ //? "AND" queries require at least two queries in Appwrite
+ return generateFilter(filter.value[0], nextDeep);
+ }
+ return Query.and(filter.value.map((f) => generateFilter(f, nextDeep)));
+
default:
throw new Error(`Operator ${filter.operator} is not supported`);
}
diff --git a/packages/appwrite/src/utils/getAppwriteFilters.ts b/packages/appwrite/src/utils/getAppwriteFilters.ts
index 9f1bda83e200..d6eb6bf73035 100644
--- a/packages/appwrite/src/utils/getAppwriteFilters.ts
+++ b/packages/appwrite/src/utils/getAppwriteFilters.ts
@@ -7,20 +7,7 @@ export const getAppwriteFilters: GetAppwriteFiltersType = (filters) => {
const appwriteFilters: string[] = [];
for (const filter of filters ?? []) {
- if (
- filter.operator !== "or" &&
- filter.operator !== "and" &&
- "field" in filter
- ) {
- const filterField = filter.field === "id" ? "$id" : filter.field;
-
- appwriteFilters.push(
- generateFilter({
- ...filter,
- field: filterField,
- }),
- );
- }
+ appwriteFilters.push(generateFilter(filter));
}
return appwriteFilters;
diff --git a/packages/appwrite/src/utils/replaceIdWithAppwriteId.ts b/packages/appwrite/src/utils/replaceIdWithAppwriteId.ts
new file mode 100644
index 000000000000..877a8fc59bdb
--- /dev/null
+++ b/packages/appwrite/src/utils/replaceIdWithAppwriteId.ts
@@ -0,0 +1,17 @@
+import type { CrudFilter } from "@refinedev/core";
+
+/**
+ * Replace ID("id") With Appwrite ID("$id")
+ * @param filter Filter to replace
+ * @returns Filter with replaced ID
+ */
+export const replaceIdWithAppwriteId = (filter: CrudFilter): CrudFilter => {
+ if ("field" in filter && filter.field === "id") {
+ filter.field = "$id";
+ }
+
+ return {
+ ...filter,
+ value: filter.value,
+ };
+};
diff --git a/packages/appwrite/test/utils/generateFilter.spec.ts b/packages/appwrite/test/utils/generateFilter.spec.ts
index 9de437849d15..bd3f46da3441 100644
--- a/packages/appwrite/test/utils/generateFilter.spec.ts
+++ b/packages/appwrite/test/utils/generateFilter.spec.ts
@@ -33,6 +33,134 @@ describe("generateFilter", () => {
filter: { operator: "contains", field: "name", value: "John" },
expected: Query.search("name", "%John%"),
},
+ {
+ filter: { operator: "between", field: "age", value: [0, 64] },
+ expected: Query.between("age", 0, 64),
+ },
+ {
+ filter: { operator: "null", field: "name", value: undefined },
+ expected: Query.isNull("name"),
+ },
+ {
+ filter: { operator: "nnull", field: "name", value: undefined },
+ expected: Query.isNotNull("name"),
+ },
+ {
+ filter: { operator: "startswith", field: "name", value: "John" },
+ expected: Query.startsWith("name", "John"),
+ },
+ {
+ filter: { operator: "endswith", field: "name", value: "John" },
+ expected: Query.endsWith("name", "John"),
+ },
+ ];
+
+ testCases.forEach(({ filter, expected }) => {
+ const result = generateFilter(filter);
+ expect(result).toEqual(expected);
+ });
+ });
+
+ it("should correctly handle 'or' and 'and' operators with only one element", () => {
+ const testCases: { filter: CrudFilter; expected: any }[] = [
+ {
+ filter: {
+ operator: "or",
+ value: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ ],
+ },
+ expected: Query.equal("name", "John"),
+ },
+ {
+ filter: {
+ operator: "and",
+ value: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ ],
+ },
+ expected: Query.equal("name", "John"),
+ },
+ ];
+
+ testCases.forEach(({ filter, expected }) => {
+ const result = generateFilter(filter);
+ expect(result).toEqual(expected);
+ });
+ });
+
+ it("should correctly handle nested 'or' and 'and' operators", () => {
+ const testCases: { filter: CrudFilter; expected: any }[] = [
+ {
+ filter: {
+ operator: "or",
+ value: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ {
+ operator: "or",
+ value: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "Tom",
+ },
+ {
+ operator: "lt",
+ field: "age",
+ value: 30,
+ },
+ ],
+ },
+ ],
+ },
+ expected: Query.or([
+ Query.equal("name", "John"),
+ Query.or([Query.equal("name", "Tom"), Query.lessThan("age", 30)]),
+ ]),
+ },
+ {
+ filter: {
+ operator: "and",
+ value: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ {
+ operator: "and",
+ value: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "Tom",
+ },
+ {
+ operator: "lt",
+ field: "age",
+ value: 30,
+ },
+ ],
+ },
+ ],
+ },
+ expected: Query.and([
+ Query.equal("name", "John"),
+ Query.and([Query.equal("name", "Tom"), Query.lessThan("age", 30)]),
+ ]),
+ },
];
testCases.forEach(({ filter, expected }) => {
@@ -41,6 +169,31 @@ describe("generateFilter", () => {
});
});
+ it("should throw an error when value array has only one element for 'between' operator", () => {
+ const filter = {
+ operator: "between",
+ field: "age",
+ value: [0],
+ } as CrudFilter;
+
+ expect(() => generateFilter(filter)).toThrowError(
+ 'Value array must contain exactly two elements for "between" operator',
+ );
+ });
+
+ it("should replace 'id' field with '$id'", () => {
+ const filter = {
+ operator: "eq",
+ field: "id",
+ value: "123",
+ } as CrudFilter;
+ const expected = Query.equal("$id", "123");
+
+ const result = generateFilter(filter);
+
+ expect(result).toEqual(expected);
+ });
+
it("should throw an error for unsupported operator", () => {
const filter = {
operator: "unsupported",
@@ -51,4 +204,14 @@ describe("generateFilter", () => {
`Operator ${filter.operator} is not supported`,
);
});
+
+ it("should throw an error when max deep is reached", () => {
+ const filter = {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ } as CrudFilter;
+
+ expect(() => generateFilter(filter, 0)).toThrowError("Max deep reached");
+ });
});
diff --git a/packages/appwrite/test/utils/getAppwriteFilters.spec.ts b/packages/appwrite/test/utils/getAppwriteFilters.spec.ts
index dcabc8d3d649..1d4c8b06919f 100644
--- a/packages/appwrite/test/utils/getAppwriteFilters.spec.ts
+++ b/packages/appwrite/test/utils/getAppwriteFilters.spec.ts
@@ -31,29 +31,72 @@ describe("getAppwriteFilters", () => {
});
});
- it('should not generate Appwrite filters for "or" and "and" operators', () => {
+ it('should generate Appwrite filters for "or" and "and" operators', () => {
const filters = [
- { operator: "or", filters: [] },
- { operator: "and", filters: [] },
+ {
+ operator: "or",
+ filters: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ {
+ operator: "lt",
+ field: "age",
+ value: 30,
+ },
+ ],
+ },
+ {
+ operator: "and",
+ filters: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ {
+ operator: "lt",
+ field: "age",
+ value: 30,
+ },
+ ],
+ },
];
getAppwriteFilters(filters as any);
- expect(generateFilter).toHaveBeenCalledTimes(0);
- });
-
- it('should replace "id" field with "$id"', () => {
- const filters: CrudFilter[] = [
- { operator: "eq", field: "id", value: "123" },
- ];
-
- getAppwriteFilters(filters);
-
- expect(generateFilter).toHaveBeenCalledTimes(1);
- expect(generateFilter).toHaveBeenCalledWith({
- operator: "eq",
- field: "$id",
- value: "123",
+ expect(generateFilter).toHaveBeenCalledTimes(2);
+ expect(generateFilter).toHaveBeenNthCalledWith(1, {
+ operator: "or",
+ filters: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ {
+ operator: "lt",
+ field: "age",
+ value: 30,
+ },
+ ],
+ });
+ expect(generateFilter).toHaveBeenNthCalledWith(2, {
+ operator: "and",
+ filters: [
+ {
+ operator: "eq",
+ field: "name",
+ value: "John",
+ },
+ {
+ operator: "lt",
+ field: "age",
+ value: 30,
+ },
+ ],
});
});
diff --git a/packages/appwrite/test/utils/getAppwriteSorting.ts b/packages/appwrite/test/utils/getAppwriteSorting.spec.ts
similarity index 100%
rename from packages/appwrite/test/utils/getAppwriteSorting.ts
rename to packages/appwrite/test/utils/getAppwriteSorting.spec.ts
diff --git a/packages/appwrite/test/utils/replaceIdWithAppwriteId.spec.ts b/packages/appwrite/test/utils/replaceIdWithAppwriteId.spec.ts
new file mode 100644
index 000000000000..6179e216014d
--- /dev/null
+++ b/packages/appwrite/test/utils/replaceIdWithAppwriteId.spec.ts
@@ -0,0 +1,55 @@
+import type { CrudFilter } from "@refinedev/core";
+import { replaceIdWithAppwriteId } from "../../src/utils/replaceIdWithAppwriteId";
+
+describe("replaceIdWithAppwriteId", () => {
+ it("should replace the id with appwrite id", () => {
+ const result = replaceIdWithAppwriteId({
+ field: "id",
+ operator: "eq",
+ value: "John Doe",
+ } satisfies CrudFilter);
+ expect(result).toStrictEqual({
+ field: "$id",
+ operator: "eq",
+ value: "John Doe",
+ });
+ });
+
+ it("should only replace the first layer of id with appwrite id", () => {
+ const result = replaceIdWithAppwriteId({
+ field: "id",
+ operator: "eq",
+ value: [
+ {
+ field: "id",
+ operator: "eq",
+ value: "John Doe",
+ },
+ ],
+ } satisfies CrudFilter);
+ expect(result).toStrictEqual({
+ field: "$id",
+ operator: "eq",
+ value: [
+ {
+ field: "id",
+ operator: "eq",
+ value: "John Doe",
+ },
+ ],
+ });
+ });
+
+ it("should not replace the other field value with appwrite id", () => {
+ const result = replaceIdWithAppwriteId({
+ field: "name",
+ operator: "eq",
+ value: "John Doe",
+ } satisfies CrudFilter);
+ expect(result).toStrictEqual({
+ field: "name",
+ operator: "eq",
+ value: "John Doe",
+ });
+ });
+});
diff --git a/packages/cli/src/commands/add/sub-commands/integration/packages/ant-design.ts b/packages/cli/src/commands/add/sub-commands/integration/packages/ant-design.ts
index 2f394d50681b..a6b6ce651f94 100644
--- a/packages/cli/src/commands/add/sub-commands/integration/packages/ant-design.ts
+++ b/packages/cli/src/commands/add/sub-commands/integration/packages/ant-design.ts
@@ -17,7 +17,14 @@ export const AntDesignIntegration: Integration = {
const description = "Setup Ant Design with Refine";
let disabled;
- if ([ProjectTypes.NEXTJS, ProjectTypes.REMIX].includes(projectType)) {
+ if (
+ [
+ ProjectTypes.NEXTJS,
+ ProjectTypes.REMIX,
+ ProjectTypes.REMIX_VITE,
+ ProjectTypes.REMIX_SPA,
+ ].includes(projectType)
+ ) {
disabled =
"Automatic setup only available Vite for now. See the documentation for manual installation: https://refine.dev/docs/ui-integrations/ant-design/introduction/#installation";
}
diff --git a/packages/cli/src/commands/add/sub-commands/integration/packages/react-router.ts b/packages/cli/src/commands/add/sub-commands/integration/packages/react-router.ts
index 133022c33b0c..ff1769130b39 100644
--- a/packages/cli/src/commands/add/sub-commands/integration/packages/react-router.ts
+++ b/packages/cli/src/commands/add/sub-commands/integration/packages/react-router.ts
@@ -21,7 +21,11 @@ export const ReactRouterIntegration: Integration = {
disabled = `Can't be used with Next.js. https://nextjs.org/docs/app/building-your-application/routing`;
}
- if (projectType === ProjectTypes.REMIX) {
+ if (
+ projectType === ProjectTypes.REMIX ||
+ projectType === ProjectTypes.REMIX_VITE ||
+ projectType === ProjectTypes.REMIX_SPA
+ ) {
disabled = `Can't be used with Remix. https://remix.run/docs/en/main/discussion/routes`;
}
diff --git a/packages/cli/src/commands/runner/projectScripts.test.ts b/packages/cli/src/commands/runner/projectScripts.test.ts
index 7cb8b190d32c..f93de7065bf1 100644
--- a/packages/cli/src/commands/runner/projectScripts.test.ts
+++ b/packages/cli/src/commands/runner/projectScripts.test.ts
@@ -53,17 +53,17 @@ describe("REACT_SCRIPT project type", () => {
});
});
-describe("Vite project type", () => {
+describe("VITE project type", () => {
const projectType = ProjectTypes.VITE;
describe("getDev with empty args", () => {
- test('should return array with only "start" if args is empty', () => {
+ test('should return array with only "dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["dev"]);
});
});
describe("getStart with empty args", () => {
- test('should return array with only "start" if args is empty', () => {
+ test('should return array with only "preview" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["preview"]);
});
});
@@ -75,7 +75,7 @@ describe("Vite project type", () => {
});
describe("getDev", () => {
- test('should prepend "start" to the args array', () => {
+ test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
@@ -85,7 +85,7 @@ describe("Vite project type", () => {
});
describe("getStart", () => {
- test('should prepend "start" to the args array', () => {
+ test('should prepend "preview" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"preview",
@@ -105,11 +105,11 @@ describe("Vite project type", () => {
});
});
-describe("Next.js project type", () => {
+describe("NEXTJS project type", () => {
const projectType = ProjectTypes.NEXTJS;
describe("getDev with empty args", () => {
- test('should return array with only "start" if args is empty', () => {
+ test('should return array with only "dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["dev"]);
});
});
@@ -127,7 +127,7 @@ describe("Next.js project type", () => {
});
describe("getDev", () => {
- test('should prepend "start" to the args array', () => {
+ test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
@@ -157,11 +157,11 @@ describe("Next.js project type", () => {
});
});
-describe("Remix project type", () => {
+describe("REMIX project type", () => {
const projectType = ProjectTypes.REMIX;
describe("getDev with empty args", () => {
- test('should return array with only "start" if args is empty', () => {
+ test('should return array with only "dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["dev"]);
});
});
@@ -183,7 +183,7 @@ describe("Remix project type", () => {
});
describe("getDev", () => {
- test('should prepend "start" to the args array', () => {
+ test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
@@ -210,6 +210,111 @@ describe("Remix project type", () => {
});
});
+describe("REMIX_VITE project type", () => {
+ const projectType = ProjectTypes.REMIX_VITE;
+
+ describe("getDev with empty args", () => {
+ test('should return array with only "vite:dev" if args is empty', () => {
+ expect(projectScripts[projectType].getDev([])).toEqual(["vite:dev"]);
+ });
+ });
+
+ describe("getStart with empty args", () => {
+ test("should return default", () => {
+ const logSpy = jest.spyOn(console, "warn");
+ expect(projectScripts[projectType].getStart([])).toEqual([
+ "./build/server/index.js",
+ ]);
+ expect(logSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe("getBuild with empty args", () => {
+ test('should return array with only "vite:build" if args is empty', () => {
+ expect(projectScripts[projectType].getBuild([])).toEqual(["vite:build"]);
+ });
+ });
+
+ describe("getDev", () => {
+ test('should prepend "vite:dev" to the args array', () => {
+ const args = ["--arg1", "--arg2"];
+ expect(projectScripts[projectType].getDev(args)).toEqual([
+ "vite:dev",
+ ...args,
+ ]);
+ });
+ });
+
+ describe("getStart", () => {
+ test("should return args", () => {
+ const args = ["--arg1", "--arg2"];
+ expect(projectScripts[projectType].getStart(args)).toEqual([...args]);
+ });
+ });
+
+ describe("getBuild", () => {
+ test('should prepend "vite:build" to the args array', () => {
+ const args = ["--arg1", "--arg2"];
+ expect(projectScripts[projectType].getBuild(args)).toEqual([
+ "vite:build",
+ ...args,
+ ]);
+ });
+ });
+});
+
+describe("REMIX_SPA project type", () => {
+ const projectType = ProjectTypes.REMIX_SPA;
+
+ describe("getDev with empty args", () => {
+ test('should return array with only "vite:dev" if args is empty', () => {
+ expect(projectScripts[projectType].getDev([])).toEqual(["vite:dev"]);
+ });
+ });
+
+ describe("getStart with empty args", () => {
+ test('should return array with only "preview" if args is empty', () => {
+ expect(projectScripts[projectType].getStart([])).toEqual(["preview"]);
+ });
+ });
+
+ describe("getBuild with empty args", () => {
+ test('should return array with only "vite:build" if args is empty', () => {
+ expect(projectScripts[projectType].getBuild([])).toEqual(["vite:build"]);
+ });
+ });
+
+ describe("getDev", () => {
+ test('should prepend "vite:dev" to the args array', () => {
+ const args = ["--arg1", "--arg2"];
+ expect(projectScripts[projectType].getDev(args)).toEqual([
+ "vite:dev",
+ ...args,
+ ]);
+ });
+ });
+
+ describe("getStart", () => {
+ test('should prepend "preview" to the args array', () => {
+ const args = ["--arg1", "--arg2"];
+ expect(projectScripts[projectType].getStart(args)).toEqual([
+ "preview",
+ ...args,
+ ]);
+ });
+ });
+
+ describe("getBuild", () => {
+ test('should prepend "vite:build" to the args array', () => {
+ const args = ["--arg1", "--arg2"];
+ expect(projectScripts[projectType].getBuild(args)).toEqual([
+ "vite:build",
+ ...args,
+ ]);
+ });
+ });
+});
+
describe("CRACO project type", () => {
const projectType = ProjectTypes.CRACO;
@@ -318,39 +423,39 @@ describe("UNKNOWN project type", () => {
const projectType = ProjectTypes.UNKNOWN;
describe("getDev with empty args", () => {
- test('should return array with only "start" if args is empty', () => {
+ test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getDev([])).toEqual([]);
});
});
describe("getStart with empty args", () => {
- test('should return array with only "start" if args is empty', () => {
+ test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getStart([])).toEqual([]);
});
});
describe("getBuild with empty args", () => {
- test('should return array with only "build" if args is empty', () => {
+ test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getBuild([])).toEqual([]);
});
});
describe("getDev", () => {
- test('should prepend "start" to the args array', () => {
+ test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([...args]);
});
});
describe("getStart", () => {
- test('should prepend "start" to the args array', () => {
+ test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([...args]);
});
});
describe("getBuild", () => {
- test('should prepend "build" to the args array', () => {
+ test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([...args]);
});
diff --git a/packages/cli/src/commands/runner/projectScripts.ts b/packages/cli/src/commands/runner/projectScripts.ts
index b3e9547e509c..47d384be79b6 100644
--- a/packages/cli/src/commands/runner/projectScripts.ts
+++ b/packages/cli/src/commands/runner/projectScripts.ts
@@ -50,6 +50,43 @@ export const projectScripts = {
return require.resolve(`.bin/${binName}`);
},
},
+ [ProjectTypes.REMIX_VITE]: {
+ getDev: (args: string[]) => ["vite:dev", ...args],
+ getStart: (args: string[]) => {
+ // remix-serve accepts a path to the entry file as an argument
+ // if we have arguments, we will pass them to remix-serve and do nothing.
+ // ex: `refine start ./build/server/index.js`
+ const hasArgs = args?.length;
+ if (hasArgs) {
+ return args;
+ }
+
+ // otherwise print a warning and use `./build/server/index.js` as default
+ console.log();
+ console.warn(
+ "🚨 Remix requires a path to the entry file. Please provide it as an argument to `refine start` command in package.json scripts",
+ );
+ console.warn("Refine will use `./build/server/index.js` as default");
+ console.warn("Example: `refine start ./build/server/index.js`");
+ console.log();
+
+ return ["./build/server/index.js"];
+ },
+ getBuild: (args: string[]) => ["vite:build", ...args],
+ getBin: (type?: "dev" | "start" | "build") => {
+ const binName = type === "start" ? "remix-serve" : "remix";
+ return require.resolve(`.bin/${binName}`);
+ },
+ },
+ [ProjectTypes.REMIX_SPA]: {
+ getDev: (args: string[]) => ["vite:dev", ...args],
+ getStart: (args: string[]) => ["preview", ...args],
+ getBuild: (args: string[]) => ["vite:build", ...args],
+ getBin: (type?: "dev" | "start" | "build") => {
+ const binName = type === "start" ? "vite" : "remix";
+ return require.resolve(`.bin/${binName}`);
+ },
+ },
[ProjectTypes.CRACO]: {
getDev: (args: string[]) => ["start", ...args],
getStart: (args: string[]) => ["start", ...args],
diff --git a/packages/cli/src/commands/runner/utils/index.ts b/packages/cli/src/commands/runner/utils/index.ts
index 543eeab0d8e9..59b0deeed82b 100644
--- a/packages/cli/src/commands/runner/utils/index.ts
+++ b/packages/cli/src/commands/runner/utils/index.ts
@@ -18,8 +18,16 @@ export const getRunnerDescription = (runner: "dev" | "start" | "build") => {
break;
}
- if (projectType === ProjectTypes.REMIX && runner === "start") {
- projectType = "remix-serve" as ProjectTypes;
+ if (runner === "start") {
+ switch (projectType) {
+ case ProjectTypes.REMIX:
+ case ProjectTypes.REMIX_VITE:
+ projectType = "remix-serve" as ProjectTypes;
+ break;
+ case ProjectTypes.REMIX_SPA:
+ projectType = ProjectTypes.VITE;
+ break;
+ }
}
return `It runs: \`${projectType} ${command.join(
diff --git a/packages/cli/src/definitions/projectTypes.ts b/packages/cli/src/definitions/projectTypes.ts
index 1899217e26c8..ec3b6f5cf91a 100644
--- a/packages/cli/src/definitions/projectTypes.ts
+++ b/packages/cli/src/definitions/projectTypes.ts
@@ -1,6 +1,8 @@
export enum ProjectTypes {
REACT_SCRIPT = "react-scripts",
REMIX = "remix",
+ REMIX_VITE = "remix-vite",
+ REMIX_SPA = "remix-spa",
NEXTJS = "nextjs",
VITE = "vite",
CRACO = "craco",
diff --git a/packages/cli/src/utils/project/index.ts b/packages/cli/src/utils/project/index.ts
index 5536218659ff..d03608dac80b 100644
--- a/packages/cli/src/utils/project/index.ts
+++ b/packages/cli/src/utils/project/index.ts
@@ -36,6 +36,11 @@ export const getProjectType = (platform?: ProjectTypes): ProjectTypes => {
dependencies.includes("@remix-run/react") ||
devDependencies.includes("@remix-run/react")
) {
+ // check for remix-vite
+ if (dependencies.includes("vite") || devDependencies.includes("vite")) {
+ return ProjectTypes.REMIX_VITE;
+ }
+
return ProjectTypes.REMIX;
}
diff --git a/packages/cli/src/utils/resource/index.spec.ts b/packages/cli/src/utils/resource/index.spec.ts
index 1b4c0c8130aa..f92cdb0c27a5 100644
--- a/packages/cli/src/utils/resource/index.spec.ts
+++ b/packages/cli/src/utils/resource/index.spec.ts
@@ -12,6 +12,16 @@ it("should get provider path", () => {
alias: "~/providers",
});
+ expect(getProviderPath(ProjectTypes.REMIX_VITE)).toEqual({
+ path: "app/providers",
+ alias: "~/providers",
+ });
+
+ expect(getProviderPath(ProjectTypes.REMIX_SPA)).toEqual({
+ path: "app/providers",
+ alias: "~/providers",
+ });
+
expect(getProviderPath(ProjectTypes.VITE)).toEqual({
path: "src/providers",
alias: "providers",
diff --git a/packages/cli/src/utils/resource/index.ts b/packages/cli/src/utils/resource/index.ts
index 0fe9f4354c84..091165e87ed4 100644
--- a/packages/cli/src/utils/resource/index.ts
+++ b/packages/cli/src/utils/resource/index.ts
@@ -11,6 +11,8 @@ export const getResourcePath = (
alias: "../src/components",
};
case ProjectTypes.REMIX:
+ case ProjectTypes.REMIX_VITE:
+ case ProjectTypes.REMIX_SPA:
return {
path: "app/components",
alias: "~/components",
@@ -34,6 +36,8 @@ export const getProviderPath = (
alias: "../src/providers",
};
case ProjectTypes.REMIX:
+ case ProjectTypes.REMIX_VITE:
+ case ProjectTypes.REMIX_SPA:
return {
path: "app/providers",
alias: "~/providers",
@@ -50,6 +54,8 @@ export const getProviderPath = (
export const getFilesPathByProject = (projectType?: ProjectTypes) => {
switch (projectType) {
case ProjectTypes.REMIX:
+ case ProjectTypes.REMIX_VITE:
+ case ProjectTypes.REMIX_SPA:
return "./app";
default:
return "./src";
diff --git a/packages/core/src/contexts/data/types.ts b/packages/core/src/contexts/data/types.ts
index 23cfeea1612a..0330f5dff12a 100644
--- a/packages/core/src/contexts/data/types.ts
+++ b/packages/core/src/contexts/data/types.ts
@@ -96,6 +96,78 @@ export type GraphQLQueryOptions = {
* ```
*/
gqlMutation?: DocumentNode;
+
+ /**
+ * @description GraphQL Variables to be used for more advanced query filters by data providers. If filters correspond to table columns,
+ * these variables will not be presented in the initial filters selected and will not be reset or set by table column filtering.
+ * @optional
+ * @example
+ * ```tsx
+ * import gql from "graphql-tag";
+ * import { useTable } from "@refinedev/antd";
+ * import type { GetFieldsFromList } from "@refinedev/hasura";
+ * import type { GetPostsQuery } from "graphql/types";
+ *
+ * const POSTS_QUERY = gql`
+ * query GetPosts(
+ * $offset: Int!
+ * $limit: Int!
+ * $order_by: [posts_order_by!]
+ * $where: posts_bool_exp
+ * ) {
+ * posts(
+ * offset: $offset
+ * limit: $limit
+ * order_by: $order_by
+ * where: $where
+ * ) {
+ * id
+ * title
+ * category {
+ * id
+ * title
+ * }
+ * }
+ * posts_aggregate(where: $where) {
+ * aggregate {
+ * count
+ * }
+ * }
+ * } `;
+ *
+ *
+ * export const PostList = () => {
+ * const { tableProps } = useTable<
+ * GetFieldsFromList
+ * >({
+ * meta: {
+ * gqlQuery: POSTS_QUERY,
+ * gqlVariables: {
+ * where: {
+ * _and: [
+ * {
+ * title: {
+ * _ilike: "%Updated%",
+ * },
+ * },
+ * {
+ * created_at: {
+ * _gte: "2023-08-04T08:26:26.489116+00:00"
+ * }
+ * }
+ * ],
+ * },
+ * },
+ * }
+ * });
+ * return ( );
+ * }
+ *
+ * ```
+ */
+ gqlVariables?: {
+ [key: string]: any;
+ };
};
export type MetaQuery = {
diff --git a/packages/core/src/hooks/use-resource-params/index.spec.ts b/packages/core/src/hooks/use-resource-params/index.spec.ts
index 38115398558f..6b7ef4bd8eea 100644
--- a/packages/core/src/hooks/use-resource-params/index.spec.ts
+++ b/packages/core/src/hooks/use-resource-params/index.spec.ts
@@ -8,6 +8,8 @@ import {
import { useResourceParams } from ".";
+import type { BaseKey } from "../../contexts/data/types";
+
describe("useResourceParams Hook", () => {
describe("with routerProvider", () => {
it("returns undefined when routerProvider doesn't have params", () => {
@@ -405,6 +407,152 @@ describe("useResourceParams Hook", () => {
);
});
});
+
+ it("should reflect changes in id prop immediately", async () => {
+ const { result, rerender } = renderHook(
+ ({ id }) => useResourceParams({ id }),
+ {
+ wrapper: TestWrapper({}),
+ initialProps: {
+ id: undefined,
+ } as { id?: BaseKey },
+ },
+ );
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ resource: undefined,
+ id: undefined,
+ action: undefined,
+ formAction: "create",
+ identifier: undefined,
+ }),
+ );
+
+ rerender({ id: "123" });
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ resource: undefined,
+ id: "123",
+ action: undefined,
+ formAction: "create",
+ identifier: undefined,
+ }),
+ );
+ });
+
+ it("should reflect both id prop changes and setId invocations", async () => {
+ const { result, rerender } = renderHook(
+ ({ id }) => useResourceParams({ id }),
+ {
+ wrapper: TestWrapper({}),
+ initialProps: {
+ id: undefined,
+ } as { id?: BaseKey },
+ },
+ );
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ resource: undefined,
+ id: undefined,
+ action: undefined,
+ formAction: "create",
+ identifier: undefined,
+ }),
+ );
+
+ act(() => {
+ result.current.setId("123");
+ });
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ resource: undefined,
+ id: "123",
+ action: undefined,
+ formAction: "create",
+ identifier: undefined,
+ }),
+ );
+
+ rerender({ id: "456" });
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ resource: undefined,
+ id: "456",
+ action: undefined,
+ formAction: "create",
+ identifier: undefined,
+ }),
+ );
+ });
+
+ it("should respect value set by setId method", async () => {
+ const { result, rerender } = renderHook(
+ ({ id }) => useResourceParams({ id }),
+ {
+ wrapper: TestWrapper({}),
+ initialProps: {
+ id: "123",
+ } as { id?: BaseKey },
+ },
+ );
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ id: "123",
+ }),
+ );
+
+ act(() => {
+ result.current.setId(undefined);
+ });
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ id: undefined,
+ }),
+ );
+
+ rerender({ id: "456" });
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ id: "456",
+ }),
+ );
+ });
+
+ it("should provide id prop in setId setter", async () => {
+ const { result, rerender } = renderHook(
+ ({ id }) => useResourceParams({ id }),
+ {
+ wrapper: TestWrapper({}),
+ initialProps: {
+ id: "123",
+ } as { id?: BaseKey },
+ },
+ );
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ id: "123",
+ }),
+ );
+
+ act(() => {
+ result.current.setId((prev) => (Number(prev) + 1).toString());
+ });
+
+ expect(result.current).toMatchObject(
+ expect.objectContaining({
+ id: "124",
+ }),
+ );
+ });
});
describe("with legacyRouterProvider", () => {
diff --git a/packages/core/src/hooks/use-resource-params/index.ts b/packages/core/src/hooks/use-resource-params/index.ts
index c2b959d522e5..3673e0e18330 100644
--- a/packages/core/src/hooks/use-resource-params/index.ts
+++ b/packages/core/src/hooks/use-resource-params/index.ts
@@ -64,7 +64,7 @@ export function useResourceParams(props?: Props): ResourceParams {
const [id, setId] = React.useState(defaultId);
- React.useEffect(() => setId(defaultId), [defaultId]);
+ React.useMemo(() => setId(defaultId), [defaultId]);
const formAction = React.useMemo(() => {
if (!isSameResource && !props?.action) {
diff --git a/packages/devtools-server/package.json b/packages/devtools-server/package.json
index 7560ee55da7e..58202bd7eeb9 100644
--- a/packages/devtools-server/package.json
+++ b/packages/devtools-server/package.json
@@ -64,7 +64,7 @@
"lodash-es": "^4.17.21",
"marked": "^4.3.0",
"node-fetch": "^2.6.7",
- "preferred-pm": "^3.1.3",
+ "package-manager-detector": "^0.1.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sanitize-html": "^2.11.0",
diff --git a/packages/devtools-server/src/packages/update-package.ts b/packages/devtools-server/src/packages/update-package.ts
index c2c113453df7..b807bcddc32d 100644
--- a/packages/devtools-server/src/packages/update-package.ts
+++ b/packages/devtools-server/src/packages/update-package.ts
@@ -1,4 +1,4 @@
-import preferredPM from "preferred-pm";
+import { detect } from "package-manager-detector";
import execa from "execa";
export const updatePackage = async (
@@ -6,11 +6,10 @@ export const updatePackage = async (
projectPath: string = process.cwd(),
) => {
try {
- const { name: pm } = (await preferredPM(projectPath)) ?? {
- name: "npm",
- };
+ const detected = await detect({ cwd: projectPath });
+ const [pm] = (detected?.agent || "npm").split("@");
- const { failed } = await execa(pm ?? "npm", [
+ const { failed } = await execa(pm, [
"install",
...packages.map((p) => `${p}@latest`),
]);
diff --git a/packages/hasura/refine.config.js b/packages/hasura/refine.config.js
index 4867469ccf6d..ecb5852a67c5 100644
--- a/packages/hasura/refine.config.js
+++ b/packages/hasura/refine.config.js
@@ -32,6 +32,10 @@ module.exports = {
src: "./src/utils/index.ts",
dest: "./providers/hasura/utils/index.ts",
},
+ {
+ src: "./src/utils/index.ts",
+ dest: "./providers/hasura/utils/boolExp.ts",
+ },
{
src: "./src/utils/camelizeKeys.ts",
dest: "./providers/hasura/utils/camelizeKeys.ts",
@@ -77,13 +81,13 @@ module.exports = {
"x-hasura-role": "public",
},
});
-
+
const gqlDataProvider = dataProvider(client, {
namingConvention: "graphql-default",
});
-
- // Refine supports GraphQL subscriptions out-of-the-box. For more detailed information, please visit here:
+
+ // Refine supports GraphQL subscriptions out-of-the-box. For more detailed information, please visit here:
// https://refine.dev/docs/core/providers/live-provider/
// const WS_URL = "ws://flowing-mammal-24.hasura.app/v1/graphql";
// const gqlWebSocketClient = graphqlWS.createClient({
diff --git a/packages/hasura/src/dataProvider/index.ts b/packages/hasura/src/dataProvider/index.ts
index da18ab27cf03..0ed27cf7ec35 100644
--- a/packages/hasura/src/dataProvider/index.ts
+++ b/packages/hasura/src/dataProvider/index.ts
@@ -8,6 +8,7 @@ import {
generateSorting,
getOperationFields,
isMutation,
+ mergeHasuraFilters,
metaFieldsToGqlFields,
upperCaseValues,
} from "../utils";
@@ -103,13 +104,26 @@ const dataProvider = (
: camelCase(`${operation}_bool_exp`, { pascalCase: true });
if (meta?.gqlQuery) {
- const response = await client.request(meta.gqlQuery, {
- where: {
+ const hasuraFilters = mergeHasuraFilters(
+ {
id: {
_in: ids,
},
},
- });
+ meta?.gqlVariables?.where,
+ );
+
+ const variables = {
+ ...(meta.gqlVariables && meta.gqlVariables),
+ ...(hasuraFilters && {
+ where: hasuraFilters,
+ }),
+ };
+
+ const response = await client.request(
+ meta.gqlQuery,
+ variables,
+ );
return {
data: response[operation],
};
@@ -159,12 +173,15 @@ const dataProvider = (
? generateSorting(sorters)
: upperCaseValues(camelizeKeys(generateSorting(sorters)));
- const hasuraFilters = generateFilters(filters, namingConvention);
-
+ let hasuraFilters = generateFilters(filters, namingConvention);
let query;
let variables;
if (meta?.gqlQuery) {
+ hasuraFilters = mergeHasuraFilters(
+ hasuraFilters,
+ meta?.gqlVariables?.where,
+ );
query = meta.gqlQuery;
variables = {
...hasuraPagination,
@@ -176,6 +193,7 @@ const dataProvider = (
: {
order_by: hasuraSorting,
})),
+ ...(meta.gqlVariables && meta.gqlVariables),
...(hasuraFilters && {
where: hasuraFilters,
}),
diff --git a/packages/hasura/src/utils/boolexp.ts b/packages/hasura/src/utils/boolexp.ts
new file mode 100644
index 000000000000..27eb90174b80
--- /dev/null
+++ b/packages/hasura/src/utils/boolexp.ts
@@ -0,0 +1,63 @@
+/**
+ * Generic operators (all column types except json, jsonb)
+ */
+interface GenericOperator {
+ _eq?: T;
+ _neq?: T;
+ _gt?: T;
+ _gte?: T;
+ _lt?: T;
+ _lte?: T;
+ _in?: Array;
+ _nin?: Array;
+ _is_null?: boolean;
+}
+
+/** expression to compare columns of type Int. All fields are combined with logical 'AND'. */
+interface NumberOperator extends GenericOperator {}
+
+/** expression to compare columns of type String. All fields are combined with logical 'AND'. */
+interface StringOperator extends GenericOperator {
+ _ilike?: string;
+ _like?: string;
+ _nilike?: string;
+ _nlike?: string;
+ _nsimilar?: string;
+ _similar?: string;
+ _regex?: string;
+ _iregex?: string;
+ _nregex?: string;
+ _niregex?: string;
+}
+
+/** expression to compare columns of type json. All fields are combined with logical 'AND'. */
+interface JsonOperator extends GenericOperator {
+ /* is the column contained in the given json value */
+ _contained_in?: object;
+ /* does the column contain the given json value at the top level */
+ _contains?: object;
+ /* does the string exist as a top-level key in the column */
+ _has_key?: string;
+ /* do any of these strings exist as top-level keys in the column */
+ _has_keys_any?: string[];
+ /* do all of these strings exist as top-level keys in the column */
+ _has_keys_all?: string[];
+}
+
+export type Operator = NumberOperator | StringOperator | JsonOperator;
+
+export type MultiConditionFilter = "_and" | "_or";
+
+type HasuraOperator = NumberOperator & StringOperator & JsonOperator;
+
+export type HasuraOperatorKey = keyof HasuraOperator;
+
+export interface BoolExp {
+ // @ts-ignore
+ _and?: BoolExp[];
+ // @ts-ignore
+ _or?: BoolExp[];
+ // @ts-ignore
+ _not?: BoolExp;
+ [key: string]: Operator | BoolExp | BoolExp[];
+}
diff --git a/packages/hasura/src/utils/generateFilters.ts b/packages/hasura/src/utils/generateFilters.ts
index 96c54d829ce6..14da4d3e1cd3 100644
--- a/packages/hasura/src/utils/generateFilters.ts
+++ b/packages/hasura/src/utils/generateFilters.ts
@@ -5,7 +5,9 @@ import type {
} from "@refinedev/core";
import camelcase from "camelcase";
import setWith from "lodash/setWith";
+import cloneDeep from "lodash/cloneDeep";
import type { NamingConvention } from "src/dataProvider";
+import type { BoolExp, MultiConditionFilter } from "./boolexp";
export type HasuraFilterCondition =
| "_and"
@@ -117,7 +119,7 @@ const convertHasuraOperatorToGraphqlDefaultNaming = (
export const generateNestedFilterQuery = (
filter: HasuraCrudFilter,
namingConvention: NamingConvention = "hasura-default",
-): any => {
+): BoolExp => {
const { operator } = filter;
if (
@@ -154,10 +156,10 @@ export const generateNestedFilterQuery = (
};
};
-export const generateFilters: any = (
+export const generateFilters = (
filters?: HasuraCrudFilters,
namingConvention: NamingConvention = "hasura-default",
-) => {
+): BoolExp | undefined => {
if (!filters) {
return undefined;
}
@@ -172,3 +174,61 @@ export const generateFilters: any = (
return nestedQuery;
};
+
+function isMultiConditionFilter(key: string): key is MultiConditionFilter {
+ const keys: MultiConditionFilter[] = ["_and", "_or"];
+ return keys.includes(key as MultiConditionFilter);
+}
+
+export const mergeHasuraFilters = (
+ filters?: BoolExp,
+ metaFilters?: BoolExp,
+): BoolExp | undefined => {
+ if (!metaFilters) {
+ return filters;
+ }
+ const mergedFilters = filters ? cloneDeep(filters) : {};
+ const gqlVariableFilters = Object.entries(metaFilters);
+ const arbitraryOperators = gqlVariableFilters.filter((f) => {
+ const [k] = f;
+ return !isMultiConditionFilter(k);
+ });
+
+ if (
+ arbitraryOperators.length > 1 ||
+ (metaFilters._and && arbitraryOperators.length)
+ ) {
+ if (!mergedFilters._and) {
+ mergedFilters._and = [];
+ }
+ }
+
+ if (
+ arbitraryOperators.length > 1 ||
+ ((metaFilters._and || metaFilters._or) && arbitraryOperators.length)
+ ) {
+ console.warn(
+ "@packages/hasura: multiple filters present. Group multiple parameters using the _and or the _or operator. Tip: You can use the _or and _and operators along with the _not operator to create arbitrarily complex boolean expressions involving multiple filtering criteria.",
+ );
+ }
+
+ gqlVariableFilters.forEach((filter) => {
+ const [k, v] = filter;
+
+ if (!isMultiConditionFilter(k) && mergedFilters._and) {
+ // Group Multiple Parameters Together
+ mergedFilters._and = mergedFilters._and.concat({ [k]: v });
+ } else if (k === "_and" && mergedFilters._and) {
+ // Merge _and conditions from both groups of Hasura Filters
+ if (!Array.isArray(v)) {
+ throw new Error(
+ "@packages/hasura: unexpected value for BoolExp _and. Expected an Array.",
+ );
+ }
+ mergedFilters._and = mergedFilters._and.concat(v);
+ } else {
+ mergedFilters[k] = v;
+ }
+ });
+ return mergedFilters;
+};
diff --git a/packages/hasura/src/utils/index.ts b/packages/hasura/src/utils/index.ts
index 61b5d89626e4..965ec0d527ba 100644
--- a/packages/hasura/src/utils/index.ts
+++ b/packages/hasura/src/utils/index.ts
@@ -6,3 +6,4 @@ export * from "./generateFilters";
export * from "./camelizeKeys";
export * from "./upperCaseValues";
export * from "./graphql";
+export * from "./boolexp";
diff --git a/packages/hasura/test/getList/index.mock.ts b/packages/hasura/test/getList/index.mock.ts
index 733e6cec3de8..81e2e15f5b53 100644
--- a/packages/hasura/test/getList/index.mock.ts
+++ b/packages/hasura/test/getList/index.mock.ts
@@ -981,3 +981,1477 @@ nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
"84380de90e9e1cd0-BUD",
],
);
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ content: { _ilike: "updated%" },
+ _or: [
+ { title: { _ilike: "Etiam tincidunt ex ut auctor faucibus" } },
+ { title: { _ilike: "%Updated%" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "6379bbda-0857-40f2-a277-b401ea6134d7",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ {
+ id: "c7ba5e30-8c5f-46bb-862d-e2bcf6487749",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ {
+ id: "ae316d48-025a-47db-b4c0-ff4694f52c85",
+ title: "Etiam tincidunt ex ut auctor faucibus",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2024-01-10T19:45:30.879255+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "9efe1ab9-bd9c-4057-8a76-98d329c1518f",
+ title: "Etiam tincidunt ex ut auctor faucibus",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2023-08-04T08:26:30.134135+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "4ef4ba41-92d5-4d1f-b1a5-fc91c9a08284",
+ title: "Multiple Updated Title",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2023-08-04T08:25:17.281437+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "572708c7-840d-430a-befd-1416bdee799a",
+ title: "Updated Title",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2024-01-10T19:45:27.810216+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "881a45fd-a5da-46f4-a045-58eeb647862f",
+ title: "Etiam tincidunt ex ut auctor faucibus",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2024-01-10T21:04:24.862392+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "2a0d531e-ad15-440f-bf0b-7d23e7e21131",
+ title: "Updated Title",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2024-01-10T19:45:29.357149+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 8 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:29 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "c481d193d13ea6a2d46351c0ab1232a9",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd029bfbc2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ _and: [
+ { content: { _ilike: "updated%" } },
+ { category: { title: { _eq: "aaaaaaaaaarhoncus" } } },
+ ],
+ _or: [
+ { title: { _ilike: "Etiam tincidunt ex ut auctor faucibus" } },
+ { title: { _ilike: "%Updated%" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "6379bbda-0857-40f2-a277-b401ea6134d7",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ {
+ id: "c7ba5e30-8c5f-46bb-862d-e2bcf6487749",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 2 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:30 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "581",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "31f578daef6ca27f50ac919f77c3ce67",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd09ed41c2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ _and: [
+ { content: { _ilike: "updated%" } },
+ { category: { title: { _eq: "aaaaaaaaaarhoncus" } } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "6379bbda-0857-40f2-a277-b401ea6134d7",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ {
+ id: "c7ba5e30-8c5f-46bb-862d-e2bcf6487749",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ {
+ id: "9cd924d2-de3b-479a-9882-23feeb0fd80f",
+ title: "habitasse platea dictumst aliquam",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2022-12-26T08:24:11.665314+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 3 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:30 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "8bed9d7de2613bf5a291f22130fd232e",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd104a0dc2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ _and: [
+ { category: { title: { _eq: "aaaaaaaaaarhoncus" } } },
+ { created_at: { _gte: "2023-06-07T06:13:57.996223+00:00" } },
+ { title: { _ilike: "%test%" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "fcaa6d74-ef59-41af-954b-50defdb78d30",
+ title: "Test Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-06-07T06:13:57.996223+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:31 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "321",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "cd8bde5c248b6b0a37cf7c9783b4a754",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd12ca32c2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ _and: [
+ { created_at: { _gte: "2023-06-07T06:13:57.996223+00:00" } },
+ { content: { _eq: "Updated Content" } },
+ { category: { title: { _eq: "aaaaaaaaaarhoncus" } } },
+ { title: { _ilike: "%updated%" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "6379bbda-0857-40f2-a277-b401ea6134d7",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ {
+ id: "c7ba5e30-8c5f-46bb-862d-e2bcf6487749",
+ title: "Updated Title",
+ category_id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ created_at: "2023-08-04T08:24:53.471186+00:00",
+ category: {
+ id: "0e0c9acc-5ade-42d3-b0ca-f762565e24ef",
+ title: "aaaaaaaaaarhoncus",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 2 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:31 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "581",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "24af4e061ed058f6fe66349eb1677313",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd15598dc2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ _and: [
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ { category: { title: { _eq: "turpis adipiscing lorem 123" } } },
+ { title: { _ilike: "%Updated%" } },
+ { title: { _ilike: "%3%" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ category_id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:32 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "335",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "28138dad035b154e7dea8e00d7b9c790",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd19de5ac2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ {
+ _not: {
+ category_id: { _eq: "ff454a95-d2d4-45b2-9eed-506c9d0fc282" },
+ },
+ },
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "7f690a87-db33-4a8f-b02d-e6d4a7241a9b",
+ title: "123",
+ category_id: "adfd9627-9a4d-4bef-8ded-a927c800804d",
+ created_at: "2024-01-08T11:19:58.060476+00:00",
+ category: {
+ id: "adfd9627-9a4d-4bef-8ded-a927c800804d",
+ title: "HK",
+ },
+ },
+ {
+ id: "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ title: "updated title3",
+ category_id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ created_at: "2023-11-04T05:22:02.513672+00:00",
+ category: {
+ id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ title: "test category2",
+ },
+ },
+ {
+ id: "7af17f71-1ddf-4969-bcec-565f05b16098",
+ title: "updated title3",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2024-01-04T12:19:46.663453+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "d52e6a60-dfd2-4b2c-b443-62b64b6b3aa0",
+ title: "123",
+ category_id: "0ea181ad-dd28-4844-bfc6-fd278e46710d",
+ created_at: "2024-01-08T14:48:58.190456+00:00",
+ category: {
+ id: "0ea181ad-dd28-4844-bfc6-fd278e46710d",
+ title: "vulputate elementum nullam 222",
+ },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ category_id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 5 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:33 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "aa95efe0b53898a46ef087b3276d49d5",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd1cbf4bc2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ order_by: { title: "asc" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { title: { _ilike: "%Updated%" } },
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ category_id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ category_id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 2 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:33 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "582",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "78ad56737027956f1bf8d1119421ba59",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd236ce7c2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ order_by: { title: "asc" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { _not: { category: { title: { _eq: "ok" } } } },
+ { title: { _ilike: "%Updated%" } },
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ category_id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:34 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "335",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "5598e52a8424e79f2ebac21a0f8b9122",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd25cd1bc2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ order_by: { title: "asc" },
+ where: {
+ _and: [
+ { _not: { category: { title: { _eq: "ok" } } } },
+ { title: { _ilike: "%Updated%" } },
+ { title: { _ilike: "%3%" } },
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ category_id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:35 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "335",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "1ff032afd969c8a9aaac8e382cd47221",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd2d5e41c2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ order_by: { id: "desc" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { title: { _ilike: "%Updated%" } },
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ category_id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ category_id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ created_at: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 2 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:35 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "582",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "4d4547a62ef90a72f7f44849ba400b3f",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd2fee75c2f1-IAH",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $order_by: [posts_order_by!], $where: posts_bool_exp) {\n posts(offset: $offset, limit: $limit, order_by: $order_by, where: $where) {\n id\n title\n category_id\n created_at\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ order_by: { id: "asc" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { _not: { category: { title: { _eq: "ok" } } } },
+ {
+ _not: {
+ category_id: { _eq: "ff454a95-d2d4-45b2-9eed-506c9d0fc282" },
+ },
+ },
+ { created_at: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "7af17f71-1ddf-4969-bcec-565f05b16098",
+ title: "updated title3",
+ category_id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ created_at: "2024-01-04T12:19:46.663453+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "7f690a87-db33-4a8f-b02d-e6d4a7241a9b",
+ title: "123",
+ category_id: "adfd9627-9a4d-4bef-8ded-a927c800804d",
+ created_at: "2024-01-08T11:19:58.060476+00:00",
+ category: {
+ id: "adfd9627-9a4d-4bef-8ded-a927c800804d",
+ title: "HK",
+ },
+ },
+ {
+ id: "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ title: "updated title3",
+ category_id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ created_at: "2023-11-04T05:22:02.513672+00:00",
+ category: {
+ id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ title: "test category2",
+ },
+ },
+ {
+ id: "d52e6a60-dfd2-4b2c-b443-62b64b6b3aa0",
+ title: "123",
+ category_id: "0ea181ad-dd28-4844-bfc6-fd278e46710d",
+ created_at: "2024-01-08T14:48:58.190456+00:00",
+ category: {
+ id: "0ea181ad-dd28-4844-bfc6-fd278e46710d",
+ title: "vulputate elementum nullam 222",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 4 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:36 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "688630a594c200adccd99413d485bcd8",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd32c812c2f1-IAH",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $orderBy: [PostsOrderBy!], $where: PostsBoolExp) {\n posts(offset: $offset, limit: $limit, orderBy: $orderBy, where: $where) {\n id\n title\n categoryId\n createdAt\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ orderBy: { title: "ASC" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { title: { _ilike: "%Updated%" } },
+ { createdAt: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ categoryId: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ createdAt: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ categoryId: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ createdAt: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 2 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:37 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "577",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "e3153b0fbce5a3c169830748befb80ff",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd35ea3416d1-IAH",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $orderBy: [PostsOrderBy!], $where: PostsBoolExp) {\n posts(offset: $offset, limit: $limit, orderBy: $orderBy, where: $where) {\n id\n title\n categoryId\n createdAt\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ orderBy: { title: "ASC" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { _not: { category: { title: { _eq: "ok" } } } },
+ { title: { _ilike: "%Updated%" } },
+ { createdAt: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ categoryId: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ createdAt: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:37 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "332",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "f88b47fd6877e5a9050afb04145ef24a",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd3dac4916d1-IAH",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $orderBy: [PostsOrderBy!], $where: PostsBoolExp) {\n posts(offset: $offset, limit: $limit, orderBy: $orderBy, where: $where) {\n id\n title\n categoryId\n createdAt\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ orderBy: { title: "ASC" },
+ where: {
+ _and: [
+ { _not: { category: { title: { _eq: "ok" } } } },
+ { title: { _ilike: "%Updated%" } },
+ { title: { _ilike: "%3%" } },
+ { createdAt: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ categoryId: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ createdAt: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:39 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "332",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "107c3e7049c48c16812ff16588215550",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd409fc316d1-IAH",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $orderBy: [PostsOrderBy!], $where: PostsBoolExp) {\n posts(offset: $offset, limit: $limit, orderBy: $orderBy, where: $where) {\n id\n title\n categoryId\n createdAt\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ orderBy: { id: "DESC" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { title: { _ilike: "%Updated%" } },
+ { createdAt: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ _or: [
+ { content: { _eq: "CREATED content23" } },
+ { content: { _eq: "CREATED content1" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ categoryId: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ createdAt: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ categoryId: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ createdAt: "2024-01-09T07:20:02.573986+00:00",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 2 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:39 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "577",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "4b3671a9fc5b4259e528ee651a84c8dc",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd484ab816d1-IAH",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($offset: Int!, $limit: Int!, $orderBy: [PostsOrderBy!], $where: PostsBoolExp) {\n posts(offset: $offset, limit: $limit, orderBy: $orderBy, where: $where) {\n id\n title\n categoryId\n createdAt\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ limit: 10,
+ offset: 0,
+ orderBy: { id: "ASC" },
+ where: {
+ _and: [
+ { title: { _ilike: "%3%" } },
+ { _not: { category: { title: { _eq: "ok" } } } },
+ {
+ _not: {
+ categoryId: { _eq: "ff454a95-d2d4-45b2-9eed-506c9d0fc282" },
+ },
+ },
+ { createdAt: { _gte: "2023-08-04T08:26:26.489116+00:00" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "7af17f71-1ddf-4969-bcec-565f05b16098",
+ title: "updated title3",
+ categoryId: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ createdAt: "2024-01-04T12:19:46.663453+00:00",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "7f690a87-db33-4a8f-b02d-e6d4a7241a9b",
+ title: "123",
+ categoryId: "adfd9627-9a4d-4bef-8ded-a927c800804d",
+ createdAt: "2024-01-08T11:19:58.060476+00:00",
+ category: {
+ id: "adfd9627-9a4d-4bef-8ded-a927c800804d",
+ title: "HK",
+ },
+ },
+ {
+ id: "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ title: "updated title3",
+ categoryId: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ createdAt: "2023-11-04T05:22:02.513672+00:00",
+ category: {
+ id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ title: "test category2",
+ },
+ },
+ {
+ id: "d52e6a60-dfd2-4b2c-b443-62b64b6b3aa0",
+ title: "123",
+ categoryId: "0ea181ad-dd28-4844-bfc6-fd278e46710d",
+ createdAt: "2024-01-08T14:48:58.190456+00:00",
+ category: {
+ id: "0ea181ad-dd28-4844-bfc6-fd278e46710d",
+ title: "vulputate elementum nullam 222",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 4 } },
+ },
+ },
+ [
+ "Date",
+ "Sun, 11 Aug 2024 05:57:40 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "50d7566bd7b6a34908327e9be6e6dd01",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b15fd4b1fa116d1-IAH",
+ ],
+ );
diff --git a/packages/hasura/test/getList/index.spec.ts b/packages/hasura/test/getList/index.spec.ts
index 2b90858d23c8..afeb3b174095 100644
--- a/packages/hasura/test/getList/index.spec.ts
+++ b/packages/hasura/test/getList/index.spec.ts
@@ -1,8 +1,12 @@
import gql from "graphql-tag";
+import type { DocumentNode } from "graphql";
import dataProvider from "../../src/index";
import { createClient } from "../gqlClient";
import "./index.mock";
+// import nock from "nock";
+// nock.recorder.rec();
+
describe("with meta.fields", () => {
describe.each(["hasura-default", "graphql-default"] as const)(
"getList with %s naming convention",
@@ -242,3 +246,647 @@ describe("with gqlQuery", () => {
expect(total).toBe(3);
});
});
+
+describe("with gqlVariables", () => {
+ beforeEach(() => {
+ jest.spyOn(console, "warn").mockImplementation(() => {
+ return;
+ });
+ });
+ const gqlQuery = gql`
+ query GetPosts(
+ $offset: Int!
+ $limit: Int!
+ $order_by: [posts_order_by!]
+ $where: posts_bool_exp
+ ) {
+ posts(
+ offset: $offset
+ limit: $limit
+ order_by: $order_by
+ where: $where
+ ) {
+ id
+ title
+ category_id
+ created_at
+ category {
+ id
+ title
+ }
+ }
+ posts_aggregate(where: $where) {
+ aggregate {
+ count
+ }
+ }
+ }
+ `;
+ it("with one column filter and 2 '_or' filters", async () => {
+ const client = createClient("hasura-default");
+
+ const { data } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ content: { _ilike: "updated%" },
+ _or: [
+ {
+ title: { _ilike: "Etiam tincidunt ex ut auctor faucibus" },
+ },
+ {
+ title: { _ilike: "%Updated%" },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(8);
+ expect(data[0].id).toBe("6379bbda-0857-40f2-a277-b401ea6134d7");
+ expect(data[7].id).toBe("2a0d531e-ad15-440f-bf0b-7d23e7e21131");
+ });
+ it("with 2 arbitrary column filters and '_or' filter", async () => {
+ const client = createClient("hasura-default");
+
+ const { data } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ content: { _ilike: "updated%" },
+ category: { title: { _eq: "aaaaaaaaaarhoncus" } },
+ _or: [
+ {
+ title: { _ilike: "Etiam tincidunt ex ut auctor faucibus" },
+ },
+ {
+ title: { _ilike: "%Updated%" },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(2);
+ expect(data[0].id).toBe("6379bbda-0857-40f2-a277-b401ea6134d7");
+ expect(data[1].id).toBe("c7ba5e30-8c5f-46bb-862d-e2bcf6487749");
+ });
+ it("with 2 arbitrary column filters", async () => {
+ const client = createClient("hasura-default");
+
+ const { data } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ content: { _ilike: "updated%" },
+ category: { title: { _eq: "aaaaaaaaaarhoncus" } },
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(3);
+ expect(data[0].id).toBe("6379bbda-0857-40f2-a277-b401ea6134d7");
+ expect(data[1].id).toBe("c7ba5e30-8c5f-46bb-862d-e2bcf6487749");
+ expect(data[2].id).toBe("9cd924d2-de3b-479a-9882-23feeb0fd80f");
+ });
+ it("with one column filter and '_and' filter", async () => {
+ const client = createClient("hasura-default");
+ const { data } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ category: { title: { _eq: "aaaaaaaaaarhoncus" } },
+ _and: [
+ {
+ created_at: {
+ _gte: "2023-06-07T06:13:57.996223+00:00",
+ },
+ },
+ {
+ title: { _ilike: "%test%" },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(1);
+ expect(data[0].id).toBe("fcaa6d74-ef59-41af-954b-50defdb78d30");
+ });
+ it("with 2 arbitrary column filters and multiple '_and' filters", async () => {
+ const client = createClient("hasura-default");
+
+ const { data } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ created_at: { _gte: "2023-06-07T06:13:57.996223+00:00" },
+ content: { _eq: "Updated Content" },
+ _and: [
+ {
+ category: { title: { _eq: "aaaaaaaaaarhoncus" } },
+ },
+ {
+ title: { _ilike: "%updated%" },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(2);
+ expect(data[0].id).toBe("6379bbda-0857-40f2-a277-b401ea6134d7");
+ expect(data[1].id).toBe("c7ba5e30-8c5f-46bb-862d-e2bcf6487749");
+ });
+
+ it("with 2 column filters and '_or' filter and '_and' filter", async () => {
+ const client = createClient("hasura-default");
+
+ const { data } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ created_at: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ category: {
+ title: { _eq: "turpis adipiscing lorem 123" },
+ },
+ _and: [
+ {
+ title: {
+ _ilike: "%Updated%",
+ },
+ },
+ {
+ title: {
+ _ilike: "%3%",
+ },
+ },
+ ],
+ _or: [
+ {
+ content: {
+ _eq: "CREATED content23",
+ },
+ },
+ {
+ content: {
+ _eq: "CREATED content1",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(1);
+ expect(data[0].id).toBe("1f85588c-7fc2-4223-b955-42909a7df4a8");
+ });
+
+ it("with filter by title, with '_and' -> '_not' nested filters", async () => {
+ const client = createClient("hasura-default");
+
+ const { data, total } = await dataProvider(client, {
+ namingConvention: "hasura-default",
+ }).getList({
+ resource: "posts",
+ filters: [
+ {
+ field: "title",
+ operator: "contains",
+ value: "3",
+ },
+ ],
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ _not: {
+ category_id: {
+ _eq: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ },
+ },
+ },
+ {
+ created_at: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data[0]["id"]).toBe("7f690a87-db33-4a8f-b02d-e6d4a7241a9b");
+ expect(data[0]["category"].title).toBe("HK");
+ expect(total).toBe(5);
+
+ expect(data[0]["title"]).toBe("123");
+ });
+
+ describe.each(["hasura-default", "graphql-default"] as const)(
+ "getList with %s naming convention",
+ (namingConvention) => {
+ let gqlQuery: DocumentNode;
+ if (namingConvention === "hasura-default") {
+ gqlQuery = gql`
+ query GetPosts(
+ $offset: Int!
+ $limit: Int!
+ $order_by: [posts_order_by!]
+ $where: posts_bool_exp
+ ) {
+ posts(
+ offset: $offset
+ limit: $limit
+ order_by: $order_by
+ where: $where
+ ) {
+ id
+ title
+ category_id
+ created_at
+ category {
+ id
+ title
+ }
+ }
+ posts_aggregate(where: $where) {
+ aggregate {
+ count
+ }
+ }
+ }
+ `;
+ } else {
+ gqlQuery = gql`
+ query GetPosts(
+ $offset: Int!
+ $limit: Int!
+ $orderBy: [PostsOrderBy!]
+ $where: PostsBoolExp
+ ) {
+ posts(
+ offset: $offset
+ limit: $limit
+ orderBy: $orderBy
+ where: $where
+ ) {
+ id
+ title
+ categoryId
+ createdAt
+ category {
+ id
+ title
+ }
+ }
+ postsAggregate(where: $where) {
+ aggregate {
+ count
+ }
+ }
+ }
+ `;
+ }
+ const categoryIdkey =
+ namingConvention === "hasura-default" ? "category_id" : "categoryId";
+ const createdAtKey =
+ namingConvention === "hasura-default" ? "created_at" : "createdAt";
+
+ it("with filter by title, sort by title asc, with '_and' & '_or' filters", async () => {
+ const client = createClient(namingConvention);
+
+ const { data, total } = await dataProvider(client, {
+ namingConvention,
+ }).getList({
+ resource: "posts",
+ filters: [
+ {
+ field: "title",
+ operator: "contains",
+ value: "3",
+ },
+ ],
+ sorters: [
+ {
+ field: "title",
+ order: "asc",
+ },
+ ],
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ title: {
+ _ilike: "%Updated%",
+ },
+ },
+ {
+ [createdAtKey]: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ _or: [
+ {
+ content: {
+ _eq: "CREATED content23",
+ },
+ },
+ {
+ content: {
+ _eq: "CREATED content1",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data[0]["id"]).toBe("0ad3a15a-3191-4f44-910f-bd210deaa589");
+ expect(data[0]["category"].title).toBe("ok");
+ expect(total).toBe(2);
+
+ expect(data[0]["title"]).toBe("updated title12345");
+ expect(data[1]["title"]).toBe("updated title3");
+ });
+ it("with filter by title, sort by title asc, with '_not' & '_and' & '_or'", async () => {
+ const client = createClient(namingConvention);
+
+ const { data, total } = await dataProvider(client, {
+ namingConvention,
+ }).getList({
+ resource: "posts",
+ filters: [
+ {
+ field: "title",
+ operator: "contains",
+ value: "3",
+ },
+ ],
+ sorters: [
+ {
+ field: "title",
+ order: "asc",
+ },
+ ],
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _not: {
+ category: { title: { _eq: "ok" } },
+ },
+ _and: [
+ {
+ title: {
+ _ilike: "%Updated%",
+ },
+ },
+ {
+ [createdAtKey]: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ _or: [
+ {
+ content: {
+ _eq: "CREATED content23",
+ },
+ },
+ {
+ content: {
+ _eq: "CREATED content1",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data[0]["id"]).toBe("1f85588c-7fc2-4223-b955-42909a7df4a8");
+ expect(data[0]["category"].title).toBe("turpis adipiscing lorem 123");
+ expect(total).toBe(1);
+
+ expect(data[0]["title"]).toBe("updated title3");
+ });
+ it("without provided CRUD filters, with sort by title asc, '_and' & '_or' filters", async () => {
+ const client = createClient(namingConvention);
+
+ const { data, total } = await dataProvider(client, {
+ namingConvention,
+ }).getList({
+ resource: "posts",
+ sorters: [
+ {
+ field: "title",
+ order: "asc",
+ },
+ ],
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ _not: {
+ category: { title: { _eq: "ok" } },
+ },
+ },
+ {
+ title: {
+ _ilike: "%Updated%",
+ },
+ },
+ {
+ title: {
+ _ilike: "%3%",
+ },
+ },
+ {
+ [createdAtKey]: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ _or: [
+ {
+ content: {
+ _eq: "CREATED content23",
+ },
+ },
+ {
+ content: {
+ _eq: "CREATED content1",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data[0]["id"]).toBe("1f85588c-7fc2-4223-b955-42909a7df4a8");
+ expect(data[0]["category"].title).toBe("turpis adipiscing lorem 123");
+ expect(total).toBe(1);
+
+ expect(data[0]["title"]).toBe("updated title3");
+ });
+
+ it("with filter by title, sort by id DESC, with '_and' & '_or'", async () => {
+ const client = createClient(namingConvention);
+
+ const { data, total } = await dataProvider(client, {
+ namingConvention,
+ }).getList({
+ resource: "posts",
+ filters: [
+ {
+ field: "title",
+ operator: "contains",
+ value: "3",
+ },
+ ],
+ sorters: [
+ {
+ field: "id",
+ order: "desc",
+ },
+ ],
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ title: {
+ _ilike: "%Updated%",
+ },
+ },
+ {
+ [createdAtKey]: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ _or: [
+ {
+ content: {
+ _eq: "CREATED content23",
+ },
+ },
+ {
+ content: {
+ _eq: "CREATED content1",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data[0]["id"]).toBe("1f85588c-7fc2-4223-b955-42909a7df4a8");
+ expect(data[0]["category"].title).toBe("turpis adipiscing lorem 123");
+ expect(total).toBe(2);
+
+ expect(data[0]["title"]).toBe("updated title3");
+ expect(data[1]["title"]).toBe("updated title12345");
+ });
+ it("with sort by id, with '_and' -> multiple '_not' filters.", async () => {
+ const client = createClient(namingConvention);
+
+ const { data, total } = await dataProvider(client, {
+ namingConvention,
+ }).getList({
+ resource: "posts",
+ sorters: [
+ {
+ field: "id",
+ order: "asc",
+ },
+ ],
+ filters: [
+ {
+ field: "title",
+ operator: "contains",
+ value: "3",
+ },
+ ],
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ _not: {
+ category: { title: { _eq: "ok" } },
+ },
+ },
+ {
+ _not: {
+ [categoryIdkey]: {
+ _eq: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ },
+ },
+ },
+ {
+ [createdAtKey]: {
+ _gte: "2023-08-04T08:26:26.489116+00:00",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data[0]["id"]).toBe("7af17f71-1ddf-4969-bcec-565f05b16098");
+ expect(data[0]["category"].title).toBe("lorem1 integer tincidunty");
+ expect(total).toBe(4);
+
+ expect(data[0]["title"]).toBe("updated title3");
+ });
+ },
+ );
+});
diff --git a/packages/hasura/test/getMany/index.mock.ts b/packages/hasura/test/getMany/index.mock.ts
index ed76d77b00ff..1cd0f84c29a6 100644
--- a/packages/hasura/test/getMany/index.mock.ts
+++ b/packages/hasura/test/getMany/index.mock.ts
@@ -287,3 +287,585 @@ nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
"843770b74ad6c1a8-BUD",
],
);
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($where: posts_bool_exp!) {\n posts(where: $where) {\n id\n title\n content\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ where: {
+ id: {
+ _in: [
+ "572708c7-840d-430a-befd-1416bdee799a",
+ "478212ed-9a78-428c-b418-306bd88e0790",
+ ],
+ },
+ content: { _neq: "Updated Content" },
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "478212ed-9a78-428c-b418-306bd88e0790",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Fri, 09 Aug 2024 16:09:02 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "277",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "812fdef57432b57197fc93e2408e6a19",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b09021828a52ccb-DFW",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($where: posts_bool_exp!) {\n posts(where: $where) {\n id\n title\n content\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ where: {
+ id: {
+ _in: [
+ "7f690a87-db33-4a8f-b02d-e6d4a7241a9b",
+ "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ "7af17f71-1ddf-4969-bcec-565f05b16098",
+ "d52e6a60-dfd2-4b2c-b443-62b64b6b3aa0",
+ "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ ],
+ },
+ title: { _ilike: "%updated%" },
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ title: "updated title3",
+ content: "dasdasd",
+ category: {
+ id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ title: "test category2",
+ },
+ },
+ {
+ id: "7af17f71-1ddf-4969-bcec-565f05b16098",
+ title: "updated title3",
+ content: "123123",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ content: "CREATED content23",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 3 } },
+ },
+ },
+ [
+ "Date",
+ "Fri, 09 Aug 2024 16:09:04 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "601",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "3af6dc8b919f84f6aa2dc61168fad780",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b090221ea102ccb-DFW",
+ ],
+ );
+
+nock("https://flowing-mammal-24.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($where: posts_bool_exp!) {\n posts(where: $where) {\n id\n title\n content\n category {\n id\n title\n }\n }\n posts_aggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ where: {
+ id: {
+ _in: [
+ "203f94d4-396a-454c-a92e-947ec6bacb37",
+ "294c278e-4e3d-4a40-88ee-208f47a42e7e",
+ "2d2af5b8-70ed-46a3-873b-f79e92226c97",
+ "1d7493ca-1401-48d3-9b92-d400fe0fbd16",
+ "3be19a24-ecee-42d9-949b-5f41623b9b5a",
+ "3d71a408-ac30-41f2-b530-3fe951b16b86",
+ "0b5e9ce1-e686-4ab6-909b-e51235f028a9",
+ "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ "2a0d531e-ad15-440f-bf0b-7d23e7e21131",
+ ],
+ },
+ _and: [
+ { _not: { category: { title: { _eq: "ok" } } } },
+ { title: { _ilike: "%updated%" } },
+ ],
+ _or: [
+ { category_id: { _eq: "e27156c3-9998-434f-bd5b-2b078283ff26" } },
+ { category_id: { _eq: "6869be25-7189-40a0-9e3c-12164c1929ec" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "203f94d4-396a-454c-a92e-947ec6bacb37",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "294c278e-4e3d-4a40-88ee-208f47a42e7e",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "2d2af5b8-70ed-46a3-873b-f79e92226c97",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "1d7493ca-1401-48d3-9b92-d400fe0fbd16",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "3be19a24-ecee-42d9-949b-5f41623b9b5a",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "6869be25-7189-40a0-9e3c-12164c1929ec",
+ title: "consequat nulla",
+ },
+ },
+ {
+ id: "3d71a408-ac30-41f2-b530-3fe951b16b86",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "2a0d531e-ad15-440f-bf0b-7d23e7e21131",
+ title: "Updated Title",
+ content: "Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ ],
+ posts_aggregate: { aggregate: { count: 7 } },
+ },
+ },
+ [
+ "Date",
+ "Fri, 09 Aug 2024 16:09:05 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "70f2dbad1101c5fb5e870b73b4e10217",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b0902288ecc2ccb-DFW",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($where: PostsBoolExp!) {\n posts(where: $where) {\n id\n title\n content\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ where: {
+ id: {
+ _in: [
+ "572708c7-840d-430a-befd-1416bdee799a",
+ "478212ed-9a78-428c-b418-306bd88e0790",
+ ],
+ },
+ content: { _neq: "Updated Content" },
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "478212ed-9a78-428c-b418-306bd88e0790",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 1 } },
+ },
+ },
+ [
+ "Date",
+ "Fri, 09 Aug 2024 16:09:06 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "276",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "40b6d1a3b187b2b9f1c02f97d1b31453",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b09022feb1146de-DFW",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($where: PostsBoolExp!) {\n posts(where: $where) {\n id\n title\n content\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ where: {
+ id: {
+ _in: [
+ "7f690a87-db33-4a8f-b02d-e6d4a7241a9b",
+ "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ "7af17f71-1ddf-4969-bcec-565f05b16098",
+ "d52e6a60-dfd2-4b2c-b443-62b64b6b3aa0",
+ "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ ],
+ },
+ title: { _ilike: "%updated%" },
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ title: "updated title3",
+ content: "dasdasd",
+ category: {
+ id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7",
+ title: "test category2",
+ },
+ },
+ {
+ id: "7af17f71-1ddf-4969-bcec-565f05b16098",
+ title: "updated title3",
+ content: "123123",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ content: "CREATED content23",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 3 } },
+ },
+ },
+ [
+ "Date",
+ "Fri, 09 Aug 2024 16:09:07 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Content-Length",
+ "600",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "c9f943366b990460cca31a0d8b02a0e6",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b0902376e9746de-DFW",
+ ],
+ );
+
+nock("https://ruling-redbird-23.hasura.app:443", { encodedQueryParams: true })
+ .post("/v1/graphql", {
+ query:
+ "query GetPosts($where: PostsBoolExp!) {\n posts(where: $where) {\n id\n title\n content\n category {\n id\n title\n }\n }\n postsAggregate(where: $where) {\n aggregate {\n count\n }\n }\n}\n",
+ variables: {
+ where: {
+ id: {
+ _in: [
+ "203f94d4-396a-454c-a92e-947ec6bacb37",
+ "294c278e-4e3d-4a40-88ee-208f47a42e7e",
+ "2d2af5b8-70ed-46a3-873b-f79e92226c97",
+ "1d7493ca-1401-48d3-9b92-d400fe0fbd16",
+ "3be19a24-ecee-42d9-949b-5f41623b9b5a",
+ "3d71a408-ac30-41f2-b530-3fe951b16b86",
+ "0b5e9ce1-e686-4ab6-909b-e51235f028a9",
+ "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ "2a0d531e-ad15-440f-bf0b-7d23e7e21131",
+ ],
+ },
+ _and: [
+ { _not: { category: { title: { _eq: "ok" } } } },
+ { title: { _ilike: "%updated%" } },
+ ],
+ _or: [
+ { categoryId: { _eq: "e27156c3-9998-434f-bd5b-2b078283ff26" } },
+ { categoryId: { _eq: "6869be25-7189-40a0-9e3c-12164c1929ec" } },
+ ],
+ },
+ },
+ operationName: "GetPosts",
+ })
+ .reply(
+ 200,
+ {
+ data: {
+ posts: [
+ {
+ id: "203f94d4-396a-454c-a92e-947ec6bacb37",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "294c278e-4e3d-4a40-88ee-208f47a42e7e",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "2d2af5b8-70ed-46a3-873b-f79e92226c97",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "1d7493ca-1401-48d3-9b92-d400fe0fbd16",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "3be19a24-ecee-42d9-949b-5f41623b9b5a",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "6869be25-7189-40a0-9e3c-12164c1929ec",
+ title: "consequat nulla",
+ },
+ },
+ {
+ id: "3d71a408-ac30-41f2-b530-3fe951b16b86",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "2a0d531e-ad15-440f-bf0b-7d23e7e21131",
+ title: "Updated Title",
+ content: "Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ ],
+ postsAggregate: { aggregate: { count: 7 } },
+ },
+ },
+ [
+ "Date",
+ "Fri, 09 Aug 2024 16:09:08 GMT",
+ "Content-Type",
+ "application/json; charset=utf-8",
+ "Transfer-Encoding",
+ "chunked",
+ "Connection",
+ "keep-alive",
+ "x-request-id",
+ "18881600c8c1f927074ee8f6ebe748cb",
+ "CF-Cache-Status",
+ "DYNAMIC",
+ "Content-Security-Policy",
+ "upgrade-insecure-requests",
+ "Referrer-Policy",
+ "strict-origin-when-cross-origin",
+ "Strict-Transport-Security",
+ "max-age=31536000; includeSubDomains",
+ "X-Content-Type-Options",
+ "nosniff",
+ "X-Frame-Options",
+ "SAMEORIGIN",
+ "X-XSS-Protection",
+ "0",
+ "Server",
+ "cloudflare",
+ "CF-RAY",
+ "8b09023edb2946de-DFW",
+ ],
+ );
diff --git a/packages/hasura/test/getMany/index.spec.ts b/packages/hasura/test/getMany/index.spec.ts
index 99c30691a6a9..c1e6d22174f8 100644
--- a/packages/hasura/test/getMany/index.spec.ts
+++ b/packages/hasura/test/getMany/index.spec.ts
@@ -1,8 +1,12 @@
import gql from "graphql-tag";
+import type { DocumentNode } from "graphql";
import dataProvider from "../../src/index";
import { createClient } from "../gqlClient";
import "./index.mock";
+// import nock from "nock";
+// nock.recorder.rec();
+
describe("with meta.fields", () => {
describe.each(["hasura-default", "graphql-default"] as const)(
"updateMany with %s naming convention",
@@ -186,3 +190,311 @@ describe("with gqlQuery", () => {
expect(data[1]["category"].id).toEqual(posts[1]["category"]["id"]);
});
});
+
+describe("with gqlVariables", () => {
+ describe.each(["hasura-default", "graphql-default"] as const)(
+ "getMany with %s naming convention",
+ (namingConvention) => {
+ let gqlQuery: DocumentNode;
+ if (namingConvention === "hasura-default") {
+ gqlQuery = gql`
+ query GetPosts($where: posts_bool_exp!) {
+ posts(where: $where) {
+ id
+ title
+ content
+ category {
+ id
+ title
+ }
+ }
+ posts_aggregate(where: $where) {
+ aggregate {
+ count
+ }
+ }
+ }
+ `;
+ } else {
+ gqlQuery = gql`
+ query GetPosts($where: PostsBoolExp!) {
+ posts(where: $where) {
+ id
+ title
+ content
+ category {
+ id
+ title
+ }
+ }
+ postsAggregate(where: $where) {
+ aggregate {
+ count
+ }
+ }
+ }
+ `;
+ }
+ it("correct response hasura-default with single hasura operator content filter", async () => {
+ const client = createClient(namingConvention);
+ const posts = [
+ {
+ id: "572708c7-840d-430a-befd-1416bdee799a",
+ title: "Updated Title",
+ content: "Updated Content",
+ category: { id: "e27156c3-9998-434f-bd5b-2b078283ff26" },
+ },
+ {
+ id: "478212ed-9a78-428c-b418-306bd88e0790",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: { id: "e27156c3-9998-434f-bd5b-2b078283ff26" },
+ },
+ ];
+
+ const { data } = await dataProvider(client, {
+ namingConvention,
+ }).getMany!({
+ resource: "posts",
+ ids: posts.map((post) => post.id),
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ content: { _neq: "Updated Content" },
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(1);
+
+ expect(data[0]["id"]).toEqual(posts[1]["id"]);
+ expect(data[0]["title"]).toEqual(posts[1]["title"]);
+ expect(data[0]["content"]).toEqual(posts[1]["content"]);
+ expect(data[0]["category"].id).toEqual(posts[1]["category"]["id"]);
+ });
+
+ it("correct request/response where like %updated%", async () => {
+ const client = createClient(namingConvention);
+ const posts = [
+ {
+ id: "7f690a87-db33-4a8f-b02d-e6d4a7241a9b",
+ title: "123",
+ content: "123123",
+ category: { id: "adfd9627-9a4d-4bef-8ded-a927c800804d" },
+ },
+ {
+ id: "a4e83c6a-1fa1-4814-b8bc-82b249b3f6d9",
+ title: "updated title3",
+ content: "dasdasd",
+ category: { id: "a08a1612-bee1-4e6f-b7d5-6fd40d7c3eb7" },
+ },
+ {
+ id: "7af17f71-1ddf-4969-bcec-565f05b16098",
+ title: "updated title3",
+ content: "123123",
+ category: { id: "e27156c3-9998-434f-bd5b-2b078283ff26" },
+ },
+ {
+ id: "d52e6a60-dfd2-4b2c-b443-62b64b6b3aa0",
+ title: "123",
+ content: "123123",
+ category: { id: "0ea181ad-dd28-4844-bfc6-fd278e46710d" },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ content: "CREATED content23",
+ category: { id: "0386c795-d8b2-40fd-b115-69ab60e7c098" },
+ },
+ ];
+
+ const { data } = await dataProvider(client, {
+ namingConvention,
+ }).getMany!({
+ resource: "posts",
+ ids: posts.map((post) => post.id),
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ title: { _ilike: "%updated%" },
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(3);
+
+ expect(data[0]["id"]).toEqual(posts[1]["id"]);
+ expect(data[0]["title"]).toEqual(posts[1]["title"]);
+ expect(data[0]["content"]).toEqual(posts[1]["content"]);
+ expect(data[0]["category"].id).toEqual(posts[1]["category"]["id"]);
+
+ expect(data[1]["id"]).toEqual(posts[2]["id"]);
+ expect(data[1]["title"]).toEqual(posts[2]["title"]);
+ expect(data[1]["content"]).toEqual(posts[2]["content"]);
+ expect(data[1]["category"].id).toEqual(posts[2]["category"]["id"]);
+
+ expect(data[2]["id"]).toEqual(posts[4]["id"]);
+ expect(data[2]["title"]).toEqual(posts[4]["title"]);
+ expect(data[2]["content"]).toEqual(posts[4]["content"]);
+ expect(data[2]["category"].id).toEqual(posts[4]["category"]["id"]);
+ });
+
+ it("builds request with multiple filters and receives accurate response", async () => {
+ const client = createClient(namingConvention);
+
+ const posts = [
+ {
+ id: "203f94d4-396a-454c-a92e-947ec6bacb37",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "294c278e-4e3d-4a40-88ee-208f47a42e7e",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "2d2af5b8-70ed-46a3-873b-f79e92226c97",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "1d7493ca-1401-48d3-9b92-d400fe0fbd16",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "3be19a24-ecee-42d9-949b-5f41623b9b5a",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "6869be25-7189-40a0-9e3c-12164c1929ec",
+ title: "consequat nulla",
+ },
+ },
+ {
+ id: "3d71a408-ac30-41f2-b530-3fe951b16b86",
+ title: "Multiple Updated Title",
+ content: "Multiple Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ {
+ id: "0b5e9ce1-e686-4ab6-909b-e51235f028a9",
+ title: "updated ws1231",
+ content: "Lorem ipsum dolor sit amet.",
+ category: {
+ id: "ef49aebd-abcc-4bac-b064-a63b31f2e8ce",
+ title: "sed accumsan felixxx",
+ },
+ },
+ {
+ id: "1f85588c-7fc2-4223-b955-42909a7df4a8",
+ title: "updated title3",
+ content: "CREATED content1",
+ category: {
+ id: "ff454a95-d2d4-45b2-9eed-506c9d0fc282",
+ title: "turpis adipiscing lorem 123",
+ },
+ },
+ {
+ id: "0ad3a15a-3191-4f44-910f-bd210deaa589",
+ title: "updated title12345",
+ content: "CREATED content23",
+ category: {
+ id: "0386c795-d8b2-40fd-b115-69ab60e7c098",
+ title: "ok",
+ },
+ },
+ {
+ id: "2a0d531e-ad15-440f-bf0b-7d23e7e21131",
+ title: "Updated Title",
+ content: "Updated Content",
+ category: {
+ id: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ title: "lorem1 integer tincidunty",
+ },
+ },
+ ];
+
+ const categoryIdkey =
+ namingConvention === "hasura-default" ? "category_id" : "categoryId";
+
+ const { data } = await dataProvider(client, {
+ namingConvention,
+ }).getMany!({
+ resource: "posts",
+ ids: posts.map((post) => post.id),
+ meta: {
+ gqlQuery,
+ gqlVariables: {
+ where: {
+ _and: [
+ {
+ _not: {
+ category: { title: { _eq: "ok" } },
+ },
+ },
+ {
+ title: { _ilike: "%updated%" },
+ },
+ ],
+ _or: [
+ {
+ [categoryIdkey]: {
+ _eq: "e27156c3-9998-434f-bd5b-2b078283ff26",
+ },
+ },
+ {
+ [categoryIdkey]: {
+ _eq: "6869be25-7189-40a0-9e3c-12164c1929ec",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ expect(data.length).toBe(7);
+
+ expect(data[0]["id"]).toEqual(posts[0]["id"]);
+ expect(data[0]["title"]).toEqual(posts[0]["title"]);
+ expect(data[0]["content"]).toEqual(posts[0]["content"]);
+ expect(data[0]["category"].id).toEqual(posts[0]["category"]["id"]);
+
+ expect(data[6]["id"]).toEqual(posts[9]["id"]);
+ expect(data[6]["title"]).toEqual(posts[9]["title"]);
+ expect(data[6]["content"]).toEqual(posts[9]["content"]);
+ expect(data[6]["category"].id).toEqual(posts[9]["category"]["id"]);
+
+ expect(data[5]["id"]).toEqual(posts[5]["id"]);
+ expect(data[5]["title"]).toEqual(posts[5]["title"]);
+ expect(data[5]["content"]).toEqual(posts[5]["content"]);
+ expect(data[5]["category"].id).toEqual(posts[5]["category"]["id"]);
+ });
+ },
+ );
+});
diff --git a/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap b/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap
index 96dbc807dbdd..d54d198e1591 100644
--- a/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap
+++ b/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap
@@ -9866,7 +9866,7 @@ exports[`MuiInferencer should match the snapshot 2`] = `