@raphiniert/ra-data-postgrest 2.0.0-alpha.0 → 2.0.0-alpha.3

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/src/index.ts CHANGED
@@ -1,4 +1,16 @@
1
- import { fetchUtils, DataProvider } from 'ra-core';
1
+ import {
2
+ fetchUtils,
3
+ DataProvider,
4
+ GetListParams,
5
+ GetOneParams,
6
+ GetManyParams,
7
+ GetManyReferenceParams,
8
+ UpdateParams,
9
+ UpdateManyParams,
10
+ CreateParams,
11
+ DeleteParams,
12
+ DeleteManyParams,
13
+ } from 'ra-core';
2
14
  import {
3
15
  PrimaryKey,
4
16
  PostgRestOperator,
@@ -12,6 +24,8 @@ import {
12
24
  decodeId,
13
25
  isCompoundKey,
14
26
  } from './urlBuilder';
27
+ import qs from 'qs';
28
+
15
29
  /**
16
30
  * Maps react-admin queries to a postgrest REST API
17
31
  *
@@ -34,13 +48,24 @@ import {
34
48
  * @example
35
49
  *
36
50
  * import * as React from 'react';
37
- * import { Admin, Resource } from 'react-admin';
38
- * import postgrestRestProvider from '@promitheus/ra-data-postgrest';
51
+ * import { Admin, Resource, fetchUtils } from 'react-admin';
52
+ * import postgrestRestProvider,
53
+ * { IDataProviderConfig,
54
+ * defaultPrimaryKeys,
55
+ * defaultSchema } from '@raphiniert/ra-data-postgrest';
39
56
  *
40
57
  * import { PostList } from './posts';
41
58
  *
59
+ * const config: IDataProviderConfig = {
60
+ * apiUrl: 'http://path.to.my.api/',
61
+ * httpClient: fetchUtils.fetchJson,
62
+ * defaultListOp: 'eq',
63
+ * primaryKeys: defaultPrimaryKeys,
64
+ * schema: defaultSchema
65
+ * }
66
+ *
42
67
  * const App = () => (
43
- * <Admin dataProvider={postgrestRestProvider('http://path.to.my.api/')}>
68
+ * <Admin dataProvider={postgrestRestProvider(config)}>
44
69
  * <Resource name="posts" list={PostList} />
45
70
  * </Admin>
46
71
  * );
@@ -48,40 +73,79 @@ import {
48
73
  * export default App;
49
74
  */
50
75
 
51
- const defaultPrimaryKeys = new Map<string, PrimaryKey>();
52
-
53
- export default (
54
- apiUrl: string,
55
- httpClient = fetchUtils.fetchJson,
56
- defaultListOp: PostgRestOperator = 'eq',
57
- primaryKeys: Map<string, PrimaryKey> = defaultPrimaryKeys
58
- ): DataProvider => ({
59
- getList: (resource, params) => {
60
- const primaryKey = getPrimaryKey(resource, primaryKeys);
76
+ export type MetaOption = string | undefined;
77
+
78
+ export const defaultPrimaryKeys = new Map<string, PrimaryKey>();
79
+
80
+ export const defaultSchema = () => '';
81
+
82
+ export interface IDataProviderConfig {
83
+ apiUrl: string;
84
+ httpClient: (string, Options) => Promise<any>;
85
+ defaultListOp: PostgRestOperator;
86
+ primaryKeys: Map<string, PrimaryKey>;
87
+ schema: () => string;
88
+ }
89
+
90
+ const useCustomSchema = (
91
+ schema: () => string,
92
+ metaSchema: MetaOption,
93
+ method: string
94
+ ) => {
95
+ let funcHeaderSchema = schema;
96
+ if (metaSchema !== undefined) {
97
+ funcHeaderSchema = () => metaSchema;
98
+ }
99
+
100
+ if (funcHeaderSchema().length > 0) {
101
+ let schemaHeader = '';
102
+ if (['GET', 'HEAD'].includes(method)) {
103
+ schemaHeader = 'Accept-Profile';
104
+ } else if (['POST', 'PATCH', 'PUT', 'DELETE'].includes(method)) {
105
+ schemaHeader = 'Content-Profile';
106
+ } else return {};
107
+
108
+ return { [schemaHeader]: funcHeaderSchema() };
109
+ } else return {};
110
+ };
111
+
112
+ export default (config: IDataProviderConfig): DataProvider => ({
113
+ getList: (resource, params: Partial<GetListParams> = {}) => {
114
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
61
115
 
62
116
  const { page, perPage } = params.pagination;
63
- const { field, order } = params.sort;
64
- const parsedFilter = parseFilters(params.filter, defaultListOp);
117
+ const { field, order } = params.sort || {};
118
+ const { filter, select } = parseFilters(params, config.defaultListOp);
119
+ const metaSchema = params.meta?.schema;
65
120
 
66
- const query = {
67
- order: getOrderBy(field, order, primaryKey),
121
+ let query = {
68
122
  offset: String((page - 1) * perPage),
69
123
  limit: String(perPage),
70
124
  // append filters
71
- ...parsedFilter,
125
+ ...filter,
72
126
  };
73
127
 
128
+ if (field) {
129
+ query.order = getOrderBy(field, order, primaryKey);
130
+ }
131
+
132
+ if (select) {
133
+ query.select = select;
134
+ }
135
+
74
136
  // add header that Content-Range is in returned header
75
137
  const options = {
76
138
  headers: new Headers({
77
139
  Accept: 'application/json',
78
140
  Prefer: 'count=exact',
141
+ ...(params.meta?.headers || {}),
142
+ ...useCustomSchema(config.schema, metaSchema, 'GET'),
79
143
  }),
80
144
  };
81
145
 
82
- const url = `${apiUrl}/${resource}?${new URLSearchParams(query)}`;
146
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
83
147
 
84
- return httpClient(url, options).then(({ headers, json }) => {
148
+ return config.httpClient(url, options).then(({ headers, json }) => {
85
149
  if (!headers.has('content-range')) {
86
150
  throw new Error(
87
151
  `The Content-Range header is missing in the HTTP Response. The postgREST data provider expects
@@ -99,68 +163,92 @@ export default (
99
163
  });
100
164
  },
101
165
 
102
- getOne: (resource, params) => {
103
- const id = params.id;
104
- const primaryKey = getPrimaryKey(resource, primaryKeys);
166
+ getOne: (resource, params: Partial<GetOneParams> = {}) => {
167
+ const { id, meta } = params;
168
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
105
169
 
106
- const query = getQuery(primaryKey, id, resource);
170
+ const query = getQuery(primaryKey, id, resource, meta);
107
171
 
108
- const url = `${apiUrl}/${resource}?${query}`;
172
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
173
+ const metaSchema = params.meta?.schema;
109
174
 
110
- return httpClient(url, {
111
- headers: new Headers({
112
- accept: 'application/vnd.pgrst.object+json',
113
- }),
114
- }).then(({ json }) => ({
115
- data: dataWithId(json, primaryKey),
116
- }));
175
+ return config
176
+ .httpClient(url, {
177
+ headers: new Headers({
178
+ accept: 'application/vnd.pgrst.object+json',
179
+ ...(params.meta?.headers || {}),
180
+ ...useCustomSchema(config.schema, metaSchema, 'GET'),
181
+ }),
182
+ })
183
+ .then(({ json }) => ({
184
+ data: dataWithId(json, primaryKey),
185
+ }));
117
186
  },
118
187
 
119
- getMany: (resource, params) => {
188
+ getMany: (resource, params: Partial<GetManyParams> = {}) => {
120
189
  const ids = params.ids;
121
- const primaryKey = getPrimaryKey(resource, primaryKeys);
190
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
122
191
 
123
- const query = getQuery(primaryKey, ids, resource);
192
+ const query = getQuery(primaryKey, ids, resource, params.meta);
124
193
 
125
- const url = `${apiUrl}/${resource}?${query}`;
194
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
195
+ const metaSchema = params.meta?.schema;
126
196
 
127
- return httpClient(url).then(({ json }) => ({
128
- data: json.map(data => dataWithId(data, primaryKey)),
129
- }));
197
+ return config
198
+ .httpClient(url, {
199
+ headers: new Headers({
200
+ ...useCustomSchema(config.schema, metaSchema, 'GET'),
201
+ }),
202
+ })
203
+ .then(({ json }) => ({
204
+ data: json.map(data => dataWithId(data, primaryKey)),
205
+ }));
130
206
  },
131
207
 
132
- getManyReference: (resource, params) => {
208
+ getManyReference: (
209
+ resource,
210
+ params: Partial<GetManyReferenceParams> = {}
211
+ ) => {
133
212
  const { page, perPage } = params.pagination;
134
213
  const { field, order } = params.sort;
135
- const parsedFilter = parseFilters(params.filter, defaultListOp);
136
- const primaryKey = getPrimaryKey(resource, primaryKeys);
137
214
 
138
- const query = params.target
215
+ const { filter, select } = parseFilters(params, config.defaultListOp);
216
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
217
+
218
+ const metaSchema = params.meta?.schema;
219
+
220
+ let query = params.target
139
221
  ? {
140
222
  [params.target]: `eq.${params.id}`,
141
223
  order: getOrderBy(field, order, primaryKey),
142
224
  offset: String((page - 1) * perPage),
143
225
  limit: String(perPage),
144
- ...parsedFilter,
226
+ ...filter,
145
227
  }
146
228
  : {
147
229
  order: getOrderBy(field, order, primaryKey),
148
230
  offset: String((page - 1) * perPage),
149
231
  limit: String(perPage),
150
- ...parsedFilter,
232
+ ...filter,
151
233
  };
152
234
 
235
+ if (select) {
236
+ query.select = select;
237
+ }
238
+
153
239
  // add header that Content-Range is in returned header
154
240
  const options = {
155
241
  headers: new Headers({
156
242
  Accept: 'application/json',
157
243
  Prefer: 'count=exact',
244
+ ...(params.meta?.headers || {}),
245
+ ...useCustomSchema(config.schema, metaSchema, 'GET'),
158
246
  }),
159
247
  };
160
248
 
161
- const url = `${apiUrl}/${resource}?${new URLSearchParams(query)}`;
249
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
162
250
 
163
- return httpClient(url, options).then(({ headers, json }) => {
251
+ return config.httpClient(url, options).then(({ headers, json }) => {
164
252
  if (!headers.has('content-range')) {
165
253
  throw new Error(
166
254
  `The Content-Range header is missing in the HTTP Response. The postgREST data provider expects
@@ -178,35 +266,40 @@ export default (
178
266
  });
179
267
  },
180
268
 
181
- update: (resource, params) => {
182
- const { id, data } = params;
183
- const primaryKey = getPrimaryKey(resource, primaryKeys);
269
+ update: (resource, params: Partial<UpdateParams> = {}) => {
270
+ const { id, data, meta } = params;
271
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
184
272
 
185
- const query = getQuery(primaryKey, id, resource);
273
+ const query = getQuery(primaryKey, id, resource, meta);
186
274
 
187
275
  const primaryKeyData = getKeyData(primaryKey, data);
188
276
 
189
- const url = `${apiUrl}/${resource}?${query}`;
277
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
278
+ const metaSchema = params.meta?.schema;
190
279
 
191
280
  const body = JSON.stringify({
192
281
  ...data,
193
282
  ...primaryKeyData,
194
283
  });
195
284
 
196
- return httpClient(url, {
197
- method: 'PATCH',
198
- headers: new Headers({
199
- Accept: 'application/vnd.pgrst.object+json',
200
- Prefer: 'return=representation',
201
- 'Content-Type': 'application/json',
202
- }),
203
- body,
204
- }).then(({ json }) => ({ data: dataWithId(json, primaryKey) }));
285
+ return config
286
+ .httpClient(url, {
287
+ method: 'PATCH',
288
+ headers: new Headers({
289
+ Accept: 'application/vnd.pgrst.object+json',
290
+ Prefer: 'return=representation',
291
+ 'Content-Type': 'application/json',
292
+ ...(params.meta?.headers || {}),
293
+ ...useCustomSchema(config.schema, metaSchema, 'PATCH'),
294
+ }),
295
+ body,
296
+ })
297
+ .then(({ json }) => ({ data: dataWithId(json, primaryKey) }));
205
298
  },
206
299
 
207
- updateMany: (resource, params) => {
300
+ updateMany: (resource, params: Partial<UpdateManyParams> = {}) => {
208
301
  const ids = params.ids;
209
- const primaryKey = getPrimaryKey(resource, primaryKeys);
302
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
210
303
 
211
304
  const body = JSON.stringify(
212
305
  params.ids.map(id => {
@@ -229,75 +322,94 @@ export default (
229
322
  })
230
323
  );
231
324
 
232
- const url = `${apiUrl}/${resource}`;
233
-
234
- return httpClient(url, {
235
- method: 'PATCH',
236
- headers: new Headers({
237
- Prefer: 'return=representation',
238
- 'Content-Type': 'application/json',
239
- }),
240
- body,
241
- }).then(({ json }) => ({
242
- data: json.map(data => encodeId(data, primaryKey)),
243
- }));
325
+ const url = `${config.apiUrl}/${resource}`;
326
+ const metaSchema = params.meta?.schema;
327
+
328
+ return config
329
+ .httpClient(url, {
330
+ method: 'PATCH',
331
+ headers: new Headers({
332
+ Prefer: 'return=representation',
333
+ 'Content-Type': 'application/json',
334
+ ...(params.meta?.headers || {}),
335
+ ...useCustomSchema(config.schema, metaSchema, 'PATCH'),
336
+ }),
337
+ body,
338
+ })
339
+ .then(({ json }) => ({
340
+ data: json.map(data => encodeId(data, primaryKey)),
341
+ }));
244
342
  },
245
343
 
246
- create: (resource, params) => {
247
- const primaryKey = getPrimaryKey(resource, primaryKeys);
248
-
249
- const url = `${apiUrl}/${resource}`;
250
-
251
- return httpClient(url, {
252
- method: 'POST',
253
- headers: new Headers({
254
- Accept: 'application/vnd.pgrst.object+json',
255
- Prefer: 'return=representation',
256
- 'Content-Type': 'application/json',
257
- }),
258
- body: JSON.stringify(params.data),
259
- }).then(({ json }) => ({
260
- data: {
261
- ...params.data,
262
- id: encodeId(json, primaryKey),
263
- },
264
- }));
344
+ create: (resource, params: Partial<CreateParams> = {}) => {
345
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
346
+ const url = `${config.apiUrl}/${resource}`;
347
+ const metaSchema = params.meta?.schema;
348
+
349
+ return config
350
+ .httpClient(url, {
351
+ method: 'POST',
352
+ headers: new Headers({
353
+ Accept: 'application/vnd.pgrst.object+json',
354
+ Prefer: 'return=representation',
355
+ 'Content-Type': 'application/json',
356
+ ...(params.meta?.headers || {}),
357
+ ...useCustomSchema(config.schema, metaSchema, 'POST'),
358
+ }),
359
+ body: JSON.stringify(params.data),
360
+ })
361
+ .then(({ json }) => ({
362
+ data: {
363
+ ...json,
364
+ id: encodeId(json, primaryKey),
365
+ },
366
+ }));
265
367
  },
266
368
 
267
- delete: (resource, params) => {
268
- const id = params.id;
269
- const primaryKey = getPrimaryKey(resource, primaryKeys);
270
-
271
- const query = getQuery(primaryKey, id, resource);
272
-
273
- const url = `${apiUrl}/${resource}?${query}`;
274
-
275
- return httpClient(url, {
276
- method: 'DELETE',
277
- headers: new Headers({
278
- Accept: 'application/vnd.pgrst.object+json',
279
- Prefer: 'return=representation',
280
- 'Content-Type': 'application/json',
281
- }),
282
- }).then(({ json }) => ({ data: dataWithId(json, primaryKey) }));
369
+ delete: (resource, params: Partial<DeleteParams> = {}) => {
370
+ const { id, meta } = params;
371
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
372
+
373
+ const query = getQuery(primaryKey, id, resource, meta);
374
+
375
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
376
+ const metaSchema = params.meta?.schema;
377
+
378
+ return config
379
+ .httpClient(url, {
380
+ method: 'DELETE',
381
+ headers: new Headers({
382
+ Accept: 'application/vnd.pgrst.object+json',
383
+ Prefer: 'return=representation',
384
+ 'Content-Type': 'application/json',
385
+ ...(params.meta?.headers || {}),
386
+ ...useCustomSchema(config.schema, metaSchema, 'DELETE'),
387
+ }),
388
+ })
389
+ .then(({ json }) => ({ data: dataWithId(json, primaryKey) }));
283
390
  },
284
391
 
285
- deleteMany: (resource, params) => {
286
- const ids = params.ids;
287
- const primaryKey = getPrimaryKey(resource, primaryKeys);
392
+ deleteMany: (resource, params: Partial<DeleteManyParams> = {}) => {
393
+ const { ids, meta } = params;
394
+ const primaryKey = getPrimaryKey(resource, config.primaryKeys);
288
395
 
289
- const query = getQuery(primaryKey, ids, resource);
396
+ const query = getQuery(primaryKey, ids, resource, meta);
290
397
 
291
- const url = `${apiUrl}/${resource}?${query}`;
398
+ const url = `${config.apiUrl}/${resource}?${qs.stringify(query)}`;
399
+ const metaSchema = params.meta?.schema;
292
400
 
293
- return httpClient(url, {
294
- method: 'DELETE',
295
- headers: new Headers({
296
- Prefer: 'return=representation',
297
- 'Content-Type': 'application/json',
298
- }),
299
- }).then(({ json }) => ({
300
- data: json.map(data => encodeId(data, primaryKey)),
301
- }));
401
+ return config
402
+ .httpClient(url, {
403
+ method: 'DELETE',
404
+ headers: new Headers({
405
+ Prefer: 'return=representation',
406
+ 'Content-Type': 'application/json',
407
+ ...(params.meta?.headers || {}),
408
+ ...useCustomSchema(config.schema, metaSchema, 'DELETE'),
409
+ }),
410
+ })
411
+ .then(({ json }) => ({
412
+ data: json.map(data => encodeId(data, primaryKey)),
413
+ }));
302
414
  },
303
415
  });