@raphiniert/ra-data-postgrest 2.4.0 → 2.5.0
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 +9 -0
- package/README.md +360 -281
- package/esm/index.js +117 -17
- package/esm/urlBuilder.js +30 -0
- package/lib/index.js +118 -18
- package/lib/urlBuilder.js +30 -0
- package/package.json +10 -11
- package/src/index.ts +140 -19
- package/src/urlBuilder.ts +30 -0
package/src/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
PostgRestSortOrder,
|
|
25
25
|
} from './urlBuilder';
|
|
26
26
|
import qs from 'qs';
|
|
27
|
-
import isEqual from '
|
|
27
|
+
import { isDeepStrictEqual as isEqual } from 'node:util';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Maps react-admin queries to a postgrest REST API
|
|
@@ -186,12 +186,15 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
186
186
|
the pagination. If you are using CORS, did you declare Content-Range in the Access-Control-Expose-Headers header?`
|
|
187
187
|
);
|
|
188
188
|
}
|
|
189
|
+
const data = json.map(obj => dataWithVirtualId(obj, primaryKey));
|
|
190
|
+
const prefetched = getPrefetchedData(data, params.meta?.prefetch);
|
|
189
191
|
return {
|
|
190
|
-
data:
|
|
192
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
191
193
|
total: parseInt(
|
|
192
194
|
headers.get('content-range').split('/').pop(),
|
|
193
195
|
10
|
|
194
196
|
),
|
|
197
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
195
198
|
};
|
|
196
199
|
});
|
|
197
200
|
},
|
|
@@ -213,9 +216,17 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
213
216
|
...useCustomSchema(config.schema, metaSchema, 'GET'),
|
|
214
217
|
}),
|
|
215
218
|
})
|
|
216
|
-
.then(({ json }) =>
|
|
217
|
-
data
|
|
218
|
-
|
|
219
|
+
.then(({ json }) => {
|
|
220
|
+
const data = dataWithVirtualId(json, primaryKey);
|
|
221
|
+
const prefetched = getPrefetchedData(
|
|
222
|
+
data,
|
|
223
|
+
params.meta?.prefetch
|
|
224
|
+
);
|
|
225
|
+
return {
|
|
226
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
227
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
228
|
+
};
|
|
229
|
+
});
|
|
219
230
|
},
|
|
220
231
|
|
|
221
232
|
getMany: (resource, params: Partial<GetManyParams> = {}) => {
|
|
@@ -236,9 +247,19 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
236
247
|
...useCustomSchema(config.schema, metaSchema, 'GET'),
|
|
237
248
|
}),
|
|
238
249
|
})
|
|
239
|
-
.then(({ json }) =>
|
|
240
|
-
data
|
|
241
|
-
|
|
250
|
+
.then(({ json }) => {
|
|
251
|
+
const data = json.map(data =>
|
|
252
|
+
dataWithVirtualId(data, primaryKey)
|
|
253
|
+
);
|
|
254
|
+
const prefetched = getPrefetchedData(
|
|
255
|
+
data,
|
|
256
|
+
params.meta?.prefetch
|
|
257
|
+
);
|
|
258
|
+
return {
|
|
259
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
260
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
261
|
+
};
|
|
262
|
+
});
|
|
242
263
|
},
|
|
243
264
|
|
|
244
265
|
getManyReference: (
|
|
@@ -292,12 +313,15 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
292
313
|
the pagination. If you are using CORS, did you declare Content-Range in the Access-Control-Expose-Headers header?`
|
|
293
314
|
);
|
|
294
315
|
}
|
|
316
|
+
const data = json.map(data => dataWithVirtualId(data, primaryKey));
|
|
317
|
+
const prefetched = getPrefetchedData(data, params.meta?.prefetch);
|
|
295
318
|
return {
|
|
296
|
-
data:
|
|
319
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
297
320
|
total: parseInt(
|
|
298
321
|
headers.get('content-range').split('/').pop(),
|
|
299
322
|
10
|
|
300
323
|
),
|
|
324
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
301
325
|
};
|
|
302
326
|
});
|
|
303
327
|
},
|
|
@@ -332,9 +356,17 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
332
356
|
}),
|
|
333
357
|
body,
|
|
334
358
|
})
|
|
335
|
-
.then(({ json }) =>
|
|
336
|
-
data
|
|
337
|
-
|
|
359
|
+
.then(({ json }) => {
|
|
360
|
+
const data = dataWithVirtualId(json, primaryKey);
|
|
361
|
+
const prefetched = getPrefetchedData(
|
|
362
|
+
data,
|
|
363
|
+
params.meta?.prefetch
|
|
364
|
+
);
|
|
365
|
+
return {
|
|
366
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
367
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
368
|
+
};
|
|
369
|
+
});
|
|
338
370
|
},
|
|
339
371
|
|
|
340
372
|
updateMany: (resource, params: Partial<UpdateManyParams> = {}) => {
|
|
@@ -391,12 +423,20 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
391
423
|
dataWithoutVirtualId(params.data, primaryKey)
|
|
392
424
|
),
|
|
393
425
|
})
|
|
394
|
-
.then(({ json }) =>
|
|
395
|
-
data
|
|
426
|
+
.then(({ json }) => {
|
|
427
|
+
const data = {
|
|
396
428
|
...json,
|
|
397
429
|
id: encodeId(json, primaryKey),
|
|
398
|
-
}
|
|
399
|
-
|
|
430
|
+
};
|
|
431
|
+
const prefetched = getPrefetchedData(
|
|
432
|
+
data,
|
|
433
|
+
params.meta?.prefetch
|
|
434
|
+
);
|
|
435
|
+
return {
|
|
436
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
437
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
438
|
+
};
|
|
439
|
+
});
|
|
400
440
|
},
|
|
401
441
|
|
|
402
442
|
delete: (resource, params: Partial<DeleteParams> = {}) => {
|
|
@@ -419,9 +459,17 @@ export default (config: IDataProviderConfig): DataProvider => ({
|
|
|
419
459
|
...useCustomSchema(config.schema, metaSchema, 'DELETE'),
|
|
420
460
|
}),
|
|
421
461
|
})
|
|
422
|
-
.then(({ json }) =>
|
|
423
|
-
data
|
|
424
|
-
|
|
462
|
+
.then(({ json }) => {
|
|
463
|
+
const data = dataWithVirtualId(json, primaryKey);
|
|
464
|
+
const prefetched = getPrefetchedData(
|
|
465
|
+
data,
|
|
466
|
+
params.meta?.prefetch
|
|
467
|
+
);
|
|
468
|
+
return {
|
|
469
|
+
data: removePrefetchedData(data, params.meta?.prefetch),
|
|
470
|
+
meta: params.meta?.prefetch ? { prefetched } : undefined,
|
|
471
|
+
};
|
|
472
|
+
});
|
|
425
473
|
},
|
|
426
474
|
|
|
427
475
|
deleteMany: (resource, params: Partial<DeleteManyParams> = {}) => {
|
|
@@ -458,3 +506,76 @@ const getChanges = (data: any, previousData: any) => {
|
|
|
458
506
|
}, {});
|
|
459
507
|
return changes;
|
|
460
508
|
};
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Extract embeds from Postgrest responses
|
|
512
|
+
*
|
|
513
|
+
* When calling Postgrest database.getOne('posts', 123, { embed: 'tags' }),
|
|
514
|
+
* the Postgrest response adds a `tags` key to the response, containing the
|
|
515
|
+
* related tags. Something like:
|
|
516
|
+
*
|
|
517
|
+
* { id: 123, title: 'React-query in details', tags: [{ id: 1, name: 'react' }, { id: 1, name: 'query' }] }
|
|
518
|
+
*
|
|
519
|
+
* We want to copy all the embeds in a data object, that will later
|
|
520
|
+
* be included into the response meta.prefetched key.
|
|
521
|
+
*
|
|
522
|
+
* @example getPrefetchedData({ id: 123, title: 'React-query in details', tags: [{ id: 1, name: 'react' }, { id: 1, name: 'query' }] }, ['tags'])
|
|
523
|
+
* // {
|
|
524
|
+
* // tags: [{ id: 1, name: 'react' }, { id: 1, name: 'query' }]
|
|
525
|
+
* // }
|
|
526
|
+
*/
|
|
527
|
+
const getPrefetchedData = (data, prefetchParam?: string[]) => {
|
|
528
|
+
if (!prefetchParam) return undefined;
|
|
529
|
+
const prefetched = {};
|
|
530
|
+
const dataArray = Array.isArray(data) ? data : [data];
|
|
531
|
+
prefetchParam.forEach(resource => {
|
|
532
|
+
dataArray.forEach(record => {
|
|
533
|
+
if (!prefetched[resource]) {
|
|
534
|
+
prefetched[resource] = [];
|
|
535
|
+
}
|
|
536
|
+
const prefetchedData = Array.isArray(record[resource])
|
|
537
|
+
? record[resource]
|
|
538
|
+
: [record[resource]];
|
|
539
|
+
prefetchedData.forEach(prefetchedRecord => {
|
|
540
|
+
if (
|
|
541
|
+
prefetched[resource].some(r => r.id === prefetchedRecord.id)
|
|
542
|
+
) {
|
|
543
|
+
// do not add the record if it's already there
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
prefetched[resource].push(prefetchedRecord);
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
return prefetched;
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Remove embeds from Postgrest responses
|
|
556
|
+
*
|
|
557
|
+
* When calling Postgrest database.getOne('posts', 123, { embed: 'tags' }),
|
|
558
|
+
* the Postgrest response adds a `post` key to the response, containing the
|
|
559
|
+
* related post. Something like:
|
|
560
|
+
*
|
|
561
|
+
* { id: 123, title: 'React-query in details', tags: [{ id: 1, name: 'react' }, { id: 1, name: 'query' }] }
|
|
562
|
+
*
|
|
563
|
+
* We want to remove all the embeds from the response.
|
|
564
|
+
*
|
|
565
|
+
* @example removePrefetchedData({ id: 123, title: 'React-query in details', tags: [{ id: 1, name: 'react' }, { id: 1, name: 'query' }] }, 'tags')
|
|
566
|
+
* // { id: 123, title: 'React-query in details' }
|
|
567
|
+
*/
|
|
568
|
+
const removePrefetchedData = (data, prefetchParam?: string[]) => {
|
|
569
|
+
if (!prefetchParam) return data;
|
|
570
|
+
const dataArray = Array.isArray(data) ? data : [data];
|
|
571
|
+
const newDataArray = dataArray.map(record => {
|
|
572
|
+
const newRecord = {};
|
|
573
|
+
for (const key in record) {
|
|
574
|
+
if (!prefetchParam.includes(key)) {
|
|
575
|
+
newRecord[key] = record[key];
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return newRecord;
|
|
579
|
+
});
|
|
580
|
+
return Array.isArray(data) ? newDataArray : newDataArray[0];
|
|
581
|
+
};
|
package/src/urlBuilder.ts
CHANGED
|
@@ -168,6 +168,15 @@ export const parseFilters = (
|
|
|
168
168
|
: meta.columns;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
if (meta?.embed || meta?.prefetch) {
|
|
172
|
+
const columns = getColumnsParam(meta.embed, meta.prefetch);
|
|
173
|
+
if (columns) {
|
|
174
|
+
result.select = result.select
|
|
175
|
+
? `${result.select},${columns.join(',')}`
|
|
176
|
+
: `*,${columns.join(',')}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
171
180
|
return result;
|
|
172
181
|
};
|
|
173
182
|
|
|
@@ -295,6 +304,16 @@ export const getQuery = (
|
|
|
295
304
|
: meta.columns;
|
|
296
305
|
}
|
|
297
306
|
|
|
307
|
+
if (meta?.embed || meta?.prefetch) {
|
|
308
|
+
const columns = getColumnsParam(meta.embed, meta.prefetch);
|
|
309
|
+
if (columns) {
|
|
310
|
+
result.select = result.select
|
|
311
|
+
? `${result.select},${columns.join(',')}`
|
|
312
|
+
: // if users did not specify any columns, we must add the wildcard select to not get only the embeds/prefetch
|
|
313
|
+
`*,${columns.join(',')}`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
298
317
|
return result;
|
|
299
318
|
};
|
|
300
319
|
|
|
@@ -318,3 +337,14 @@ export const getOrderBy = (
|
|
|
318
337
|
return `${field}.${postgRestOrder}`;
|
|
319
338
|
}
|
|
320
339
|
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Compute the columns parameter for the embed and prefetch query meta properties
|
|
343
|
+
*/
|
|
344
|
+
const getColumnsParam = (embed: string[], prefetch: string[]) => {
|
|
345
|
+
if (!embed && !prefetch) return;
|
|
346
|
+
const param = new Set<string>();
|
|
347
|
+
if (embed) embed.forEach(resource => param.add(`${resource}(*)`));
|
|
348
|
+
if (prefetch) prefetch.forEach(resource => param.add(`${resource}(*)`));
|
|
349
|
+
return Array.from(param);
|
|
350
|
+
};
|