fork refine

This commit is contained in:
Stefan Pejcic
2024-02-05 10:23:04 +01:00
parent 3fffde9a8f
commit 8496a83edb
3634 changed files with 715528 additions and 2 deletions

View File

@@ -0,0 +1,11 @@
node_modules
.DS_Store
test
jest.config.js
**/*.spec.ts
**/*.spec.tsx
**/*.test.ts
**/*.test.tsx
tsup.config.ts
tsconfig.test.json
tsconfig.declarations.json

View File

@@ -0,0 +1,455 @@
# @refinedev/graphql
## 6.4.8
### Patch Changes
- [#5425](https://github.com/refinedev/refine/pull/5425) [`190af9fce2`](https://github.com/refinedev/refine/commit/190af9fce292bc46b169e3e121be6bf1c2a939a5) Thanks [@aliemir](https://github.com/aliemir)! - Updated `@refinedev/core` peer dependencies to latest (`^4.46.1`)
## 6.4.7
### Patch Changes
- [#5330](https://github.com/refinedev/refine/pull/5330) [`7c8827b43d`](https://github.com/refinedev/refine/commit/7c8827b43d9e378818be6ee23032925c97ce02d5) Thanks [@BatuhanW](https://github.com/BatuhanW)! - chore: upgrade nock library version to ^13.4.0
## 6.4.6
### Patch Changes
- [#5022](https://github.com/refinedev/refine/pull/5022) [`80513a4e42f`](https://github.com/refinedev/refine/commit/80513a4e42f8dda39e01157643594a9e4c32001b) Thanks [@BatuhanW](https://github.com/BatuhanW)! - chore: update README.md
- fix grammar errors.
- make all README.md files consistent.
- add code example code snippets.
## 6.4.5
### Patch Changes
- [#5022](https://github.com/refinedev/refine/pull/5022) [`80513a4e42f`](https://github.com/refinedev/refine/commit/80513a4e42f8dda39e01157643594a9e4c32001b) Thanks [@BatuhanW](https://github.com/BatuhanW)! - chore: update README.md
- fix grammar errors.
- make all README.md files consistent.
- add code example code snippets.
## 6.4.4
### Patch Changes
- [#4530](https://github.com/refinedev/refine/pull/4530) [`a90f14301c3`](https://github.com/refinedev/refine/commit/a90f14301c37d0a57299258c3a525fd95d6b11c1) Thanks [@hbtecRJE](https://github.com/hbtecRJE)! - fix(graphql): issue with graphql liveprovider subscription query
For the graphql subscription, 'query' was used instead of 'subscription'.
## 6.4.3
### Patch Changes
- [#4530](https://github.com/refinedev/refine/pull/4530) [`a90f14301c3`](https://github.com/refinedev/refine/commit/a90f14301c37d0a57299258c3a525fd95d6b11c1) Thanks [@hbtecRJE](https://github.com/hbtecRJE)! - fix(graphql): issue with graphql liveprovider subscription query
For the graphql subscription, 'query' was used instead of 'subscription'.
## 6.4.2
### Patch Changes
- [#4285](https://github.com/refinedev/refine/pull/4285) [`b5cd3328504`](https://github.com/refinedev/refine/commit/b5cd332850428383e8b43f997cbb0340ac7f0dc6) Thanks [@alicanerdurmaz](https://github.com/alicanerdurmaz)! - fixed: A bug that prevented data providers from being swizzled.
## 6.4.1
### Patch Changes
- [#4285](https://github.com/refinedev/refine/pull/4285) [`b5cd3328504`](https://github.com/refinedev/refine/commit/b5cd332850428383e8b43f997cbb0340ac7f0dc6) Thanks [@alicanerdurmaz](https://github.com/alicanerdurmaz)! - fixed: A bug that prevented data providers from being swizzled.
## 6.4.0
### Minor Changes
- [#4211](https://github.com/refinedev/refine/pull/4211) [`dca1d8b7ca8`](https://github.com/refinedev/refine/commit/dca1d8b7ca8b84184d00180af7e265c6bd12d708) Thanks [@mattbho](https://github.com/mattbho)! - Custom GraphQL data provider now allow users to provide a custom InputType via the meta field.
## 6.3.0
### Minor Changes
- [#4211](https://github.com/refinedev/refine/pull/4211) [`dca1d8b7ca8`](https://github.com/refinedev/refine/commit/dca1d8b7ca8b84184d00180af7e265c6bd12d708) Thanks [@mattbho](https://github.com/mattbho)! - Custom GraphQL data provider now allow users to provide a custom InputType via the meta field.
## 6.2.0
### Minor Changes
- [#4168](https://github.com/refinedev/refine/pull/4168) [`51ab614f75b`](https://github.com/refinedev/refine/commit/51ab614f75bb96d55ff08061e7cb63bdda5f1c8f) Thanks [@alicanerdurmaz](https://github.com/alicanerdurmaz)! - feat: added refine.config.js to support swizzling. Now with swizzle support, you can easily customize graphql data provider for your needs.
feat: tests added for utility functions.
chore: utility functions have been moved to their own files.
## 6.1.0
### Minor Changes
- [#4168](https://github.com/refinedev/refine/pull/4168) [`51ab614f75b`](https://github.com/refinedev/refine/commit/51ab614f75bb96d55ff08061e7cb63bdda5f1c8f) Thanks [@alicanerdurmaz](https://github.com/alicanerdurmaz)! - feat: added refine.config.js to support swizzling. Now with swizzle support, you can easily customize graphql data provider for your needs.
feat: tests added for utility functions.
chore: utility functions have been moved to their own files.
## 6.0.1
### Patch Changes
- [#4030](https://github.com/refinedev/refine/pull/4030) [`1b019280252`](https://github.com/refinedev/refine/commit/1b019280252140c251bf464426b0b072acd310fe) Thanks [@yildirayunlu](https://github.com/yildirayunlu)! - Upgraded `graphql-request` dependency `4.x` to [`5.x`](https://github.com/jasonkuhrt/graphql-request/releases/tag/5.0.0).
## 6.0.0
### Major Changes
- [#4030](https://github.com/refinedev/refine/pull/4030) [`1b019280252`](https://github.com/refinedev/refine/commit/1b019280252140c251bf464426b0b072acd310fe) Thanks [@yildirayunlu](https://github.com/yildirayunlu)! - Upgraded `graphql-request` dependency `4.x` to [`5.x`](https://github.com/jasonkuhrt/graphql-request/releases/tag/5.0.0).
## 5.1.0
### Minor Changes
- Thanks [@aliemir](https://github.com/aliemir), [@alicanerdurmaz](https://github.com/alicanerdurmaz), [@batuhanW](https://github.com/batuhanW), [@salihozdemir](https://github.com/salihozdemir), [@yildirayunlu](https://github.com/yildirayunlu), [@recepkutuk](https://github.com/recepkutuk)!
- `metaData` prop is now deprecated for all data provider methods. Use `meta` prop instead.
> For backward compatibility, we still support `metaData` prop with refine v4.
```diff
create: async ({
- metaData
+ meta
}) => {
...
},
```
- `sort`, `hasPagination`, and `metaData` parameters of `getList` method are now deprecated. Use `sorters`, `pagination`, and `meta` parameters instead.
> For backward compatibility, we still support `sort`, `hasPagination` and `metaData` props with refine v4.
```diff
getList: async ({
- sort
+ sorters
- hasPagination
+ pagination: { mode: "off" | "server | "client" }
- metaData
+ meta
}) => {
...
},
```
- Thanks [@aliemir](https://github.com/aliemir), [@alicanerdurmaz](https://github.com/alicanerdurmaz), [@batuhanW](https://github.com/batuhanW), [@salihozdemir](https://github.com/salihozdemir), [@yildirayunlu](https://github.com/yildirayunlu), [@recepkutuk](https://github.com/recepkutuk)!
**Moving to the `@refinedev` scope 🎉🎉**
Moved to the `@refinedev` scope and updated our packages to use the new scope. From now on, all packages will be published under the `@refinedev` scope with their new names.
Now, we're also removing the `refine` prefix from all packages. So, the `@pankod/refine-core` package is now `@refinedev/core`, `@pankod/refine-antd` is now `@refinedev/antd`, and so on.
### Patch Changes
## 4.15.0
### Minor Changes
- [#3822](https://github.com/refinedev/refine/pull/3822) [`0baa99ba787`](https://github.com/refinedev/refine/commit/0baa99ba7874394d9d28d0a7b29c082c604258fb) Thanks [@BatuhanW](https://github.com/BatuhanW)! - - refine v4 release announcement added to "postinstall". - refine v4 is released 🎉 The new version is 100% backward compatible. You can upgrade to v4 with a single command! See the migration guide here: https://refine.dev/docs/migration-guide/3x-to-4x
## 4.14.0
### Minor Changes
- [#3822](https://github.com/refinedev/refine/pull/3822) [`0baa99ba787`](https://github.com/refinedev/refine/commit/0baa99ba7874394d9d28d0a7b29c082c604258fb) Thanks [@BatuhanW](https://github.com/BatuhanW)! - - refine v4 release announcement added to "postinstall". - refine v4 is released 🎉 The new version is 100% backward compatible. You can upgrade to v4 with a single command! See the migration guide here: https://refine.dev/docs/migration-guide/3x-to-4x
## 4.13.0
### Minor Changes
- [#3597](https://github.com/refinedev/refine/pull/3597) [`69140d996ed`](https://github.com/refinedev/refine/commit/69140d996ed20a4afbed34e791082b8dd61450b1) Thanks [@aliemir](https://github.com/aliemir)! - Fix typo in variable names. Exported helper `genereteSort` is renamed to `generateSort` but the old name is still exported for backward compatibility.
## 4.12.0
### Minor Changes
- [#3597](https://github.com/refinedev/refine/pull/3597) [`69140d996ed`](https://github.com/refinedev/refine/commit/69140d996ed20a4afbed34e791082b8dd61450b1) Thanks [@aliemir](https://github.com/aliemir)! - Fix typo in variable names. Exported helper `genereteSort` is renamed to `generateSort` but the old name is still exported for backward compatibility.
## 4.11.0
### Minor Changes
- Only `or` was supported as a conditional filter. Now `and` and `or` can be used together and nested. 🚀
```
{
operator: "or",
value: [
{
operator: "and",
value: [
{
field: "name",
operator: "eq",
value: "John Doe",
},
{
field: "age",
operator: "eq",
value: 30,
},
],
},
{
operator: "and",
value: [
{
field: "name",
operator: "eq",
value: "JR Doe",
},
{
field: "age",
operator: "eq",
value: 1,
},
],
},
],
}
```
## 4.10.0
### Minor Changes
- [#2751](https://github.com/refinedev/refine/pull/2751) [`addff64c77`](https://github.com/refinedev/refine/commit/addff64c777e4c9f044a1a109cb05453e6e9f762) Thanks [@yildirayunlu](https://github.com/yildirayunlu)! - Only `or` was supported as a conditional filter. Now `and` and `or` can be used together and nested. 🚀
```
{
operator: "or",
value: [
{
operator: "and",
value: [
{
field: "name",
operator: "eq",
value: "John Doe",
},
{
field: "age",
operator: "eq",
value: 30,
},
],
},
{
operator: "and",
value: [
{
field: "name",
operator: "eq",
value: "JR Doe",
},
{
field: "age",
operator: "eq",
value: 1,
},
],
},
],
}
```
## 4.9.0
### Minor Changes
- Updated `dataProvider` types with `Required` utility to mark `getMany`, `createMany`, `updateMany` and `deleteMany` as implemented.
## 4.8.0
### Minor Changes
- [#2688](https://github.com/refinedev/refine/pull/2688) [`508045ac30`](https://github.com/refinedev/refine/commit/508045ac30cd3948f68497e13fdf04f7c72ce387) Thanks [@aliemir](https://github.com/aliemir)! - Updated `dataProvider` types with `Required` utility to mark `getMany`, `createMany`, `updateMany` and `deleteMany` as implemented.
## 4.7.0
### Minor Changes
- Update type declaration generation with `tsc` instead of `tsup` for better navigation throughout projects source code.
## 4.6.0
### Minor Changes
- [#2440](https://github.com/refinedev/refine/pull/2440) [`0150dcd070`](https://github.com/refinedev/refine/commit/0150dcd0700253f1c4908e7e5f2e178bb122e9af) Thanks [@aliemir](https://github.com/aliemir)! - Update type declaration generation with `tsc` instead of `tsup` for better navigation throughout projects source code.
## 4.5.0
### Minor Changes
- All of the refine packages have dependencies on the `@pankod/refine-core` package. So far we have managed these dependencies with `peerDependencies` + `dependencies` but this causes issues like #2183. (having more than one @pankod/refine-core version in node_modules and creating different instances)
Managing as `peerDependencies` + `devDependencies` seems like the best way for now to avoid such issues.
## 4.4.0
### Minor Changes
- [#2217](https://github.com/refinedev/refine/pull/2217) [`b4aae00f77`](https://github.com/refinedev/refine/commit/b4aae00f77a2476d847994db21298ae25e4cf6e5) Thanks [@omeraplak](https://github.com/omeraplak)! - All of the refine packages have dependencies on the `@pankod/refine-core` package. So far we have managed these dependencies with `peerDependencies` + `dependencies` but this causes issues like #2183. (having more than one @pankod/refine-core version in node_modules and creating different instances)
Managing as `peerDependencies` + `devDependencies` seems like the best way for now to avoid such issues.
## 4.3.0
### Minor Changes
- ### `@pankod/refine-core`
- Added extra params to `useSubscription` and `useResourceSubscription`
- `useOne`, `useMany` and `useList` passed extra params to own subscription hook.
### `@pankod/refine-hasura`
- Added `liveProvider`.
To see an example of how to use it, check out [`here`](https://github.com/refinedev/refine/blob/master/examples/dataProvider/hasura/src/App.tsx).
### `@pankod/refine-nhost`
- Added `liveProvider`.
To see an example of how to use it, check out [`here`](https://github.com/refinedev/refine/blob/master/examples/dataProvider/nhost/src/App.tsx).
### `@pankod/refine-graphql`
- Added `liveProvider`.
### Patch Changes
- Updated dependencies []:
- @pankod/refine-core@3.42.0
## 4.2.0
### Minor Changes
- [#2120](https://github.com/refinedev/refine/pull/2120) [`2aa7aace52`](https://github.com/refinedev/refine/commit/2aa7aace52b3f232327db2b0f41f793a2885e788) Thanks [@salihozdemir](https://github.com/salihozdemir)! - ### `@pankod/refine-core`
- Added extra params to `useSubscription` and `useResourceSubscription`
- `useOne`, `useMany` and `useList` passed extra params to own subscription hook.
### `@pankod/refine-hasura`
- Added `liveProvider`.
To see an example of how to use it, check out [`here`](https://github.com/refinedev/refine/blob/master/examples/dataProvider/hasura/src/App.tsx).
### `@pankod/refine-nhost`
- Added `liveProvider`.
To see an example of how to use it, check out [`here`](https://github.com/refinedev/refine/blob/master/examples/dataProvider/nhost/src/App.tsx).
### `@pankod/refine-graphql`
- Added `liveProvider`.
### Patch Changes
- Updated dependencies [[`2aa7aace52`](https://github.com/refinedev/refine/commit/2aa7aace52b3f232327db2b0f41f793a2885e788)]:
- @pankod/refine-core@3.41.0
## 4.1.0
### Minor Changes
- Upgraded `grapql-request` version in graphql data provider packages.
Now the `graphql-request` and `qql-query-builder` packages are exported in these packages.
```diff
- import dataProvider from "@pankod/refine-strapi-graphql";
- import { GraphQLClient } from "graphql-request";
- import * as qqlQueryBuilder from "gql-query-builder";
+ import dataProvider, { GraphQLClient, qqlQueryBuilder } from "@pankod/refine-strapi-graphql";
```
### Patch Changes
- Updated dependencies []:
- @pankod/refine-core@3.38.2
## 4.0.0
### Major Changes
- [#2113](https://github.com/refinedev/refine/pull/2113) [`c2fb7ac0e9`](https://github.com/refinedev/refine/commit/c2fb7ac0e9b5871de76aa975b2a196ab39fa7a6b) Thanks [@omeraplak](https://github.com/omeraplak)! - Upgraded `grapql-request` version in graphql data provider packages.
Now the `graphql-request` and `qql-query-builder` packages are exported in these packages.
```diff
- import dataProvider from "@pankod/refine-strapi-graphql";
- import { GraphQLClient } from "graphql-request";
- import * as qqlQueryBuilder from "gql-query-builder";
+ import dataProvider, { GraphQLClient, qqlQueryBuilder } from "@pankod/refine-strapi-graphql";
```
### Patch Changes
- Updated dependencies [[`ee8e8bbd6c`](https://github.com/refinedev/refine/commit/ee8e8bbd6cf6ff2ab1a87883e4030205dedb16ea)]:
- @pankod/refine-core@3.38.1
## 3.25.4
### Patch Changes
- Updated pagination parameters default values and added `hasPagination` property to `getList` method of the data providers.
**Implementation**
Updated the `getList` method accordingly to the changes in the `useTable` and `useList` of `@pankod/refine-core`. `hasPagination` is used to disable pagination (defaults to `true`)
**Use Cases**
For some resources, there might be no support for pagination or users might want to see all of the data without any pagination, prior to these changes this was not supported in **refine** data providers.
- Updated dependencies []:
- @pankod/refine-core@3.36.0
## 3.25.3
### Patch Changes
- [#2050](https://github.com/refinedev/refine/pull/2050) [`635cfe9fdb`](https://github.com/refinedev/refine/commit/635cfe9fdbfe5940b950ae99c1f0b686c78bb8e5) Thanks [@ozkalai](https://github.com/ozkalai)! - Updated pagination parameters default values and added `hasPagination` property to `getList` method of the data providers.
**Implementation**
Updated the `getList` method accordingly to the changes in the `useTable` and `useList` of `@pankod/refine-core`. `hasPagination` is used to disable pagination (defaults to `true`)
**Use Cases**
For some resources, there might be no support for pagination or users might want to see all of the data without any pagination, prior to these changes this was not supported in **refine** data providers.
- Updated dependencies [[`ecde34a9b3`](https://github.com/refinedev/refine/commit/ecde34a9b38ef5667fa863f9ebb9dcb1cfff1651), [`635cfe9fdb`](https://github.com/refinedev/refine/commit/635cfe9fdbfe5940b950ae99c1f0b686c78bb8e5)]:
- @pankod/refine-core@3.35.0
## 3.25.2
### Patch Changes
- Added `graphql-request` dependency to peerDependencies
## 3.25.1
### Patch Changes
- [#1930](https://github.com/refinedev/refine/pull/1930) [`04572f5085`](https://github.com/refinedev/refine/commit/04572f5085f024218bd011c388c0dd06e4c4fd55) Thanks [@omeraplak](https://github.com/omeraplak)! - Added `graphql-request` dependency to peerDependencies
## 3.22.2
### Patch Changes
- Updated dependencies [[`2deb19babf`](https://github.com/refinedev/refine/commit/2deb19babfc6db5b00b111ec29aa5ece4c371bbc)]:
- @pankod/refine-core@3.23.2

21
packages/graphql/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Refine Development Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,72 @@
<div align="center" style="margin: 30px;">
<a href="https://refine.dev">
<img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png">
</a>
</div>
<br/>
<div align="center">
<a href="https://refine.dev">Home Page</a> |
<a href="https://discord.gg/refine">Discord</a> |
<a href="https://refine.dev/examples/">Examples</a> |
<a href="https://refine.dev/blog/">Blog</a> |
<a href="https://refine.dev/docs/">Documentation</a>
<br/>
<br/>
[![Discord](https://img.shields.io/discord/837692625737613362.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/refine)
[![Twitter Follow](https://img.shields.io/twitter/follow/refine_dev?style=social)](https://twitter.com/refine_dev)
<a href="https://www.producthunt.com/posts/refine-3?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-refine&#0045;3" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=362220&theme=light&period=daily" alt="refine - 100&#0037;&#0032;open&#0032;source&#0032;React&#0032;framework&#0032;to&#0032;build&#0032;web&#0032;apps&#0032;3x&#0032;faster | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
</div>
<br/>
<div align="center">refine is an open-source, headless React framework for developers building enterprise internal tools, admin panels, dashboards, B2B applications.
<br/>
It eliminates repetitive tasks in CRUD operations and provides industry-standard solutions for critical project components like **authentication**, **access control**, **routing**, **networking**, **state management**, and **i18n**.
</div>
# GraphQL integration for refine
[GraphQL](https://graphql.org/) is an open-source data query and manipulation language for APIs and a query runtime engine.
[refine](https://refine.dev/) is **headless by design**, offering unlimited styling and customization options. Moreover, refine ships with ready-made integrations for [Ant Design](https://ant.design/), [Material UI](https://mui.com/material-ui/getting-started/overview/), [Mantine](https://mantine.dev/), and [Chakra UI](https://chakra-ui.com/) for convenience.
refine has connectors for 15+ backend services, including REST API, [GraphQL](https://graphql.org/), and popular services like [Airtable](https://www.airtable.com/), [Strapi](https://strapi.io/), [Supabase](https://supabase.com/), [Firebase](https://firebase.google.com/), and [NestJS](https://nestjs.com/).
## Installation & Usage
```
npm install @refinedev/graphql
```
```tsx
import dataProvider, { GraphQLClient } from "@refinedev/graphql";
const client = new GraphQLClient("YOUR_API_URL");
const App = () => {
return (
<Refine
dataProvider={dataProvider(client)}
/* ... */
>
{/* ... */}
</Refine>
);
};
```
## Documentation
- For more detailed information and usage, refer to the [refine data provider documentation](https://refine.dev/docs/core/providers/data-provider).
- [Refer to refine GraphQL docs](https://refine.dev/docs/packages/documentation/data-providers/graphql/).
- [Refer to documentation for more info about refine](https://refine.dev/docs/).
- [Step up to refine tutorials](https://refine.dev/docs/tutorial/introduction/index/).

View File

@@ -0,0 +1,7 @@
module.exports = {
preset: "ts-jest",
rootDir: "./",
displayName: "graphql",
setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"],
testEnvironment: "jsdom",
};

View File

@@ -0,0 +1,61 @@
{
"name": "@refinedev/graphql",
"description": "refine GraphQL data provider. refine is a React-based framework for building internal tools, rapidly. It ships with Ant Design System, an enterprise-level UI toolkit.",
"version": "6.4.8",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"private": false,
"files": [
"dist",
"src",
"./refine.config.js"
],
"engines": {
"node": ">=10"
},
"scripts": {
"start": "tsup --watch --format esm,cjs,iife --legacy-output",
"build": "tsup --format esm,cjs,iife --minify --legacy-output",
"test": "jest --passWithNoTests --runInBand",
"prepare": "npm run build"
},
"author": "refine",
"module": "dist/esm/index.js",
"devDependencies": {
"@refinedev/cli": "^2.16.21",
"@refinedev/core": "^4.46.1",
"@esbuild-plugins/node-resolve": "^0.1.4",
"@types/pluralize": "^0.0.29",
"@types/jest": "^29.2.4",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"nock": "^13.4.0",
"ts-jest": "^29.0.3",
"tslib": "^2.3.1",
"tsup": "^6.7.0"
},
"dependencies": {
"camelcase": "^6.2.0",
"gql-query-builder": "^3.5.5",
"graphql": "^15.6.1",
"graphql-request": "^5.2.0",
"pluralize": "^8.0.0",
"graphql-ws": "^5.9.1"
},
"peerDependencies": {
"@refinedev/core": "^4.46.1",
"graphql-request": "^5.2.0",
"gql-query-builder": "^3.5.5",
"graphql-ws": "^5.9.1"
},
"repository": {
"type": "git",
"url": "https://github.com/refinedev/refine.git",
"directory": "packages/graphql"
},
"gitHead": "829f5a516f98c06f666d6be3e6e6099c75c07719",
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,76 @@
/** @type {import('@refinedev/cli').RefineConfig} */
module.exports = {
group: "Data Provider",
swizzle: {
items: [
{
group: "Providers",
label: "GraphQL",
requiredPackages: [
"graphql-ws@5.9.1",
"graphql-request@5.2.0",
"gql-query-builder@3.5.5",
"camelcase@6.2.0",
"graphql@15.6.1",
"pluralize@8.0.0",
],
files: [
{
src: "./src/index.ts",
dest: "./providers/graphql/index.ts",
},
{
src: "./src/dataProvider/index.ts",
dest: "./providers/graphql/dataProvider/index.ts",
},
{
src: "./src/liveProvider/index.ts",
dest: "./providers/graphql/liveProvider/index.ts",
},
{
src: "./src/utils/index.ts",
dest: "./providers/graphql/utils/index.ts",
},
{
src: "./src/utils/generateFilter.ts",
dest: "./providers/graphql/utils/generateFilter.ts",
},
{
src: "./src/utils/generateSort.ts",
dest: "./providers/graphql/utils/generateSort.ts",
},
{
src: "./src/utils/generateUseListSubscription.ts",
dest: "./providers/graphql/utils/generateUseListSubscription.ts",
},
{
src: "./src/utils/generateUseManySubscription.ts",
dest: "./providers/graphql/utils/generateUseManySubscription.ts",
},
{
src: "./src/utils/generateUseOneSubscription.ts",
dest: "./providers/graphql/utils/generateUseOneSubscription.ts",
},
],
message: `
**\`Usage\`**
\`\`\`
// title: App.tsx
import dataProvider, { liveProvider } from "providers/graphql";
const App = () => {
return (
<Refine
dataProvider={dataProvider}
liveProvider={liveProvider}
/* ... */
/>
);
}
\`\`\`
`,
},
],
},
};

View File

@@ -0,0 +1,359 @@
import { DataProvider, BaseRecord } from "@refinedev/core";
import { GraphQLClient } from "graphql-request";
import * as gql from "gql-query-builder";
import pluralize from "pluralize";
import camelCase from "camelcase";
import { generateFilter, generateSort } from "../utils";
const dataProvider = (client: GraphQLClient): Required<DataProvider> => {
return {
getList: async ({ resource, pagination, sorters, filters, meta }) => {
const {
current = 1,
pageSize = 10,
mode = "server",
} = pagination ?? {};
const sortBy = generateSort(sorters);
const filterBy = generateFilter(filters);
const camelResource = camelCase(resource);
const operation = meta?.operation ?? camelResource;
const { query, variables } = gql.query({
operation,
variables: {
...meta?.variables,
sort: sortBy,
where: { value: filterBy, type: "JSON" },
...(mode === "server"
? {
start: (current - 1) * pageSize,
limit: pageSize,
}
: {}),
},
fields: meta?.fields,
});
const response = await client.request<BaseRecord>(query, variables);
return {
data: response[operation],
total: response[operation].count,
};
},
getMany: async ({ resource, ids, meta }) => {
const camelResource = camelCase(resource);
const operation = meta?.operation ?? camelResource;
const { query, variables } = gql.query({
operation,
variables: {
where: {
value: { id_in: ids },
type: "JSON",
},
},
fields: meta?.fields,
});
const response = await client.request<BaseRecord>(query, variables);
return {
data: response[operation],
};
},
create: async ({ resource, variables, meta }) => {
const singularResource = pluralize.singular(resource);
const camelCreateName = camelCase(`create-${singularResource}`);
const operation = meta?.operation ?? camelCreateName;
const inputType = meta?.inputType ?? `${camelCreateName}Input`;
const { query, variables: gqlVariables } = gql.mutation({
operation,
variables: {
input: {
value: { data: variables },
type: inputType,
},
},
fields: meta?.fields ?? [
{
operation: singularResource,
fields: ["id"],
variables: {},
},
],
});
const response = await client.request<BaseRecord>(
query,
gqlVariables,
);
return {
data: response[operation][singularResource],
};
},
createMany: async ({ resource, variables, meta }) => {
const singularResource = pluralize.singular(resource);
const camelCreateName = camelCase(`create-${singularResource}`);
const operation = meta?.operation ?? camelCreateName;
const inputType = meta?.operation ?? `${camelCreateName}Input`;
const response = await Promise.all(
variables.map(async (param) => {
const { query, variables: gqlVariables } = gql.mutation({
operation,
variables: {
input: {
value: { data: param },
type: inputType,
},
},
fields: meta?.fields ?? [
{
operation: singularResource,
fields: ["id"],
variables: {},
},
],
});
const result = await client.request<BaseRecord>(
query,
gqlVariables,
);
return result[operation][singularResource];
}),
);
return {
data: response,
};
},
update: async ({ resource, id, variables, meta }) => {
const singularResource = pluralize.singular(resource);
const camelUpdateName = camelCase(`update-${singularResource}`);
const operation = meta?.operation ?? camelUpdateName;
const inputType = meta?.operation ?? `${camelUpdateName}Input`;
const { query, variables: gqlVariables } = gql.mutation({
operation,
variables: {
input: {
value: { where: { id }, data: variables },
type: inputType,
},
},
fields: meta?.fields ?? [
{
operation: singularResource,
fields: ["id"],
variables: {},
},
],
});
const response = await client.request<BaseRecord>(
query,
gqlVariables,
);
return {
data: response[operation][singularResource],
};
},
updateMany: async ({ resource, ids, variables, meta }) => {
const singularResource = pluralize.singular(resource);
const camelUpdateName = camelCase(`update-${singularResource}`);
const operation = meta?.operation ?? camelUpdateName;
const inputType = meta?.operation ?? `${camelUpdateName}Input`;
const response = await Promise.all(
ids.map(async (id) => {
const { query, variables: gqlVariables } = gql.mutation({
operation,
variables: {
input: {
value: { where: { id }, data: variables },
type: inputType,
},
},
fields: meta?.fields ?? [
{
operation: singularResource,
fields: ["id"],
variables: {},
},
],
});
const result = await client.request<BaseRecord>(
query,
gqlVariables,
);
return result[operation][singularResource];
}),
);
return {
data: response,
};
},
getOne: async ({ resource, id, meta }) => {
const singularResource = pluralize.singular(resource);
const camelResource = camelCase(singularResource);
const operation = meta?.operation ?? camelResource;
const { query, variables } = gql.query({
operation,
variables: {
id: { value: id, type: "ID", required: true },
},
fields: meta?.fields,
});
const response = await client.request<BaseRecord>(query, variables);
return {
data: response[operation],
};
},
deleteOne: async ({ resource, id, meta }) => {
const singularResource = pluralize.singular(resource);
const camelDeleteName = camelCase(`delete-${singularResource}`);
const operation = meta?.operation ?? camelDeleteName;
const inputType = meta?.operation ?? `${camelDeleteName}Input`;
const { query, variables } = gql.mutation({
operation,
variables: {
input: {
value: { where: { id } },
type: inputType,
},
},
fields: meta?.fields ?? [
{
operation: singularResource,
fields: ["id"],
variables: {},
},
],
});
const response = await client.request<BaseRecord>(query, variables);
return {
data: response[operation][singularResource],
};
},
deleteMany: async ({ resource, ids, meta }) => {
const singularResource = pluralize.singular(resource);
const camelDeleteName = camelCase(`delete-${singularResource}`);
const operation = meta?.operation ?? camelDeleteName;
const inputType = meta?.operation ?? `${camelDeleteName}Input`;
const response = await Promise.all(
ids.map(async (id) => {
const { query, variables: gqlVariables } = gql.mutation({
operation,
variables: {
input: {
value: { where: { id } },
type: inputType,
},
},
fields: meta?.fields ?? [
{
operation: singularResource,
fields: ["id"],
variables: {},
},
],
});
const result = await client.request<BaseRecord>(
query,
gqlVariables,
);
return result[operation][singularResource];
}),
);
return {
data: response,
};
},
getApiUrl: () => {
throw Error("Not implemented on refine-graphql data provider.");
},
custom: async ({ url, method, headers, meta }) => {
let gqlClient = client;
if (url) {
gqlClient = new GraphQLClient(url, { headers });
}
if (meta) {
if (meta.operation) {
if (method === "get") {
const { query, variables } = gql.query({
operation: meta.operation,
fields: meta.fields,
variables: meta.variables,
});
const response = await gqlClient.request<BaseRecord>(
query,
variables,
);
return {
data: response[meta.operation],
};
} else {
const { query, variables } = gql.mutation({
operation: meta.operation,
fields: meta.fields,
variables: meta.variables,
});
const response = await gqlClient.request<BaseRecord>(
query,
variables,
);
return {
data: response[meta.operation],
};
}
} else {
throw Error("GraphQL operation name required.");
}
} else {
throw Error(
"GraphQL need to operation, fields and variables values in meta object.",
);
}
},
};
};
export default dataProvider;

View File

@@ -0,0 +1,33 @@
import dataProvider from "./dataProvider";
export default dataProvider;
export * from "./dataProvider";
export * from "./liveProvider";
export {
GraphQLClient,
batchRequests,
gql,
rawRequest,
request,
resolveRequestDocument,
} from "graphql-request";
export type {
BatchRequestDocument,
BatchRequestsExtendedOptions,
BatchRequestsOptions,
ClientError,
GraphQLWebSocketClient,
RawRequestExtendedOptions,
RawRequestOptions,
RequestDocument,
RequestExtendedOptions,
RequestOptions,
Variables,
} from "graphql-request";
export * as qqlQueryBuilder from "gql-query-builder";
export * as graphqlWS from "graphql-ws";
export * from "./utils";

View File

@@ -0,0 +1,82 @@
import { LiveProvider } from "@refinedev/core";
import { Client } from "graphql-ws";
import {
generateUseListSubscription,
generateUseManySubscription,
generateUseOneSubscription,
} from "../utils";
const subscriptions = {
useList: generateUseListSubscription,
useOne: generateUseOneSubscription,
useMany: generateUseManySubscription,
};
export const liveProvider = (client: Client): LiveProvider => {
return {
subscribe: ({ callback, params }) => {
const {
resource,
meta,
pagination,
sorters,
filters,
subscriptionType,
id,
ids,
} = params ?? {};
if (!meta) {
throw new Error(
"[useSubscription]: `meta` is required in `params` for graphql subscriptions",
);
}
if (!subscriptionType) {
throw new Error(
"[useSubscription]: `subscriptionType` is required in `params` for graphql subscriptions",
);
}
if (!resource) {
throw new Error(
"[useSubscription]: `resource` is required in `params` for graphql subscriptions",
);
}
const genereteSubscription = subscriptions[subscriptionType];
const { query, variables, operation } = genereteSubscription({
ids,
id,
resource,
filters,
meta,
pagination,
sorters,
});
const onNext = (payload: any) => {
callback(payload.data[operation]);
};
const unsubscribe = client.subscribe(
{
query,
variables,
},
{
next: onNext,
error: () => null,
complete: () => null,
},
);
return unsubscribe;
},
unsubscribe: (unsubscribe) => {
unsubscribe();
},
};
};

View File

@@ -0,0 +1,36 @@
import { CrudFilters, LogicalFilter } from "@refinedev/core";
export const generateFilter = (filters?: CrudFilters) => {
const queryFilters: { [key: string]: any } = {};
if (filters) {
filters.map((filter) => {
if (
filter.operator !== "or" &&
filter.operator !== "and" &&
"field" in filter
) {
const { field, operator, value } = filter;
if (operator === "eq") {
queryFilters[`${field}`] = value;
} else {
queryFilters[`${field}_${operator}`] = value;
}
} else {
const value = filter.value as LogicalFilter[];
const orFilters: any[] = [];
value.map((val) => {
orFilters.push({
[`${val.field}_${val.operator}`]: val.value,
});
});
queryFilters["_or"] = orFilters;
}
});
}
return queryFilters;
};

View File

@@ -0,0 +1,18 @@
import { CrudSorting } from "@refinedev/core";
export const generateSort = (sorters?: CrudSorting) => {
if (sorters && sorters.length > 0) {
const sortQuery = sorters.map((i) => {
return `${i.field}:${i.order}`;
});
return sortQuery.join();
}
return [];
};
/**
* @deprecated Please use `generateSort` instead.
*/
export const genereteSort = generateSort;

View File

@@ -0,0 +1,59 @@
import {
MetaQuery,
Pagination,
CrudSorting,
CrudFilters,
} from "@refinedev/core";
import * as gql from "gql-query-builder";
import camelCase from "camelcase";
import { generateSort } from "./generateSort";
import { generateFilter } from "./generateFilter";
type GenerateUseListSubscriptionParams = {
resource: string;
meta: MetaQuery;
pagination?: Pagination;
sorters?: CrudSorting;
filters?: CrudFilters;
};
type GenerateUseListSubscriptionReturnValues = {
variables: any;
query: string;
operation: string;
};
export const generateUseListSubscription = ({
resource,
meta,
pagination,
sorters,
filters,
}: GenerateUseListSubscriptionParams): GenerateUseListSubscriptionReturnValues => {
const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {};
const sortBy = generateSort(sorters);
const filterBy = generateFilter(filters);
const camelResource = camelCase(resource);
const operation = meta.operation ?? camelResource;
const { query, variables } = gql.subscription({
operation,
variables: {
...meta.variables,
sort: sortBy,
where: { value: filterBy, type: "JSON" },
...(mode === "server"
? {
start: (current - 1) * pageSize,
limit: pageSize,
}
: {}),
},
fields: meta.fields,
});
return { query, variables, operation };
};

View File

@@ -0,0 +1,44 @@
import { BaseKey, MetaQuery } from "@refinedev/core";
import * as gql from "gql-query-builder";
import camelCase from "camelcase";
type GenerateUseManySubscriptionParams = {
resource: string;
meta: MetaQuery;
ids?: BaseKey[];
};
type GenerateUseManySubscriptionReturnValues = {
variables: any;
query: string;
operation: string;
};
export const generateUseManySubscription = ({
resource,
meta,
ids,
}: GenerateUseManySubscriptionParams): GenerateUseManySubscriptionReturnValues => {
if (!ids) {
console.error(
"[useSubscription]: `ids` is required in `params` for graphql subscriptions",
);
}
const camelResource = camelCase(resource);
const operation = meta.operation ?? camelResource;
const { query, variables } = gql.subscription({
operation,
variables: {
where: {
value: { id_in: ids },
type: "JSON",
},
},
fields: meta.fields,
});
return { query, variables, operation };
};

View File

@@ -0,0 +1,43 @@
import { MetaQuery, BaseKey } from "@refinedev/core";
import * as gql from "gql-query-builder";
import pluralize from "pluralize";
import camelCase from "camelcase";
type GenerateUseOneSubscriptionParams = {
resource: string;
meta: MetaQuery;
id?: BaseKey;
};
type GenerateUseOneSubscriptionReturnValues = {
variables: any;
query: string;
operation: string;
};
export const generateUseOneSubscription = ({
resource,
meta,
id,
}: GenerateUseOneSubscriptionParams): GenerateUseOneSubscriptionReturnValues => {
if (!id) {
console.error(
"[useSubscription]: `id` is required in `params` for graphql subscriptions",
);
}
const singularResource = pluralize.singular(resource);
const camelResource = camelCase(singularResource);
const operation = meta.operation ?? camelResource;
const { query, variables } = gql.subscription({
operation,
variables: {
id: { value: id, type: "ID", required: true },
},
fields: meta.fields,
});
return { query, variables, operation };
};

View File

@@ -0,0 +1,5 @@
export * from "./generateUseListSubscription";
export * from "./generateUseManySubscription";
export * from "./generateUseOneSubscription";
export * from "./generateSort";
export * from "./generateFilter";

View File

@@ -0,0 +1,76 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: createPostInput) {\n createPost (input: $input) {\n post { id, title, content, category { id } }\n }\n }",
variables: {
input: { data: { title: "foo", content: "bar", category: "2" } },
},
})
.reply(
200,
{
data: {
createPost: {
post: {
id: "43",
title: "foo",
content: "bar",
category: { id: "2" },
},
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 13:28:08 GMT",
"Content-Type",
"application/json",
"Content-Length",
"97",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"39ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: createPostInput) {\n createPost (input: $input) {\n post { id }\n }\n }",
variables: {
input: { data: { title: "foo", content: "bar", category: "2" } },
},
})
.reply(200, { data: { createPost: { post: { id: "44" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 13:30:33 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"295ms",
]);

View File

@@ -0,0 +1,48 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("create", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).create({
resource: "posts",
variables: {
title: "foo",
content: "bar",
category: "2",
},
meta: {
fields: [
{
operation: "post",
fields: [
"id",
"title",
"content",
{ category: ["id"] },
],
variables: {},
},
],
},
});
expect(data["id"]).toEqual("43");
expect(data["title"]).toEqual("foo");
expect(data["content"]).toEqual("bar");
expect(data["category"].id).toEqual("2");
});
it("correct response without meta", async () => {
const { data } = await dataProvider(client).create({
resource: "posts",
variables: {
title: "foo",
content: "bar",
category: "2",
},
});
expect(data["id"]).toEqual("44");
});
});

View File

@@ -0,0 +1,155 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: createPostInput) {\n createPost (input: $input) {\n post { id, title, content, category { id } }\n }\n }",
variables: {
input: { data: { title: "foo", content: "bar", category: "2" } },
},
})
.reply(
200,
{
data: {
createPost: {
post: {
id: "45",
title: "foo",
content: "bar",
category: { id: "2" },
},
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 13:45:11 GMT",
"Content-Type",
"application/json",
"Content-Length",
"97",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"350ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: createPostInput) {\n createPost (input: $input) {\n post { id, title, content, category { id } }\n }\n }",
variables: {
input: {
data: { title: "foo-2", content: "bar-2", category: "3" },
},
},
})
.reply(
200,
{
data: {
createPost: {
post: {
id: "46",
title: "foo-2",
content: "bar-2",
category: { id: "3" },
},
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 13:45:11 GMT",
"Content-Type",
"application/json",
"Content-Length",
"101",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"341ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: createPostInput) {\n createPost (input: $input) {\n post { id }\n }\n }",
variables: {
input: { data: { title: "foo", content: "bar", category: "2" } },
},
})
.reply(200, { data: { createPost: { post: { id: "48" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 14:22:07 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"298ms",
]);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: createPostInput) {\n createPost (input: $input) {\n post { id }\n }\n }",
variables: {
input: {
data: { title: "foo-2", content: "bar-2", category: "3" },
},
},
})
.reply(200, { data: { createPost: { post: { id: "47" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 14:22:07 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"302ms",
]);

View File

@@ -0,0 +1,69 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("createMany", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).createMany!({
resource: "posts",
variables: [
{
title: "foo",
content: "bar",
category: "2",
},
{
title: "foo-2",
content: "bar-2",
category: "3",
},
],
meta: {
fields: [
{
operation: "post",
fields: [
"id",
"title",
"content",
{ category: ["id"] },
],
variables: {},
},
],
},
});
expect(data[0]["id"]).toEqual("45");
expect(data[0]["title"]).toEqual("foo");
expect(data[0]["content"]).toEqual("bar");
expect(data[0]["category"].id).toEqual("2");
expect(data[1]["id"]).toEqual("46");
expect(data[1]["title"]).toEqual("foo-2");
expect(data[1]["content"]).toEqual("bar-2");
expect(data[1]["category"].id).toEqual("3");
});
it("correct response without meta", async () => {
const { data } = await dataProvider(client).createMany!({
resource: "posts",
variables: [
{
title: "foo",
content: "bar",
category: "2",
},
{
title: "foo-2",
content: "bar-2",
category: "3",
},
],
});
expect(data[0]["id"]).toEqual("48");
expect(data[1]["id"]).toEqual("47");
});
});

View File

@@ -0,0 +1,74 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($sort: String, $where: JSON) { posts (sort: $sort, where: $where) { id, title, category { id } } }",
variables: { sort: "id:asc", where: { title_contains: "foo" } },
})
.reply(
200,
[
"1f8b0800000000000003aa564a492c4954b2aa562ac82f2e2956b28aae56ca4c51b252323254d2512ac92cc94905724a0b80aa525374d3f2f3758d80e2c9405e7a7e5125481f58b5b1526dad0e4ca709769dd8f41921eb33254f9f09b24b89516f44a27a1312d59b12a73eb6b6960b000000ffff",
"0300a671041a7f010000",
],
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 11:16:31 GMT",
"Content-Type",
"application/json",
"Transfer-Encoding",
"chunked",
"Connection",
"close",
"Vary",
"Accept-Encoding",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"52ms",
"Content-Encoding",
"gzip",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id, title }\n }\n }",
variables: {
input: { where: { id: "32" }, data: { title: "custom-foo" } },
},
})
.reply(
200,
{ data: { updatePost: { post: { id: "32", title: "custom-foo" } } } },
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 11:21:50 GMT",
"Content-Type",
"application/json",
"Content-Length",
"66",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"85ms",
],
);

View File

@@ -0,0 +1,52 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("custom", () => {
it("correct get query response", async () => {
const response = await dataProvider(client).custom?.({
url: "",
method: "get",
meta: {
operation: "posts",
variables: {
sort: "id:asc",
where: { value: { title_contains: "foo" }, type: "JSON" },
},
fields: ["id", "title", { category: ["id"] }],
},
});
expect(response?.data[0].id).toBe("21");
expect(response?.data[0].title).toBe("updated-foo-2");
});
it("correct get mutation response", async () => {
const response = await dataProvider(client).custom?.({
url: "",
method: "post",
meta: {
operation: "updatePost",
variables: {
input: {
value: {
where: { id: "32" },
data: { title: "custom-foo" },
},
type: "updatePostInput",
},
},
fields: [
{
operation: "post",
fields: ["id", "title"],
variables: {},
},
],
},
});
expect(response?.data.post.id).toBe("32");
expect(response?.data.post.title).toBe("custom-foo");
});
});

View File

@@ -0,0 +1,121 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: deletePostInput) {\n deletePost (input: $input) {\n post { id, title }\n }\n }",
variables: { input: { where: { id: "37" } } },
})
.reply(
200,
{ data: { deletePost: { post: { id: "37", title: "Hello" } } } },
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 09:07:05 GMT",
"Content-Type",
"application/json",
"Content-Length",
"61",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"144ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: deletePostInput) {\n deletePost (input: $input) {\n post { id, title }\n }\n }",
variables: { input: { where: { id: "38" } } },
})
.reply(
200,
{ data: { deletePost: { post: { id: "38", title: "Loem" } } } },
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 09:07:05 GMT",
"Content-Type",
"application/json",
"Content-Length",
"60",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"129ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: deletePostInput) {\n deletePost (input: $input) {\n post { id }\n }\n }",
variables: { input: { where: { id: "34" } } },
})
.reply(200, { data: { deletePost: { post: { id: "34" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 09:08:27 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"137ms",
]);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: deletePostInput) {\n deletePost (input: $input) {\n post { id }\n }\n }",
variables: { input: { where: { id: "35" } } },
})
.reply(200, { data: { deletePost: { post: { id: "35" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 09:08:27 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"129ms",
]);

View File

@@ -0,0 +1,38 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("deleteMany", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).deleteMany!({
resource: "posts",
ids: ["37", "38"],
meta: {
fields: [
{
operation: "post",
fields: ["id", "title"],
variables: {},
},
],
},
});
expect(data[0].id).toEqual("37");
expect(data[0].title).toEqual("Hello");
expect(data[1].id).toEqual("38");
expect(data[1].title).toEqual("Loem");
});
it("correct response without meta", async () => {
const { data } = await dataProvider(client).deleteMany!({
resource: "posts",
ids: ["34", "35"],
});
expect(data[0].id).toEqual("34");
expect(data[1].id).toEqual("35");
});
});

View File

@@ -0,0 +1,61 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: deletePostInput) {\n deletePost (input: $input) {\n post { id, title }\n }\n }",
variables: { input: { where: { id: "43" } } },
})
.reply(
200,
{ data: { deletePost: { post: { id: "43", title: "foo" } } } },
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:58:32 GMT",
"Content-Type",
"application/json",
"Content-Length",
"59",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"85ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: deletePostInput) {\n deletePost (input: $input) {\n post { id }\n }\n }",
variables: { input: { where: { id: "48" } } },
})
.reply(200, { data: { deletePost: { post: { id: "48" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:39:15 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"70ms",
]);

View File

@@ -0,0 +1,33 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("deleteOne", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).deleteOne({
resource: "posts",
id: "43",
meta: {
fields: [
{
operation: "post",
fields: ["id", "title"],
variables: {},
},
],
},
});
expect(data.id).toEqual("43");
expect(data.title).toEqual("foo");
});
it("correct response without meta", async () => {
const { data } = await dataProvider(client).deleteOne({
resource: "posts",
id: "48",
});
expect(data.id).toEqual("48");
});
});

View File

@@ -0,0 +1,166 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($sort: String, $where: JSON, $start: Int, $limit: Int) { posts (sort: $sort, where: $where, start: $start, limit: $limit) { id, title } }",
variables: { where: {}, start: 0, limit: 10 },
})
.reply(
200,
[
"1f8b0800000000000003aa564a492c4954b2aa562ac82f2e2956b28aae56ca4c51b252323254d2512ac92cc94905721cf3f24b32528b14fc52cb150280ea946a7560ca8c919461913621ce1453e29499e1b7cc823853ccf19b628957dad880284b8c0db19a125b5bcb05000000ffff",
"0300af7dd9d673010000",
],
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 07:22:50 GMT",
"Content-Type",
"application/json",
"Transfer-Encoding",
"chunked",
"Connection",
"close",
"Vary",
"Accept-Encoding",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"29ms",
"Content-Encoding",
"gzip",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($sort: String, $where: JSON, $start: Int, $limit: Int) { posts (sort: $sort, where: $where, start: $start, limit: $limit) { id, title } }",
variables: { sort: "id:asc", where: {}, start: 0, limit: 10 },
})
.reply(
200,
[
"1f8b0800000000000003aa564a492c4954b2aa562ac82f2e2956b28aae56ca4c51b2523257d2512ac92cc94905b2dd8b120b320273148c956a75a0d21648d25ef9197908194b2499c4e2142042c8191ae09334c4905430429236c62f6d82cf68532449bfd4728500a067155c5273f315420b8001908aa4d40c49a9635e7e49466a91025c4b680142a591211e954ab5b1b5b55c00000000ffff",
"030048acc85b5e010000",
],
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 07:31:50 GMT",
"Content-Type",
"application/json",
"Transfer-Encoding",
"chunked",
"Connection",
"close",
"Vary",
"Accept-Encoding",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"107ms",
"Content-Encoding",
"gzip",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($sort: String, $where: JSON, $start: Int, $limit: Int) { posts (sort: $sort, where: $where, start: $start, limit: $limit) { title } }",
variables: { where: { id: "907" }, start: 0, limit: 10 },
})
.reply(
200,
{
data: {
posts: [
{
title: "Molestias iste voluptatem velit sed voluptate aut voluptatibus explicabo.",
},
],
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 04 Mar 2022 11:33:59 GMT",
"Content-Type",
"application/json",
"Content-Length",
"107",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"24ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($sort: String, $where: JSON, $start: Int, $limit: Int) { posts (sort: $sort, where: $where, start: $start, limit: $limit) { id, title, category { id, title } } }",
variables: {
sort: "title:asc",
where: { category: "8" },
start: 0,
limit: 10,
},
})
.reply(
200,
{
data: {
posts: [
{
id: "349",
title: "Illo non iusto rem distinctio sequi dolores nobis.",
category: { id: "8", title: "Test" },
},
],
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 04 Mar 2022 11:50:17 GMT",
"Content-Type",
"application/json",
"Content-Length",
"132",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"37ms",
],
);

View File

@@ -0,0 +1,79 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("getList", () => {
it("correct response", async () => {
const { data } = await dataProvider(client).getList({
resource: "posts",
meta: {
fields: ["id", "title"],
},
});
expect(data[0]["id"]).toBe("21");
expect(data[0]["title"]).toBe("Another New Post");
});
it("correct sorting response", async () => {
const { data } = await dataProvider(client).getList({
resource: "posts",
sorters: [
{
field: "id",
order: "asc",
},
],
meta: {
fields: ["id", "title"],
},
});
expect(data[0]["id"]).toBe("7");
expect(data[0]["title"]).toBe("GraphQl 3");
});
it("correct filter response", async () => {
const { data } = await dataProvider(client).getList({
resource: "posts",
filters: [
{
field: "id",
operator: "eq",
value: "907",
},
],
meta: {
fields: ["title"],
},
});
expect(data[0]["title"]).toBe(
"Molestias iste voluptatem velit sed voluptate aut voluptatibus explicabo.",
);
});
it("correct filter and sort response", async () => {
const response = await dataProvider(client).getList({
resource: "posts",
filters: [
{
field: "category",
operator: "eq",
value: "8",
},
],
sorters: [
{
field: "title",
order: "asc",
},
],
meta: {
fields: ["id", "title", { category: ["id", "title"] }],
},
});
expect(response.data[0]["id"]).toBe("349");
expect(response.data[0]["category"].title).toBe("Test");
});
});

View File

@@ -0,0 +1,50 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($where: JSON) { posts (where: $where) { id, title, content, category { id } } }",
variables: { where: { id_in: ["45", "46"] } },
})
.reply(
200,
{
data: {
posts: [
{
id: "45",
title: "foo",
content: "bar",
category: { id: "2" },
},
{
id: "46",
title: "foo-2",
content: "bar-2",
category: { id: "3" },
},
],
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 14:35:47 GMT",
"Content-Type",
"application/json",
"Content-Length",
"153",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"49ms",
],
);

View File

@@ -0,0 +1,25 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("getMany", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).getMany!({
resource: "posts",
ids: ["45", "46"],
meta: {
fields: ["id", "title", "content", { category: ["id"] }],
},
});
expect(data[0]["id"]).toEqual("45");
expect(data[0]["title"]).toEqual("foo");
expect(data[0]["content"]).toEqual("bar");
expect(data[0]["category"].id).toEqual("2");
expect(data[1]["id"]).toEqual("46");
expect(data[1]["title"]).toEqual("foo-2");
expect(data[1]["content"]).toEqual("bar-2");
expect(data[1]["category"].id).toEqual("3");
});
});

View File

@@ -0,0 +1,42 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "query ($id: ID!) { post (id: $id) { id, title, content, category { id } } }",
variables: { id: "45" },
})
.reply(
200,
{
data: {
post: {
id: "45",
title: "foo",
content: "bar",
category: { id: "2" },
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Thu, 16 Sep 2021 14:47:17 GMT",
"Content-Type",
"application/json",
"Content-Length",
"82",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"32ms",
],
);

View File

@@ -0,0 +1,20 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("useOne", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).getOne({
resource: "posts",
id: "45",
meta: {
fields: ["id", "title", "content", { category: ["id"] }],
},
});
expect(data["id"]).toEqual("45");
expect(data["title"]).toEqual("foo");
expect(data["content"]).toEqual("bar");
expect(data["category"].id).toEqual("2");
});
});

View File

@@ -0,0 +1,12 @@
import { GraphQLClient } from "graphql-request";
const API_URL = "https://api.strapi.refine.dev/graphql";
const client = new GraphQLClient(API_URL);
client.setHeader(
"Authorization",
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjQ2MzkzNDY2LCJleHAiOjE2NDg5ODU0NjZ9.5TjmTOLL7x7kcNKpq9MFwI_w1fReF4f-wlir2rocvi8",
);
export default client;

View File

@@ -0,0 +1,6 @@
import nock from "nock";
afterAll(() => {
nock.cleanAll();
nock.restore();
});

View File

@@ -0,0 +1,90 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id, title, content, category { id } }\n }\n }",
variables: {
input: {
where: { id: "21" },
data: {
title: "updated-foo",
content: "updated-bar",
category: "2",
},
},
},
})
.reply(
200,
{
data: {
updatePost: {
post: {
id: "21",
title: "updated-foo",
content: "updated-bar",
category: { id: "2" },
},
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:18:44 GMT",
"Content-Type",
"application/json",
"Content-Length",
"113",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"157ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id }\n }\n }",
variables: {
input: {
where: { id: "21" },
data: {
title: "updated-foo-2",
content: "updated-bar-2",
category: "3",
},
},
},
})
.reply(200, { data: { updatePost: { post: { id: "21" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:20:36 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"454ms",
]);

View File

@@ -0,0 +1,49 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("create", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).update({
resource: "posts",
id: "21",
variables: {
title: "updated-foo",
content: "updated-bar",
category: "2",
},
meta: {
fields: [
{
operation: "post",
fields: [
"id",
"title",
"content",
{ category: ["id"] },
],
variables: {},
},
],
},
});
expect(data["title"]).toEqual("updated-foo");
expect(data["content"]).toEqual("updated-bar");
expect(data["category"].id).toEqual("2");
});
it("correct response without meta", async () => {
const { data } = await dataProvider(client).update({
resource: "posts",
id: "21",
variables: {
title: "updated-foo-2",
content: "updated-bar-2",
category: "3",
},
});
expect(data["id"]).toEqual("21");
});
});

View File

@@ -0,0 +1,179 @@
import nock from "nock";
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id, title, content, category { id } }\n }\n }",
variables: {
input: {
where: { id: "24" },
data: {
title: "updated-foo",
content: "updated-bar",
category: "2",
},
},
},
})
.reply(
200,
{
data: {
updatePost: {
post: {
id: "24",
title: "updated-foo",
content: "updated-bar",
category: { id: "2" },
},
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:35:00 GMT",
"Content-Type",
"application/json",
"Content-Length",
"113",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"326ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id, title, content, category { id } }\n }\n }",
variables: {
input: {
where: { id: "25" },
data: {
title: "updated-foo",
content: "updated-bar",
category: "2",
},
},
},
})
.reply(
200,
{
data: {
updatePost: {
post: {
id: "25",
title: "updated-foo",
content: "updated-bar",
category: { id: "2" },
},
},
},
},
[
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:35:00 GMT",
"Content-Type",
"application/json",
"Content-Length",
"113",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"324ms",
],
);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id }\n }\n }",
variables: {
input: {
where: { id: "24" },
data: {
title: "updated-foo-2",
content: "updated-bar-2",
category: "3",
},
},
},
})
.reply(200, { data: { updatePost: { post: { id: "24" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:32:03 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"372ms",
]);
nock("https://api.strapi.refine.dev:443", { encodedQueryParams: true })
.post("/graphql", {
query: "mutation ($input: updatePostInput) {\n updatePost (input: $input) {\n post { id }\n }\n }",
variables: {
input: {
where: { id: "25" },
data: {
title: "updated-foo-2",
content: "updated-bar-2",
category: "3",
},
},
},
})
.reply(200, { data: { updatePost: { post: { id: "25" } } } }, [
"Server",
"nginx/1.17.10",
"Date",
"Fri, 17 Sep 2021 08:32:03 GMT",
"Content-Type",
"application/json",
"Content-Length",
"45",
"Connection",
"close",
"Vary",
"Origin",
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
"X-Frame-Options",
"SAMEORIGIN",
"X-Powered-By",
"Strapi <strapi.io>",
"X-Response-Time",
"442ms",
]);

View File

@@ -0,0 +1,57 @@
import dataProvider from "../../src/index";
import client from "../gqlClient";
import "./index.mock";
describe("updateMany", () => {
it("correct response with meta", async () => {
const { data } = await dataProvider(client).updateMany!({
resource: "posts",
ids: ["24", "25"],
variables: {
title: "updated-foo",
content: "updated-bar",
category: "2",
},
meta: {
fields: [
{
operation: "post",
fields: [
"id",
"title",
"content",
{ category: ["id"] },
],
variables: {},
},
],
},
});
expect(data[0]["id"]).toEqual("24");
expect(data[0]["title"]).toEqual("updated-foo");
expect(data[0]["content"]).toEqual("updated-bar");
expect(data[0]["category"].id).toEqual("2");
expect(data[1]["id"]).toEqual("25");
expect(data[1]["title"]).toEqual("updated-foo");
expect(data[1]["content"]).toEqual("updated-bar");
expect(data[1]["category"].id).toEqual("2");
});
it("correct response without meta", async () => {
const { data } = await dataProvider(client).updateMany!({
resource: "posts",
ids: ["24", "25"],
variables: {
title: "updated-foo-2",
content: "updated-bar-2",
category: "3",
},
});
expect(data[0]["id"]).toEqual("24");
expect(data[1]["id"]).toEqual("25");
});
});

View File

@@ -0,0 +1,32 @@
import { CrudFilters } from "@refinedev/core";
import { generateFilter } from "../../src/utils";
describe("generateFilter", () => {
it("should return an empty object if no filters are provided", () => {
expect(generateFilter()).toEqual({});
});
it("should return correct queryFilters for given filters", () => {
const filters: CrudFilters = [
{ field: "name", operator: "eq", value: "John" },
{ field: "age", operator: "gte", value: 18 },
{ field: "city", operator: "ne", value: "New York" },
{
operator: "or",
value: [
{ field: "status", operator: "eq", value: "active" },
{ field: "status", operator: "eq", value: "pending" },
],
},
];
const expectedResult = {
name: "John",
age_gte: 18,
city_ne: "New York",
_or: [{ status_eq: "active" }, { status_eq: "pending" }],
};
expect(generateFilter(filters)).toEqual(expectedResult);
});
});

View File

@@ -0,0 +1,51 @@
import { CrudSorting } from "@refinedev/core";
import { generateSort, genereteSort } from "../../src/utils";
describe("generateSort", () => {
it.each([undefined, null, []])(
"should return empty array when sorters is %p",
(sorters) => {
expect(generateSort(sorters as CrudSorting)).toEqual([]);
},
);
it("should return correct sort query for single sorter", () => {
const sorters: CrudSorting = [
{
field: "name",
order: "asc",
},
];
expect(generateSort(sorters)).toEqual("name:asc");
});
it("should return correct sort query for multiple sorters", () => {
const sorters: CrudSorting = [
{
field: "name",
order: "asc",
},
{
field: "age",
order: "desc",
},
];
expect(generateSort(sorters)).toEqual("name:asc,age:desc");
});
});
describe("genereteSort (deprecated)", () => {
it("should be deprecated and equal to generateSort", () => {
const sorters: CrudSorting = [
{
field: "name",
order: "asc",
},
{
field: "age",
order: "desc",
},
];
expect(genereteSort(sorters)).toEqual(generateSort(sorters));
});
});

View File

@@ -0,0 +1,91 @@
import {
CrudFilters,
CrudSorting,
MetaQuery,
Pagination,
} from "@refinedev/core";
import { generateUseListSubscription } from "../../src/utils";
describe("generateUseListSubscription", () => {
const resource = "exampleResource";
const meta: MetaQuery = {
operation: "exampleOperation",
fields: ["id", "name"],
};
const pagination: Pagination = { current: 1, pageSize: 10, mode: "server" };
const sorters: CrudSorting = [{ field: "name", order: "asc" }];
const filters: CrudFilters = [
{ field: "name", operator: "contains", value: "John" },
];
it("should generate a subscription with the provided parameters", () => {
const { query, variables, operation } = generateUseListSubscription({
resource,
meta,
pagination,
sorters,
filters,
});
expect(operation).toEqual(meta.operation);
expect(query).toContain(meta.operation);
expect(query).toMatch(/id/);
expect(query).toMatch(/name/);
expect(variables).toEqual({
sort: "name:asc",
where: { name_contains: "John" },
start: 0,
limit: 10,
});
});
it("should generate a subscription without pagination, sorters and filters", () => {
const { query, variables, operation } = generateUseListSubscription({
resource,
meta,
});
expect(operation).toEqual(meta.operation);
expect(query).toContain(meta.operation);
expect(query).toMatch(/id/);
expect(query).toMatch(/name/);
expect(variables).toEqual({
sort: undefined,
where: {},
start: 0,
limit: 10,
});
});
it("should generate a subscription without pagination when mode is client ", () => {
const { variables } = generateUseListSubscription({
resource,
meta,
pagination: { ...pagination, mode: "client" },
sorters,
filters,
});
expect(variables).toEqual({
sort: "name:asc",
where: { name_contains: "John" },
});
});
it("should generate a subscription with resource when meta.operation is undefined", () => {
const { query, operation } = generateUseListSubscription({
resource: "example-resource",
meta: { ...meta, operation: undefined },
pagination,
sorters,
filters,
});
expect(operation).toEqual("exampleResource");
expect(query).toContain("exampleResource");
});
});

View File

@@ -0,0 +1,50 @@
import { generateUseManySubscription } from "../../src/utils";
describe("generateUseManySubscription", () => {
const resource = "exampleResource";
const meta = {
operation: "exampleOperation",
fields: ["id", "name"],
};
const ids = [1, 2, 3];
it("should generate a subscription with the provided parameters", () => {
const { query, variables, operation } = generateUseManySubscription({
resource,
meta,
ids,
});
expect(operation).toEqual(meta.operation);
expect(query).toContain(meta.operation);
expect(query).toMatch(/id/);
expect(query).toMatch(/name/);
expect(variables).toEqual({
where: { id_in: [1, 2, 3] },
});
});
it("should log an error when ids is not provided", () => {
console.error = jest.fn();
generateUseManySubscription({
resource,
meta,
});
expect(console.error).toHaveBeenCalledWith(
"[useSubscription]: `ids` is required in `params` for graphql subscriptions",
);
});
it("should generate a subscription with resource when meta.operation is undefined", () => {
const { query, operation } = generateUseManySubscription({
resource: "example-resource",
meta: { ...meta, operation: undefined },
});
expect(operation).toEqual("exampleResource");
expect(query).toContain("exampleResource");
});
});

View File

@@ -0,0 +1,49 @@
import { generateUseOneSubscription } from "../../src/utils";
describe("generateUseOneSubscription", () => {
const resource = "exampleResources";
const meta = {
operation: "exampleOperation",
fields: ["id", "name"],
};
const id = 1;
it("should generate a subscription with the provided parameters", () => {
const { query, variables, operation } = generateUseOneSubscription({
resource,
meta,
id,
});
expect(operation).toEqual(meta.operation);
expect(query).toContain(meta.operation);
expect(query).toMatch(/id/);
expect(query).toMatch(/name/);
expect(variables).toEqual({
id: 1,
});
});
it("should log an error when id is not provided", () => {
console.error = jest.fn();
generateUseOneSubscription({
resource,
meta,
});
expect(console.error).toHaveBeenCalledWith(
"[useSubscription]: `id` is required in `params` for graphql subscriptions",
);
});
it("should generate a subscription with resource when meta.operation is undefined", () => {
const { query, operation } = generateUseOneSubscription({
resource: "example-resource",
meta: { ...meta, operation: undefined },
});
expect(operation).toEqual("exampleResource");
expect(query).toContain("exampleResource");
});
});

View File

@@ -0,0 +1,21 @@
{
"extends": "./tsconfig.json",
"exclude": [
"node_modules",
"dist",
"test",
"../test/**/*",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx"
],
"compilerOptions": {
"outDir": "dist",
"declarationDir": "dist",
"declaration": true,
"emitDeclarationOnly": true,
"noEmit": false,
"declarationMap": true
}
}

View File

@@ -0,0 +1,11 @@
{
"include": [
"src",
"types"
],
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"rootDir": "./src",
"baseUrl": ".",
}
}

View File

@@ -0,0 +1,24 @@
import { defineConfig } from "tsup";
import { NodeResolvePlugin } from "@esbuild-plugins/node-resolve";
export default defineConfig({
entry: ["src/index.ts"],
splitting: false,
sourcemap: true,
clean: false,
platform: "browser",
esbuildPlugins: [
NodeResolvePlugin({
extensions: [".js", "ts", "tsx", "jsx"],
onResolved: (resolved) => {
if (resolved.includes("node_modules")) {
return {
external: true,
};
}
return resolved;
},
}),
],
onSuccess: "tsc --project tsconfig.declarations.json",
});