@svadmin/hasura 0.9.3 → 0.9.5
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.
- package/package.json +5 -2
- package/src/data-provider.ts +17 -162
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/hasura",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
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
|
+
"@svadmin/core": "^0.21.2",
|
|
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": {
|
package/src/data-provider.ts
CHANGED
|
@@ -1,163 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a Hasura data provider using the official @refinedev/hasura package.
|
|
6
|
+
* Requires `graphql-request` and `@refinedev/hasura` to be installed.
|
|
7
|
+
*
|
|
8
|
+
* @param client The GraphQLClient instance
|
|
9
|
+
* @param options Optional configuration for the Hasura data provider
|
|
10
|
+
* @returns A fully compatible svadmin DataProvider
|
|
11
|
+
*/
|
|
12
|
+
export async function createHasuraDataProvider(client: any, options?: any): Promise<DataProvider> {
|
|
13
|
+
// @ts-ignore Peer dependency
|
|
14
|
+
const pkg = (await import('@refinedev/hasura')) as any;
|
|
15
|
+
const init: any = pkg.default || pkg.dataProvider || pkg.DataProvider;
|
|
16
|
+
const refineHasuraProvider = init(client, options);
|
|
17
|
+
return createRefineAdapter(refineHasuraProvider);
|
|
163
18
|
}
|