@svadmin/hasura 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +5 -2
  2. package/src/data-provider.ts +15 -162
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/hasura",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Hasura GraphQL DataProvider for svadmin",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -16,7 +16,10 @@
16
16
  }
17
17
  },
18
18
  "peerDependencies": {
19
- "@svadmin/core": "^0.19.4"
19
+ "@svadmin/core": "^0.21.1",
20
+ "@svadmin/refine-adapter": "^0.9.3",
21
+ "@refinedev/hasura": "^5.0.0",
22
+ "graphql-request": "^6.1.0"
20
23
  },
21
24
  "license": "MIT",
22
25
  "repository": {
@@ -1,163 +1,16 @@
1
- import type {
2
- DataProvider, GetListParams, GetListResult, GetOneParams, GetOneResult,
3
- CreateParams, CreateResult, UpdateParams, UpdateResult, DeleteParams, DeleteResult,
4
- GetManyParams, GetManyResult, CustomParams, CustomResult, Sort, Filter, BaseRecord
5
- } from '@svadmin/core';
6
-
7
- // Maps core filter operators to Hasura GraphQL query operators
8
- function mapOperator(operator: string): string {
9
- switch (operator) {
10
- case 'eq': return '_eq';
11
- case 'ne': return '_neq';
12
- case 'lt': return '_lt';
13
- case 'gt': return '_gt';
14
- case 'lte': return '_lte';
15
- case 'gte': return '_gte';
16
- case 'contains': return '_ilike';
17
- case 'in': return '_in';
18
- case 'null': return '_is_null';
19
- default: return '_eq';
20
- }
21
- }
22
-
23
- // Minimal GraphQL Executor
24
- async function executeGraphQL(endpoint: string, query: string, variables: Record<string, unknown> = {}, headers: Record<string, string> = {}): Promise<Record<string, unknown>> {
25
- const response = await fetch(endpoint, {
26
- method: 'POST',
27
- headers: { 'Content-Type': 'application/json', ...headers },
28
- body: JSON.stringify({ query, variables }),
29
- });
30
- const json = await response.json();
31
- if (json.errors) {
32
- throw new Error((json.errors as Array<{ message: string }>).map((e) => e.message).join('\n'));
33
- }
34
- return json.data;
35
- }
36
-
37
- export function createHasuraDataProvider(endpoint: string, defaultHeaders: Record<string, string> = {}): DataProvider {
38
- return {
39
- getApiUrl: () => endpoint,
40
-
41
- async getList<T extends BaseRecord = BaseRecord>({ resource, pagination, sorters, filters, meta }: GetListParams): Promise<GetListResult<T>> {
42
- const { current = 1, pageSize = 10, mode } = pagination ?? {};
43
- const offset = mode === 'server' ? (current - 1) * pageSize : 0;
44
- const limit = mode === 'server' ? pageSize : undefined;
45
-
46
- // Simplistic AST builder for demonstration. In production, 'graphql-request' or gql AST tools should be used.
47
- const variables: Record<string, unknown> = { limit, offset };
48
-
49
- let whereClause = '';
50
- if (filters && filters.length > 0) {
51
- const clauses = filters.filter((f): f is Filter & { field: string; operator: string; value: unknown } => 'field' in f).map((f, i: number) => {
52
- variables[`f${i}`] = f.operator === 'contains' ? `%${f.value}%` : f.value;
53
- return `${f.field}: { ${mapOperator(f.operator)}: $f${i} }`;
54
- });
55
- whereClause = clauses.length ? `where: { ${clauses.join(', ')} },` : '';
56
- }
57
-
58
- let orderByClause = '';
59
- if (sorters && sorters.length > 0) {
60
- const orders = sorters.map((s: Sort) => `${s.field}: ${s.order}`);
61
- orderByClause = `order_by: { ${orders.join(', ')} },`;
62
- }
63
-
64
- const fields = (meta?.fields as string[])?.join(' ') || 'id';
65
-
66
- const query = `
67
- query getList($limit: Int, $offset: Int, ${Object.keys(variables).filter(k => k.startsWith('f')).map(k => `$${k}: String`).join(', ')}) {
68
- ${resource}(limit: $limit, offset: $offset, ${whereClause} ${orderByClause}) {
69
- ${fields}
70
- }
71
- ${resource}_aggregate {
72
- aggregate { count }
73
- }
74
- }
75
- `;
76
-
77
- const data = await executeGraphQL(endpoint, query, variables, defaultHeaders);
78
- const resourceData = data[resource] as unknown as unknown as T[] | undefined;
79
- const aggregateData = data[`${resource}_aggregate`] as { aggregate?: { count?: number } } | undefined;
80
- return {
81
- data: resourceData || [],
82
- total: aggregateData?.aggregate?.count || 0,
83
- };
84
- },
85
-
86
- async getOne<T extends BaseRecord = BaseRecord>({ resource, id, meta }: GetOneParams): Promise<GetOneResult<T>> {
87
- const fields = (meta?.fields as string[])?.join(' ') || 'id';
88
- const query = `
89
- query getOne($id: uuid!) {
90
- ${resource}_by_pk(id: $id) {
91
- ${fields}
92
- }
93
- }
94
- `;
95
- const data = await executeGraphQL(endpoint, query, { id }, defaultHeaders);
96
- return { data: data[`${resource}_by_pk`] as unknown as unknown as T };
97
- },
98
-
99
- async create<T extends BaseRecord = BaseRecord>({ resource, variables, meta }: CreateParams): Promise<CreateResult<T>> {
100
- const fields = (meta?.fields as string[])?.join(' ') || 'id';
101
- const query = `
102
- mutation create($object: ${resource}_insert_input!) {
103
- insert_${resource}_one(object: $object) {
104
- ${fields}
105
- }
106
- }
107
- `;
108
- const data = await executeGraphQL(endpoint, query, { object: variables }, defaultHeaders);
109
- return { data: data[`insert_${resource}_one`] as unknown as unknown as T };
110
- },
111
-
112
- async update<T extends BaseRecord = BaseRecord>({ resource, id, variables, meta }: UpdateParams): Promise<UpdateResult<T>> {
113
- const fields = (meta?.fields as string[])?.join(' ') || 'id';
114
- const query = `
115
- mutation update($id: uuid!, $set: ${resource}_set_input!) {
116
- update_${resource}_by_pk(pk_columns: {id: $id}, _set: $set) {
117
- ${fields}
118
- }
119
- }
120
- `;
121
- const data = await executeGraphQL(endpoint, query, { id, set: variables }, defaultHeaders);
122
- return { data: data[`update_${resource}_by_pk`] as unknown as unknown as T };
123
- },
124
-
125
- async deleteOne<T extends BaseRecord = BaseRecord>({ resource, id, meta }: DeleteParams): Promise<DeleteResult<T>> {
126
- const fields = (meta?.fields as string[])?.join(' ') || 'id';
127
- const query = `
128
- mutation deleteOne($id: uuid!) {
129
- delete_${resource}_by_pk(id: $id) {
130
- ${fields}
131
- }
132
- }
133
- `;
134
- const data = await executeGraphQL(endpoint, query, { id }, defaultHeaders);
135
- return { data: data[`delete_${resource}_by_pk`] as unknown as unknown as T };
136
- },
137
-
138
- async getMany<T extends BaseRecord = BaseRecord>({ resource, ids, meta }: GetManyParams): Promise<GetManyResult<T>> {
139
- const fields = (meta?.fields as string[])?.join(' ') || 'id';
140
- const query = `
141
- query getMany($ids: [uuid!]!) {
142
- ${resource}(where: {id: {_in: $ids}}) {
143
- ${fields}
144
- }
145
- }
146
- `;
147
- const data = await executeGraphQL(endpoint, query, { ids }, defaultHeaders);
148
- return { data: (data[resource] as unknown as unknown as T[]) || [] };
149
- },
150
-
151
- async custom<T = unknown>({ url, method, payload, headers }: CustomParams): Promise<CustomResult<T>> {
152
- // By default Hasura only understands GraphQL via POST, but arbitrary custom calls might be used for REST endpoints mapping.
153
- const response = await fetch(url, {
154
- method: method.toUpperCase(),
155
- headers: { 'Content-Type': 'application/json', ...defaultHeaders, ...headers },
156
- body: payload ? JSON.stringify(payload) : undefined,
157
- });
158
- if (!response.ok) throw new Error(`Custom request failed: ${response.status}`);
159
- const data = await response.json();
160
- return { data: data as unknown as unknown as T };
161
- },
162
- };
1
+ import type { DataProvider } from '@svadmin/core';
2
+ import { createRefineAdapter } from '@svadmin/refine-adapter';
3
+ import dataProvider from '@refinedev/hasura';
4
+
5
+ /**
6
+ * Creates a Hasura data provider using the official @refinedev/hasura package.
7
+ * Requires `graphql-request` and `@refinedev/hasura` to be installed.
8
+ *
9
+ * @param client The GraphQLClient instance
10
+ * @param options Optional configuration for the Hasura data provider
11
+ * @returns A fully compatible svadmin DataProvider
12
+ */
13
+ export function createHasuraDataProvider(client: any, options?: any): DataProvider {
14
+ const refineHasuraProvider = dataProvider(client, options);
15
+ return createRefineAdapter(refineHasuraProvider);
163
16
  }