@raphiniert/ra-data-postgrest 2.0.0-alpha.1 → 2.0.0-alpha.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.
- package/CHANGELOG.md +49 -0
- package/IDEAS.md +46 -0
- package/README.md +50 -17
- package/esm/index.d.ts +58 -7
- package/esm/index.js +220 -219
- package/esm/urlBuilder.d.ts +4 -4
- package/esm/urlBuilder.js +95 -28
- package/lib/index.d.ts +58 -7
- package/lib/index.js +221 -218
- package/lib/urlBuilder.d.ts +4 -4
- package/lib/urlBuilder.js +102 -35
- package/package.json +10 -10
- package/src/index.ts +208 -138
- package/src/urlBuilder.ts +96 -31
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
fetchUtils,
|
|
3
2
|
DataProvider,
|
|
4
3
|
GetListParams,
|
|
5
4
|
GetOneParams,
|
|
@@ -17,12 +16,11 @@ import {
|
|
|
17
16
|
getPrimaryKey,
|
|
18
17
|
parseFilters,
|
|
19
18
|
getOrderBy,
|
|
20
|
-
|
|
19
|
+
dataWithVirtualId,
|
|
20
|
+
dataWithoutVirtualId,
|
|
21
|
+
removePrimaryKey,
|
|
21
22
|
getQuery,
|
|
22
|
-
getKeyData,
|
|
23
23
|
encodeId,
|
|
24
|
-
decodeId,
|
|
25
|
-
isCompoundKey,
|
|
26
24
|
} from './urlBuilder';
|
|
27
25
|
import qs from 'qs';
|
|
28
26
|
|
|
@@ -48,13 +46,24 @@ import qs from 'qs';
|
|
|
48
46
|
* @example
|
|
49
47
|
*
|
|
50
48
|
* import * as React from 'react';
|
|
51
|
-
* import { Admin, Resource } from 'react-admin';
|
|
52
|
-
* import postgrestRestProvider
|
|
49
|
+
* import { Admin, Resource, fetchUtils } from 'react-admin';
|
|
50
|
+
* import postgrestRestProvider,
|
|
51
|
+
* { IDataProviderConfig,
|
|
52
|
+
* defaultPrimaryKeys,
|
|
53
|
+
* defaultSchema } from '@raphiniert/ra-data-postgrest';
|
|
53
54
|
*
|
|
54
55
|
* import { PostList } from './posts';
|
|
55
56
|
*
|
|
57
|
+
* const config: IDataProviderConfig = {
|
|
58
|
+
* apiUrl: 'http://path.to.my.api/',
|
|
59
|
+
* httpClient: fetchUtils.fetchJson,
|
|
60
|
+
* defaultListOp: 'eq',
|
|
61
|
+
* primaryKeys: defaultPrimaryKeys,
|
|
62
|
+
* schema: defaultSchema
|
|
63
|
+
* }
|
|
64
|
+
*
|
|
56
65
|
* const App = () => (
|
|
57
|
-
* <Admin dataProvider={postgrestRestProvider(
|
|
66
|
+
* <Admin dataProvider={postgrestRestProvider(config)}>
|
|
58
67
|
* <Resource name="posts" list={PostList} />
|
|
59
68
|
* </Admin>
|
|
60
69
|
* );
|
|
@@ -62,20 +71,50 @@ import qs from 'qs';
|
|
|
62
71
|
* export default App;
|
|
63
72
|
*/
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
export
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
export type MetaOption = string | undefined;
|
|
75
|
+
|
|
76
|
+
export const defaultPrimaryKeys = new Map<string, PrimaryKey>();
|
|
77
|
+
|
|
78
|
+
export const defaultSchema = () => '';
|
|
79
|
+
|
|
80
|
+
export interface IDataProviderConfig {
|
|
81
|
+
apiUrl: string;
|
|
82
|
+
httpClient: (string, Options) => Promise<any>;
|
|
83
|
+
defaultListOp: PostgRestOperator;
|
|
84
|
+
primaryKeys: Map<string, PrimaryKey>;
|
|
85
|
+
schema: () => string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const useCustomSchema = (
|
|
89
|
+
schema: () => string,
|
|
90
|
+
metaSchema: MetaOption,
|
|
91
|
+
method: string
|
|
92
|
+
) => {
|
|
93
|
+
let funcHeaderSchema = schema;
|
|
94
|
+
if (metaSchema !== undefined) {
|
|
95
|
+
funcHeaderSchema = () => metaSchema;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (funcHeaderSchema().length > 0) {
|
|
99
|
+
let schemaHeader = '';
|
|
100
|
+
if (['GET', 'HEAD'].includes(method)) {
|
|
101
|
+
schemaHeader = 'Accept-Profile';
|
|
102
|
+
} else if (['POST', 'PATCH', 'PUT', 'DELETE'].includes(method)) {
|
|
103
|
+
schemaHeader = 'Content-Profile';
|
|
104
|
+
} else return {};
|
|
105
|
+
|
|
106
|
+
return { [schemaHeader]: funcHeaderSchema() };
|
|
107
|
+
} else return {};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export default (config: IDataProviderConfig): DataProvider => ({
|
|
73
111
|
getList: (resource, params: Partial<GetListParams> = {}) => {
|
|
74
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
112
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
75
113
|
|
|
76
114
|
const { page, perPage } = params.pagination;
|
|
77
115
|
const { field, order } = params.sort || {};
|
|
78
|
-
const { filter, select } = parseFilters(params, defaultListOp);
|
|
116
|
+
const { filter, select } = parseFilters(params, config.defaultListOp);
|
|
117
|
+
const metaSchema = params.meta?.schema;
|
|
79
118
|
|
|
80
119
|
let query = {
|
|
81
120
|
offset: String((page - 1) * perPage),
|
|
@@ -98,12 +137,13 @@ export default (
|
|
|
98
137
|
Accept: 'application/json',
|
|
99
138
|
Prefer: 'count=exact',
|
|
100
139
|
...(params.meta?.headers || {}),
|
|
140
|
+
...useCustomSchema(config.schema, metaSchema, 'GET'),
|
|
101
141
|
}),
|
|
102
142
|
};
|
|
103
143
|
|
|
104
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
144
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
105
145
|
|
|
106
|
-
return httpClient(url, options).then(({ headers, json }) => {
|
|
146
|
+
return config.httpClient(url, options).then(({ headers, json }) => {
|
|
107
147
|
if (!headers.has('content-range')) {
|
|
108
148
|
throw new Error(
|
|
109
149
|
`The Content-Range header is missing in the HTTP Response. The postgREST data provider expects
|
|
@@ -112,7 +152,7 @@ export default (
|
|
|
112
152
|
);
|
|
113
153
|
}
|
|
114
154
|
return {
|
|
115
|
-
data: json.map(obj =>
|
|
155
|
+
data: json.map(obj => dataWithVirtualId(obj, primaryKey)),
|
|
116
156
|
total: parseInt(
|
|
117
157
|
headers.get('content-range').split('/').pop(),
|
|
118
158
|
10
|
|
@@ -123,32 +163,44 @@ export default (
|
|
|
123
163
|
|
|
124
164
|
getOne: (resource, params: Partial<GetOneParams> = {}) => {
|
|
125
165
|
const { id, meta } = params;
|
|
126
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
166
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
127
167
|
|
|
128
168
|
const query = getQuery(primaryKey, id, resource, meta);
|
|
129
169
|
|
|
130
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
170
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
171
|
+
const metaSchema = params.meta?.schema;
|
|
131
172
|
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
173
|
+
return config
|
|
174
|
+
.httpClient(url, {
|
|
175
|
+
headers: new Headers({
|
|
176
|
+
accept: 'application/vnd.pgrst.object+json',
|
|
177
|
+
...(params.meta?.headers || {}),
|
|
178
|
+
...useCustomSchema(config.schema, metaSchema, 'GET'),
|
|
179
|
+
}),
|
|
180
|
+
})
|
|
181
|
+
.then(({ json }) => ({
|
|
182
|
+
data: dataWithVirtualId(json, primaryKey),
|
|
183
|
+
}));
|
|
140
184
|
},
|
|
141
185
|
|
|
142
186
|
getMany: (resource, params: Partial<GetManyParams> = {}) => {
|
|
143
187
|
const ids = params.ids;
|
|
144
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
188
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
145
189
|
|
|
146
190
|
const query = getQuery(primaryKey, ids, resource, params.meta);
|
|
147
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
148
191
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
192
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
193
|
+
const metaSchema = params.meta?.schema;
|
|
194
|
+
|
|
195
|
+
return config
|
|
196
|
+
.httpClient(url, {
|
|
197
|
+
headers: new Headers({
|
|
198
|
+
...useCustomSchema(config.schema, metaSchema, 'GET'),
|
|
199
|
+
}),
|
|
200
|
+
})
|
|
201
|
+
.then(({ json }) => ({
|
|
202
|
+
data: json.map(data => dataWithVirtualId(data, primaryKey)),
|
|
203
|
+
}));
|
|
152
204
|
},
|
|
153
205
|
|
|
154
206
|
getManyReference: (
|
|
@@ -158,8 +210,10 @@ export default (
|
|
|
158
210
|
const { page, perPage } = params.pagination;
|
|
159
211
|
const { field, order } = params.sort;
|
|
160
212
|
|
|
161
|
-
const { filter, select } = parseFilters(params, defaultListOp);
|
|
162
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
213
|
+
const { filter, select } = parseFilters(params, config.defaultListOp);
|
|
214
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
215
|
+
|
|
216
|
+
const metaSchema = params.meta?.schema;
|
|
163
217
|
|
|
164
218
|
let query = params.target
|
|
165
219
|
? {
|
|
@@ -186,12 +240,13 @@ export default (
|
|
|
186
240
|
Accept: 'application/json',
|
|
187
241
|
Prefer: 'count=exact',
|
|
188
242
|
...(params.meta?.headers || {}),
|
|
243
|
+
...useCustomSchema(config.schema, metaSchema, 'GET'),
|
|
189
244
|
}),
|
|
190
245
|
};
|
|
191
246
|
|
|
192
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
247
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
193
248
|
|
|
194
|
-
return httpClient(url, options).then(({ headers, json }) => {
|
|
249
|
+
return config.httpClient(url, options).then(({ headers, json }) => {
|
|
195
250
|
if (!headers.has('content-range')) {
|
|
196
251
|
throw new Error(
|
|
197
252
|
`The Content-Range header is missing in the HTTP Response. The postgREST data provider expects
|
|
@@ -200,7 +255,7 @@ export default (
|
|
|
200
255
|
);
|
|
201
256
|
}
|
|
202
257
|
return {
|
|
203
|
-
data: json.map(data =>
|
|
258
|
+
data: json.map(data => dataWithVirtualId(data, primaryKey)),
|
|
204
259
|
total: parseInt(
|
|
205
260
|
headers.get('content-range').split('/').pop(),
|
|
206
261
|
10
|
|
@@ -211,128 +266,143 @@ export default (
|
|
|
211
266
|
|
|
212
267
|
update: (resource, params: Partial<UpdateParams> = {}) => {
|
|
213
268
|
const { id, data, meta } = params;
|
|
214
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
215
|
-
|
|
269
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
216
270
|
const query = getQuery(primaryKey, id, resource, meta);
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
271
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
272
|
+
const metaSchema = params.meta?.schema;
|
|
221
273
|
|
|
222
274
|
const body = JSON.stringify({
|
|
223
|
-
...
|
|
224
|
-
|
|
275
|
+
...dataWithoutVirtualId(
|
|
276
|
+
removePrimaryKey(data, primaryKey),
|
|
277
|
+
primaryKey
|
|
278
|
+
),
|
|
225
279
|
});
|
|
226
280
|
|
|
227
|
-
return
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
281
|
+
return config
|
|
282
|
+
.httpClient(url, {
|
|
283
|
+
method: 'PATCH',
|
|
284
|
+
headers: new Headers({
|
|
285
|
+
Accept: 'application/vnd.pgrst.object+json',
|
|
286
|
+
Prefer: 'return=representation',
|
|
287
|
+
'Content-Type': 'application/json',
|
|
288
|
+
...(params.meta?.headers || {}),
|
|
289
|
+
...useCustomSchema(config.schema, metaSchema, 'PATCH'),
|
|
290
|
+
}),
|
|
291
|
+
body,
|
|
292
|
+
})
|
|
293
|
+
.then(({ json }) => ({
|
|
294
|
+
data: dataWithVirtualId(json, primaryKey),
|
|
295
|
+
}));
|
|
237
296
|
},
|
|
238
297
|
|
|
239
298
|
updateMany: (resource, params: Partial<UpdateManyParams> = {}) => {
|
|
240
|
-
const ids = params
|
|
241
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
primaryKey.forEach((key, index) => {
|
|
251
|
-
primaryKeyData[key] = primaryKeyParams[index];
|
|
252
|
-
});
|
|
253
|
-
} else {
|
|
254
|
-
primaryKeyData[primaryKey[0]] = primaryKeyParams[0];
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
...data,
|
|
259
|
-
...primaryKeyData,
|
|
260
|
-
};
|
|
261
|
-
})
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
const url = `${apiUrl}/${resource}`;
|
|
299
|
+
const { ids, meta, data } = params;
|
|
300
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
301
|
+
const query = getQuery(primaryKey, ids, resource, meta);
|
|
302
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
303
|
+
const body = JSON.stringify({
|
|
304
|
+
...dataWithoutVirtualId(
|
|
305
|
+
removePrimaryKey(data, primaryKey),
|
|
306
|
+
primaryKey
|
|
307
|
+
),
|
|
308
|
+
});
|
|
265
309
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
310
|
+
const metaSchema = params.meta?.schema;
|
|
311
|
+
|
|
312
|
+
return config
|
|
313
|
+
.httpClient(url, {
|
|
314
|
+
method: 'PATCH',
|
|
315
|
+
headers: new Headers({
|
|
316
|
+
Prefer: 'return=representation',
|
|
317
|
+
'Content-Type': 'application/json',
|
|
318
|
+
...(params.meta?.headers || {}),
|
|
319
|
+
...useCustomSchema(config.schema, metaSchema, 'PATCH'),
|
|
320
|
+
}),
|
|
321
|
+
body,
|
|
322
|
+
})
|
|
323
|
+
.then(({ json }) => ({
|
|
324
|
+
data: json.map(data => encodeId(data, primaryKey)),
|
|
325
|
+
}));
|
|
277
326
|
},
|
|
278
327
|
|
|
279
328
|
create: (resource, params: Partial<CreateParams> = {}) => {
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
329
|
+
const { meta } = params;
|
|
330
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
331
|
+
const query = getQuery(primaryKey, undefined, resource, meta);
|
|
332
|
+
const queryStr = qs.stringify(query);
|
|
333
|
+
const url = `${config.apiUrl}/${resource}${
|
|
334
|
+
queryStr.length > 0 ? '?' : ''
|
|
335
|
+
}${queryStr}`;
|
|
336
|
+
const metaSchema = params.meta?.schema;
|
|
337
|
+
|
|
338
|
+
return config
|
|
339
|
+
.httpClient(url, {
|
|
340
|
+
method: 'POST',
|
|
341
|
+
headers: new Headers({
|
|
342
|
+
Accept: 'application/vnd.pgrst.object+json',
|
|
343
|
+
Prefer: 'return=representation',
|
|
344
|
+
'Content-Type': 'application/json',
|
|
345
|
+
...(params.meta?.headers || {}),
|
|
346
|
+
...useCustomSchema(config.schema, metaSchema, 'POST'),
|
|
347
|
+
}),
|
|
348
|
+
body: JSON.stringify(
|
|
349
|
+
dataWithoutVirtualId(params.data, primaryKey)
|
|
350
|
+
),
|
|
351
|
+
})
|
|
352
|
+
.then(({ json }) => ({
|
|
353
|
+
data: {
|
|
354
|
+
...json,
|
|
355
|
+
id: encodeId(json, primaryKey),
|
|
356
|
+
},
|
|
357
|
+
}));
|
|
298
358
|
},
|
|
299
359
|
|
|
300
360
|
delete: (resource, params: Partial<DeleteParams> = {}) => {
|
|
301
361
|
const { id, meta } = params;
|
|
302
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
362
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
303
363
|
|
|
304
364
|
const query = getQuery(primaryKey, id, resource, meta);
|
|
305
365
|
|
|
306
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
366
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
367
|
+
const metaSchema = params.meta?.schema;
|
|
368
|
+
|
|
369
|
+
return config
|
|
370
|
+
.httpClient(url, {
|
|
371
|
+
method: 'DELETE',
|
|
372
|
+
headers: new Headers({
|
|
373
|
+
Accept: 'application/vnd.pgrst.object+json',
|
|
374
|
+
Prefer: 'return=representation',
|
|
375
|
+
'Content-Type': 'application/json',
|
|
376
|
+
...(params.meta?.headers || {}),
|
|
377
|
+
...useCustomSchema(config.schema, metaSchema, 'DELETE'),
|
|
378
|
+
}),
|
|
379
|
+
})
|
|
380
|
+
.then(({ json }) => ({
|
|
381
|
+
data: dataWithVirtualId(json, primaryKey),
|
|
382
|
+
}));
|
|
317
383
|
},
|
|
318
384
|
|
|
319
385
|
deleteMany: (resource, params: Partial<DeleteManyParams> = {}) => {
|
|
320
386
|
const { ids, meta } = params;
|
|
321
|
-
const primaryKey = getPrimaryKey(resource, primaryKeys);
|
|
387
|
+
const primaryKey = getPrimaryKey(resource, config.primaryKeys);
|
|
322
388
|
|
|
323
389
|
const query = getQuery(primaryKey, ids, resource, meta);
|
|
324
390
|
|
|
325
|
-
const url = `${apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
391
|
+
const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
|
|
392
|
+
const metaSchema = params.meta?.schema;
|
|
393
|
+
|
|
394
|
+
return config
|
|
395
|
+
.httpClient(url, {
|
|
396
|
+
method: 'DELETE',
|
|
397
|
+
headers: new Headers({
|
|
398
|
+
Prefer: 'return=representation',
|
|
399
|
+
'Content-Type': 'application/json',
|
|
400
|
+
...(params.meta?.headers || {}),
|
|
401
|
+
...useCustomSchema(config.schema, metaSchema, 'DELETE'),
|
|
402
|
+
}),
|
|
403
|
+
})
|
|
404
|
+
.then(({ json }) => ({
|
|
405
|
+
data: json.map(data => encodeId(data, primaryKey)),
|
|
406
|
+
}));
|
|
337
407
|
},
|
|
338
408
|
});
|
package/src/urlBuilder.ts
CHANGED
|
@@ -49,6 +49,18 @@ type ParsedFiltersResult = {
|
|
|
49
49
|
select: any;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
const isObject = obj =>
|
|
53
|
+
typeof obj === 'object' && !Array.isArray(obj) && obj !== null;
|
|
54
|
+
|
|
55
|
+
const resolveKeys = (filter: any, keys: Array<any>) => {
|
|
56
|
+
let result = filter[keys[0]];
|
|
57
|
+
for (let i = 1; i < keys.length; ++i) {
|
|
58
|
+
result = result[keys[i]];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
|
|
52
64
|
export type PostgRestOperator = (typeof postgrestOperators)[number];
|
|
53
65
|
|
|
54
66
|
export const parseFilters = (
|
|
@@ -62,31 +74,81 @@ export const parseFilters = (
|
|
|
62
74
|
Object.keys(filter).forEach(function (key) {
|
|
63
75
|
// key: the name of the object key
|
|
64
76
|
|
|
77
|
+
let keyArray = [key];
|
|
78
|
+
let values: Array<string>;
|
|
79
|
+
|
|
80
|
+
// in case of a nested object selection in a text field
|
|
81
|
+
// RA returns object{ object } structure which is resolved here
|
|
82
|
+
// see issue https://github.com/raphiniert-com/ra-data-postgrest/issues/58
|
|
83
|
+
if (key.split('@')[0] !== '' && isObject(filter[key])) {
|
|
84
|
+
let innerVal = filter[key];
|
|
85
|
+
|
|
86
|
+
do {
|
|
87
|
+
const inner = resolveKeys(filter, keyArray);
|
|
88
|
+
const [innerKey] = Object.keys(inner);
|
|
89
|
+
|
|
90
|
+
keyArray.push(innerKey);
|
|
91
|
+
innerVal = inner[innerKey];
|
|
92
|
+
} while (isObject(innerVal));
|
|
93
|
+
|
|
94
|
+
key = keyArray.join('.');
|
|
95
|
+
values = [innerVal];
|
|
96
|
+
} else {
|
|
97
|
+
values = [filter[key]];
|
|
98
|
+
}
|
|
99
|
+
|
|
65
100
|
const splitKey = key.split('@');
|
|
66
101
|
const operation: PostgRestOperator =
|
|
67
102
|
splitKey.length == 2
|
|
68
103
|
? (splitKey[1] as PostgRestOperator)
|
|
69
104
|
: defaultListOp;
|
|
70
105
|
|
|
71
|
-
let values: Array<string>;
|
|
72
106
|
if (['like', 'ilike'].includes(operation)) {
|
|
73
107
|
// we split the search term in words
|
|
74
|
-
values = filter
|
|
75
|
-
}
|
|
76
|
-
|
|
108
|
+
values = resolveKeys(filter, keyArray).trim().split(/\s+/);
|
|
109
|
+
}
|
|
110
|
+
// CASE: Logical operator
|
|
111
|
+
else if (['or', 'and'].includes(operation)) {
|
|
112
|
+
// we extract each value entries and make it dot separated string.
|
|
113
|
+
const { filter: subFilter } = parseFilters(
|
|
114
|
+
{ filter: resolveKeys(filter, keyArray) },
|
|
115
|
+
defaultListOp
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// something like { "age@lt": 18 } and { "age@gt": 21 }
|
|
119
|
+
const filterExpressions: string[] = [];
|
|
120
|
+
Object.entries(subFilter).forEach(([op, val]) => {
|
|
121
|
+
if (Array.isArray(val))
|
|
122
|
+
filterExpressions.push(...val.map(v => [op, v].join('.')));
|
|
123
|
+
else filterExpressions.push([op, val].join('.'));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// finally we flatten all as single string and enclose with bracket.
|
|
127
|
+
values = [`(${filterExpressions.join(',')})`];
|
|
77
128
|
}
|
|
78
129
|
|
|
79
130
|
values.forEach(value => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
131
|
+
let op: string = (() => {
|
|
132
|
+
// if operator is intentionally blank, rpc syntax
|
|
133
|
+
if (operation.length === 0) return `${value}`;
|
|
134
|
+
|
|
135
|
+
if (operation.includes('like'))
|
|
136
|
+
return `${operation}.*${value}*`;
|
|
137
|
+
if (['and', 'or'].includes(operation)) return `${value}`;
|
|
138
|
+
if (['cs', 'cd'].includes(operation))
|
|
139
|
+
return `${operation}.{${value}}`;
|
|
140
|
+
return `${operation}.${value}`;
|
|
141
|
+
})();
|
|
86
142
|
|
|
143
|
+
// If resulting filter doesn't contain the fieldname, then add it.
|
|
87
144
|
if (result.filter[splitKey[0]] === undefined) {
|
|
88
|
-
// first operator for the key,
|
|
89
|
-
|
|
145
|
+
// first operator for the key,
|
|
146
|
+
if (['and', 'or'].includes(operation)) {
|
|
147
|
+
result.filter[operation] = op;
|
|
148
|
+
} else {
|
|
149
|
+
// we add it to the dict
|
|
150
|
+
result.filter[splitKey[0]] = op;
|
|
151
|
+
}
|
|
90
152
|
} else {
|
|
91
153
|
if (!Array.isArray(result[splitKey[0]])) {
|
|
92
154
|
// second operator, we transform to an array
|
|
@@ -140,8 +202,16 @@ export const encodeId = (data: any, primaryKey: PrimaryKey): Identifier => {
|
|
|
140
202
|
}
|
|
141
203
|
};
|
|
142
204
|
|
|
143
|
-
export const
|
|
144
|
-
|
|
205
|
+
export const removePrimaryKey = (data: any, primaryKey: PrimaryKey) => {
|
|
206
|
+
const newData = { ...data };
|
|
207
|
+
primaryKey.forEach(key => {
|
|
208
|
+
delete newData[key];
|
|
209
|
+
});
|
|
210
|
+
return newData;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export const dataWithVirtualId = (data: any, primaryKey: PrimaryKey) => {
|
|
214
|
+
if (primaryKey.length === 1 && primaryKey[0] === 'id') {
|
|
145
215
|
return data;
|
|
146
216
|
}
|
|
147
217
|
|
|
@@ -150,13 +220,22 @@ export const dataWithId = (data: any, primaryKey: PrimaryKey) => {
|
|
|
150
220
|
});
|
|
151
221
|
};
|
|
152
222
|
|
|
153
|
-
export const
|
|
223
|
+
export const dataWithoutVirtualId = (data: any, primaryKey: PrimaryKey) => {
|
|
224
|
+
if (primaryKey.length === 1 && primaryKey[0] === 'id') {
|
|
225
|
+
return data;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const { id, ...dataWithoutId } = data;
|
|
229
|
+
return dataWithoutId;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const isCompoundKey = (primaryKey: PrimaryKey): Boolean => {
|
|
154
233
|
return primaryKey.length > 1;
|
|
155
234
|
};
|
|
156
235
|
|
|
157
236
|
export const getQuery = (
|
|
158
237
|
primaryKey: PrimaryKey,
|
|
159
|
-
ids: Identifier | Array<Identifier
|
|
238
|
+
ids: Identifier | Array<Identifier> | undefined,
|
|
160
239
|
resource: string,
|
|
161
240
|
meta: any = null
|
|
162
241
|
): any => {
|
|
@@ -185,7 +264,7 @@ export const getQuery = (
|
|
|
185
264
|
[primaryKey[0]]: `in.(${ids.join(',')})`,
|
|
186
265
|
};
|
|
187
266
|
}
|
|
188
|
-
} else {
|
|
267
|
+
} else if (ids) {
|
|
189
268
|
// if ids is one Identifier
|
|
190
269
|
const id: Identifier = ids.toString();
|
|
191
270
|
const primaryKeyParams = decodeId(id, primaryKey);
|
|
@@ -221,20 +300,6 @@ export const getQuery = (
|
|
|
221
300
|
return result;
|
|
222
301
|
};
|
|
223
302
|
|
|
224
|
-
export const getKeyData = (primaryKey: PrimaryKey, data: object): object => {
|
|
225
|
-
if (isCompoundKey(primaryKey)) {
|
|
226
|
-
return primaryKey.reduce(
|
|
227
|
-
(keyData, key) => ({
|
|
228
|
-
...keyData,
|
|
229
|
-
[key]: data[key],
|
|
230
|
-
}),
|
|
231
|
-
{}
|
|
232
|
-
);
|
|
233
|
-
} else {
|
|
234
|
-
return { [primaryKey[0]]: data[primaryKey[0]] };
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
|
|
238
303
|
export const getOrderBy = (
|
|
239
304
|
field: string,
|
|
240
305
|
order: string,
|