@data-client/rest 0.5.0 → 0.7.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/dist/index.js +22 -12
- package/dist/index.umd.min.js +1 -1
- package/dist/next.js +22 -12
- package/legacy/RestEndpoint.js +4 -1
- package/legacy/createResource.js +10 -5
- package/legacy/paginatedCollections.js +13 -11
- package/legacy/resourceTypes.js +1 -1
- package/lib/RestEndpoint.d.ts +59 -12
- package/lib/RestEndpoint.js +4 -1
- package/lib/createResource.d.ts +1 -1
- package/lib/createResource.d.ts.map +1 -1
- package/lib/createResource.js +8 -3
- package/lib/paginatedCollections.js +13 -11
- package/lib/resourceTypes.d.ts +6 -4
- package/lib/resourceTypes.d.ts.map +1 -1
- package/lib/resourceTypes.js +1 -1
- package/package.json +2 -2
- package/src/RestEndpoint.d.ts +59 -12
- package/src/RestEndpoint.js +4 -0
- package/src/__tests__/RestEndpoint.ts +95 -9
- package/src/__tests__/createResource.test.ts +77 -72
- package/src/createResource.ts +12 -3
- package/src/paginatedCollections.ts +11 -7
- package/src/resourceTypes.ts +24 -18
- package/ts4.0/RestEndpoint.d.ts +41 -7
- package/ts4.0/createResource.d.ts +1 -1
- package/ts4.0/resourceTypes.d.ts +6 -4
- package/ts4.1/RestEndpoint.d.ts +41 -7
- package/ts4.1/createResource.d.ts +1 -1
- package/ts4.1/resourceTypes.d.ts +6 -4
package/src/RestEndpoint.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface RestInstanceBase<
|
|
|
46
46
|
/** @see https://resthooks.io/rest/api/RestEndpoint#method */
|
|
47
47
|
readonly method: (O & { method: string })['method'];
|
|
48
48
|
readonly signal: AbortSignal | undefined;
|
|
49
|
+
readonly paginationField?: string;
|
|
49
50
|
|
|
50
51
|
/* fetch lifecycles */
|
|
51
52
|
/* before-fetch */
|
|
@@ -77,10 +78,15 @@ export interface RestInstanceBase<
|
|
|
77
78
|
/** Creates a child endpoint that inherits from this while overriding provided `options`.
|
|
78
79
|
* @see https://dataclient.io/rest/api/RestEndpoint#extend
|
|
79
80
|
*/
|
|
80
|
-
extend<
|
|
81
|
+
extend<
|
|
82
|
+
E extends RestInstanceBase,
|
|
83
|
+
ExtendOptions extends PartialRestGenerics | {},
|
|
84
|
+
>(
|
|
81
85
|
this: E,
|
|
82
|
-
options: Readonly<
|
|
83
|
-
|
|
86
|
+
options: Readonly<
|
|
87
|
+
RestEndpointExtendOptions<ExtendOptions, E, F> & ExtendOptions
|
|
88
|
+
>,
|
|
89
|
+
): RestExtendedEndpoint<ExtendOptions, E>;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
export interface RestInstance<
|
|
@@ -92,9 +98,10 @@ export interface RestInstance<
|
|
|
92
98
|
body?: any;
|
|
93
99
|
searchParams?: any;
|
|
94
100
|
method?: string;
|
|
101
|
+
paginationField?: string;
|
|
95
102
|
} = { path: string },
|
|
96
103
|
> extends RestInstanceBase<F, S, M, O> {
|
|
97
|
-
/** Endpoint to append the next page extending a list for pagination
|
|
104
|
+
/** Creates an Endpoint to append the next page extending a list for pagination
|
|
98
105
|
* @see https://dataclient.io/rest/api/RestEndpoint#paginated
|
|
99
106
|
*/
|
|
100
107
|
paginated<
|
|
@@ -111,6 +118,17 @@ export interface RestInstance<
|
|
|
111
118
|
this: E,
|
|
112
119
|
cursorField: C,
|
|
113
120
|
): PaginationFieldEndpoint<E, C>;
|
|
121
|
+
/** Endpoint to append the next page extending a list for pagination
|
|
122
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#getPage
|
|
123
|
+
*/
|
|
124
|
+
getPage: 'paginationField' extends keyof O
|
|
125
|
+
? O['paginationField'] extends string
|
|
126
|
+
? PaginationFieldEndpoint<
|
|
127
|
+
F & { schema: S; sideEffect: M } & O,
|
|
128
|
+
O['paginationField']
|
|
129
|
+
>
|
|
130
|
+
: undefined
|
|
131
|
+
: undefined;
|
|
114
132
|
/** Endpoint that pushes (place at end) a newly created entity to this Collection
|
|
115
133
|
* @see https://dataclient.io/rest/api/RestEndpoint#push
|
|
116
134
|
*/
|
|
@@ -159,11 +177,16 @@ export type RestEndpointExtendOptions<
|
|
|
159
177
|
? Extract<O['schema'], Schema | undefined>
|
|
160
178
|
: E['schema']
|
|
161
179
|
> &
|
|
162
|
-
Partial<
|
|
180
|
+
Partial<
|
|
181
|
+
Omit<
|
|
182
|
+
E,
|
|
183
|
+
KeyofRestEndpoint | keyof PartialRestGenerics | keyof RestEndpointOptions
|
|
184
|
+
>
|
|
185
|
+
>;
|
|
163
186
|
|
|
164
187
|
type OptionsToRestEndpoint<
|
|
165
188
|
O extends PartialRestGenerics,
|
|
166
|
-
E extends RestInstanceBase & { body?: any },
|
|
189
|
+
E extends RestInstanceBase & { body?: any; paginationField?: string },
|
|
167
190
|
F extends FetchFunction,
|
|
168
191
|
> = 'path' extends keyof O
|
|
169
192
|
? RestType<
|
|
@@ -186,6 +209,9 @@ type OptionsToRestEndpoint<
|
|
|
186
209
|
? O['searchParams']
|
|
187
210
|
: E['searchParams'];
|
|
188
211
|
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
212
|
+
paginationField: 'paginationField' extends keyof O
|
|
213
|
+
? O['paginationField']
|
|
214
|
+
: E['paginationField'];
|
|
189
215
|
}
|
|
190
216
|
>
|
|
191
217
|
: 'body' extends keyof O
|
|
@@ -209,6 +235,9 @@ type OptionsToRestEndpoint<
|
|
|
209
235
|
? O['searchParams']
|
|
210
236
|
: E['searchParams'];
|
|
211
237
|
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
238
|
+
paginationField: 'paginationField' extends keyof O
|
|
239
|
+
? O['paginationField']
|
|
240
|
+
: Extract<E['paginationField'], string>;
|
|
212
241
|
}
|
|
213
242
|
>
|
|
214
243
|
: 'searchParams' extends keyof O
|
|
@@ -228,6 +257,9 @@ type OptionsToRestEndpoint<
|
|
|
228
257
|
body: E['body'];
|
|
229
258
|
searchParams: O['searchParams'];
|
|
230
259
|
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
260
|
+
paginationField: 'paginationField' extends keyof O
|
|
261
|
+
? O['paginationField']
|
|
262
|
+
: Extract<E['paginationField'], string>;
|
|
231
263
|
}
|
|
232
264
|
>
|
|
233
265
|
: RestInstance<
|
|
@@ -243,15 +275,21 @@ type OptionsToRestEndpoint<
|
|
|
243
275
|
? O['searchParams']
|
|
244
276
|
: E['searchParams'];
|
|
245
277
|
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
278
|
+
paginationField: 'paginationField' extends keyof O
|
|
279
|
+
? O['paginationField']
|
|
280
|
+
: E['paginationField'];
|
|
246
281
|
}
|
|
247
282
|
>;
|
|
248
283
|
|
|
249
284
|
export type RestExtendedEndpoint<
|
|
250
285
|
O extends PartialRestGenerics,
|
|
251
|
-
E extends RestInstanceBase,
|
|
286
|
+
E extends RestInstanceBase & { getPage?: unknown },
|
|
252
287
|
> = OptionsToRestEndpoint<
|
|
253
288
|
O,
|
|
254
|
-
E
|
|
289
|
+
E &
|
|
290
|
+
(E extends { getPage: { paginationField: string } }
|
|
291
|
+
? { paginationField: E['getPage']['paginationField'] }
|
|
292
|
+
: unknown),
|
|
255
293
|
RestInstance<
|
|
256
294
|
(
|
|
257
295
|
...args: Parameters<E>
|
|
@@ -266,13 +304,20 @@ export type RestExtendedEndpoint<
|
|
|
266
304
|
Omit<E, KeyofRestEndpoint | keyof O>;
|
|
267
305
|
|
|
268
306
|
export interface PartialRestGenerics {
|
|
307
|
+
/** @see https://resthooks.io/rest/api/RestEndpoint#path */
|
|
269
308
|
readonly path?: string;
|
|
309
|
+
/** @see https://resthooks.io/rest/api/RestEndpoint#schema */
|
|
270
310
|
readonly schema?: Schema | undefined;
|
|
311
|
+
/** @see https://resthooks.io/rest/api/RestEndpoint#method */
|
|
271
312
|
readonly method?: string;
|
|
272
313
|
/** Only used for types */
|
|
314
|
+
/** @see https://dataclient.io/rest/api/RestEndpoint#body */
|
|
273
315
|
body?: any;
|
|
274
316
|
/** Only used for types */
|
|
317
|
+
/** @see https://dataclient.io/rest/api/RestEndpoint#searchParams */
|
|
275
318
|
searchParams?: any;
|
|
319
|
+
/** @see https://dataclient.io/rest/api/RestEndpoint#paginationfield */
|
|
320
|
+
readonly paginationField?: string;
|
|
276
321
|
/** @see https://resthooks.io/rest/api/RestEndpoint#process */
|
|
277
322
|
process?(value: any, ...args: any): any;
|
|
278
323
|
}
|
|
@@ -283,7 +328,7 @@ export interface RestGenerics extends PartialRestGenerics {
|
|
|
283
328
|
export type PaginationEndpoint<
|
|
284
329
|
E extends FetchFunction & RestGenerics & { sideEffect?: true | undefined },
|
|
285
330
|
A extends any[],
|
|
286
|
-
> =
|
|
331
|
+
> = RestInstanceBase<
|
|
287
332
|
ParamFetchNoBody<A[0], ResolveType<E>>,
|
|
288
333
|
E['schema'],
|
|
289
334
|
E['sideEffect'],
|
|
@@ -294,7 +339,7 @@ export type PaginationEndpoint<
|
|
|
294
339
|
export type PaginationFieldEndpoint<
|
|
295
340
|
E extends FetchFunction & RestGenerics & { sideEffect?: true | undefined },
|
|
296
341
|
C extends string,
|
|
297
|
-
> =
|
|
342
|
+
> = RestInstanceBase<
|
|
298
343
|
ParamFetchNoBody<
|
|
299
344
|
{ [K in C]: string | number | boolean } & E['searchParams'] &
|
|
300
345
|
PathArgs<Exclude<E['path'], undefined>>,
|
|
@@ -305,7 +350,7 @@ export type PaginationFieldEndpoint<
|
|
|
305
350
|
Pick<E, 'path' | 'searchParams' | 'body'> & {
|
|
306
351
|
searchParams: { [K in C]: string | number | boolean } & E['searchParams'];
|
|
307
352
|
}
|
|
308
|
-
|
|
353
|
+
> & { paginationField: C };
|
|
309
354
|
export type AddEndpoint<
|
|
310
355
|
F extends FetchFunction = FetchFunction,
|
|
311
356
|
S extends Schema | undefined = any,
|
|
@@ -443,7 +488,8 @@ export type RestType<
|
|
|
443
488
|
path: string;
|
|
444
489
|
body?: any;
|
|
445
490
|
searchParams?: any;
|
|
446
|
-
|
|
491
|
+
paginationField?: string;
|
|
492
|
+
} = { path: string; paginationField: string },
|
|
447
493
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
448
494
|
> = IfTypeScriptLooseNull<
|
|
449
495
|
RestInstance<
|
|
@@ -576,6 +622,7 @@ export type GetEndpoint<
|
|
|
576
622
|
readonly schema: Schema;
|
|
577
623
|
/** Only used for types */
|
|
578
624
|
readonly searchParams?: any;
|
|
625
|
+
readonly paginationField?: string;
|
|
579
626
|
} = {
|
|
580
627
|
path: string;
|
|
581
628
|
schema: Schema;
|
package/src/RestEndpoint.js
CHANGED
|
@@ -231,6 +231,10 @@ Response (first 300 characters): ${text.substring(0, 300)}`;
|
|
|
231
231
|
return this.extend({ update: paginationUpdate(this, removeCursor) });
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
get getPage() {
|
|
235
|
+
return this.paginated(this.paginationField);
|
|
236
|
+
}
|
|
237
|
+
|
|
234
238
|
get push() {
|
|
235
239
|
return this.extend({
|
|
236
240
|
method: 'POST',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Entity } from '@data-client/endpoint';
|
|
1
|
+
import { Entity, schema } from '@data-client/endpoint';
|
|
2
2
|
import { useController } from '@data-client/react';
|
|
3
3
|
import { useSuspense } from '@data-client/react';
|
|
4
4
|
import { CacheProvider } from '@data-client/react';
|
|
@@ -86,6 +86,62 @@ const getNextPage2 = getArticleList2.paginated(
|
|
|
86
86
|
}) => [rest],
|
|
87
87
|
);
|
|
88
88
|
|
|
89
|
+
const getArticleList3 = new RestEndpoint({
|
|
90
|
+
urlPrefix: 'http://test.com',
|
|
91
|
+
path: '/article-paginated',
|
|
92
|
+
schema: {
|
|
93
|
+
nextPage: '',
|
|
94
|
+
data: { results: new schema.Collection([PaginatedArticle]) },
|
|
95
|
+
},
|
|
96
|
+
method: 'GET',
|
|
97
|
+
searchParams: {} as { group: string | number },
|
|
98
|
+
paginationField: 'cursor',
|
|
99
|
+
}).extend({
|
|
100
|
+
dataExpiryLength: 10000,
|
|
101
|
+
});
|
|
102
|
+
const getNextPage3 = getArticleList3.getPage;
|
|
103
|
+
|
|
104
|
+
// type tests
|
|
105
|
+
() => {
|
|
106
|
+
const base = new RestEndpoint({
|
|
107
|
+
urlPrefix: 'http://test.com',
|
|
108
|
+
path: '/article-paginated',
|
|
109
|
+
schema: {
|
|
110
|
+
nextPage: '',
|
|
111
|
+
data: { results: new schema.Collection([PaginatedArticle]) },
|
|
112
|
+
},
|
|
113
|
+
method: 'GET',
|
|
114
|
+
});
|
|
115
|
+
// @ts-expect-error
|
|
116
|
+
() => base.getPage();
|
|
117
|
+
() =>
|
|
118
|
+
// @ts-expect-error
|
|
119
|
+
base
|
|
120
|
+
.extend({
|
|
121
|
+
path: '',
|
|
122
|
+
dataExpiryLength: 10000,
|
|
123
|
+
})
|
|
124
|
+
.getPage();
|
|
125
|
+
const a = new RestEndpoint({
|
|
126
|
+
urlPrefix: 'http://test.com',
|
|
127
|
+
path: '/article-paginated',
|
|
128
|
+
schema: {
|
|
129
|
+
nextPage: '',
|
|
130
|
+
data: { results: new schema.Collection([PaginatedArticle]) },
|
|
131
|
+
},
|
|
132
|
+
method: 'GET',
|
|
133
|
+
searchParams: {} as { group: string | number },
|
|
134
|
+
}).extend({
|
|
135
|
+
path: ':blob',
|
|
136
|
+
searchParams: {} as { isAdmin?: boolean },
|
|
137
|
+
method: 'GET',
|
|
138
|
+
paginationField: 'cursor',
|
|
139
|
+
});
|
|
140
|
+
a.getPage({ cursor: 'hi', blob: 'ho' });
|
|
141
|
+
// @ts-expect-error
|
|
142
|
+
a.getPage({ blob: 'ho' });
|
|
143
|
+
};
|
|
144
|
+
|
|
89
145
|
describe('RestEndpoint', () => {
|
|
90
146
|
const renderRestHook: ReturnType<typeof makeRenderRestHook> =
|
|
91
147
|
makeRenderRestHook(CacheProvider);
|
|
@@ -277,26 +333,56 @@ describe('RestEndpoint', () => {
|
|
|
277
333
|
.get(`/article-paginated/happy?cursor=2`)
|
|
278
334
|
.reply(200, paginatedSecondPage);
|
|
279
335
|
|
|
280
|
-
const { result, waitForNextUpdate } = renderRestHook(() => {
|
|
281
|
-
const { fetch } = useController();
|
|
336
|
+
const { result, waitForNextUpdate, controller } = renderRestHook(() => {
|
|
282
337
|
const {
|
|
283
338
|
data: { results: articles },
|
|
284
339
|
nextPage,
|
|
285
340
|
} = useSuspense(getArticleList2, {
|
|
286
341
|
group: 'happy',
|
|
287
342
|
});
|
|
288
|
-
return { articles, nextPage
|
|
343
|
+
return { articles, nextPage };
|
|
344
|
+
});
|
|
345
|
+
await waitForNextUpdate();
|
|
346
|
+
// @ts-expect-error
|
|
347
|
+
() => controller.fetch(getNextPage2);
|
|
348
|
+
// @ts-expect-error
|
|
349
|
+
() => controller.fetch(getNextPage2, { fake: 5 });
|
|
350
|
+
// @ts-expect-error
|
|
351
|
+
() => controller.fetch(getNextPage2, { group: 'happy' });
|
|
352
|
+
// @ts-expect-error
|
|
353
|
+
() => controller.fetch(getNextPage2, { cursor: 2 });
|
|
354
|
+
await controller.fetch(getNextPage2, {
|
|
355
|
+
group: 'happy',
|
|
356
|
+
cursor: 2,
|
|
357
|
+
});
|
|
358
|
+
expect(result.current.articles.map(({ id }) => id)).toEqual([5, 3, 7, 8]);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('getPage: should update on get for a paginated resource with parameter in path', async () => {
|
|
362
|
+
mynock.get(`/article-paginated?group=happy`).reply(200, paginatedFirstPage);
|
|
363
|
+
mynock
|
|
364
|
+
.get(`/article-paginated?cursor=2&group=happy`)
|
|
365
|
+
.reply(200, paginatedSecondPage);
|
|
366
|
+
|
|
367
|
+
const { result, waitForNextUpdate, controller } = renderRestHook(() => {
|
|
368
|
+
const {
|
|
369
|
+
data: { results: articles },
|
|
370
|
+
nextPage,
|
|
371
|
+
} = useSuspense(getArticleList3, {
|
|
372
|
+
group: 'happy',
|
|
373
|
+
});
|
|
374
|
+
return { articles, nextPage };
|
|
289
375
|
});
|
|
290
376
|
await waitForNextUpdate();
|
|
291
377
|
// @ts-expect-error
|
|
292
|
-
() =>
|
|
378
|
+
() => controller.fetch(getNextPage3);
|
|
293
379
|
// @ts-expect-error
|
|
294
|
-
() =>
|
|
380
|
+
() => controller.fetch(getNextPage3, { fake: 5 });
|
|
295
381
|
// @ts-expect-error
|
|
296
|
-
() =>
|
|
382
|
+
() => controller.fetch(getNextPage3, { group: 'happy' });
|
|
297
383
|
// @ts-expect-error
|
|
298
|
-
() =>
|
|
299
|
-
await
|
|
384
|
+
() => controller.fetch(getNextPage3, { cursor: 2 });
|
|
385
|
+
await controller.fetch(getNextPage3, {
|
|
300
386
|
group: 'happy',
|
|
301
387
|
cursor: 2,
|
|
302
388
|
});
|
|
@@ -84,9 +84,12 @@ describe('createResource()', () => {
|
|
|
84
84
|
path: 'http\\://test.com/groups/:group/users/:id',
|
|
85
85
|
schema: User,
|
|
86
86
|
});
|
|
87
|
+
// @ts-expect-error
|
|
88
|
+
UserResourceBase.getList.getPage.paginationField;
|
|
87
89
|
const UserResource = UserResourceBase.extend('getList', {
|
|
88
90
|
path: ':blob',
|
|
89
91
|
searchParams: {} as { isAdmin?: boolean },
|
|
92
|
+
paginationField: 'cursor',
|
|
90
93
|
getOptimisticResponse(snap, params) {
|
|
91
94
|
params.isAdmin;
|
|
92
95
|
params.blob;
|
|
@@ -94,9 +97,10 @@ describe('createResource()', () => {
|
|
|
94
97
|
params.nothere;
|
|
95
98
|
return [] as User[];
|
|
96
99
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
process(users: User[]) {
|
|
101
|
+
if (!Array.isArray(users)) return users;
|
|
102
|
+
return users.slice(0, 7);
|
|
103
|
+
},
|
|
100
104
|
})
|
|
101
105
|
.extend('partialUpdate', {
|
|
102
106
|
getOptimisticResponse(snap, params, body) {
|
|
@@ -128,19 +132,18 @@ describe('createResource()', () => {
|
|
|
128
132
|
|
|
129
133
|
() => UserResource.getList({ blob: '5', isAdmin: true });
|
|
130
134
|
() =>
|
|
131
|
-
UserResource.getList.
|
|
135
|
+
UserResource.getList.getPage({
|
|
132
136
|
blob: '5',
|
|
133
137
|
isAdmin: true,
|
|
134
138
|
cursor: 'next',
|
|
135
139
|
});
|
|
136
|
-
() =>
|
|
137
|
-
UserResource.getList.paginated('cursor')({ blob: '5', cursor: 'next' });
|
|
140
|
+
() => UserResource.getList.getPage({ blob: '5', cursor: 'next' });
|
|
138
141
|
() =>
|
|
139
142
|
// @ts-expect-error
|
|
140
|
-
UserResource.getList.
|
|
143
|
+
UserResource.getList.getPage({ blob: '5', isAdmin: true });
|
|
141
144
|
() =>
|
|
142
145
|
// @ts-expect-error
|
|
143
|
-
UserResource.getList.
|
|
146
|
+
UserResource.getList.getPage({
|
|
144
147
|
cursor: 'next',
|
|
145
148
|
isAdmin: true,
|
|
146
149
|
});
|
|
@@ -208,6 +211,7 @@ describe('createResource()', () => {
|
|
|
208
211
|
const UserResourceBase = createResource({
|
|
209
212
|
path: 'http\\://test.com/groups/:group/users/:id',
|
|
210
213
|
schema: User,
|
|
214
|
+
paginationField: 'cursor',
|
|
211
215
|
});
|
|
212
216
|
const UserResource = UserResourceBase.extend({
|
|
213
217
|
getList: {
|
|
@@ -263,17 +267,16 @@ describe('createResource()', () => {
|
|
|
263
267
|
|
|
264
268
|
() => UserResource.getList({ blob: '5', isAdmin: true });
|
|
265
269
|
() =>
|
|
266
|
-
UserResource.getList.
|
|
270
|
+
UserResource.getList.getPage({
|
|
267
271
|
blob: '5',
|
|
268
272
|
isAdmin: true,
|
|
269
273
|
cursor: 'next',
|
|
270
274
|
});
|
|
271
|
-
() =>
|
|
272
|
-
UserResource.getList.paginated('cursor')({ blob: '5', cursor: 'next' });
|
|
275
|
+
() => UserResource.getList.getPage({ blob: '5', cursor: 'next' });
|
|
273
276
|
// @ts-expect-error
|
|
274
|
-
() => UserResource.
|
|
277
|
+
() => UserResource.getList.getPage({ blob: '5', isAdmin: true });
|
|
275
278
|
// @ts-expect-error
|
|
276
|
-
() => UserResource.
|
|
279
|
+
() => UserResource.getList.getPage({ cursor: 'next', isAdmin: true });
|
|
277
280
|
() => UserResource.get({ group: '1', id: '5' });
|
|
278
281
|
() => UserResource.getList.push({ blob: '5' }, { username: 'bob' });
|
|
279
282
|
() =>
|
|
@@ -357,69 +360,71 @@ describe('createResource()', () => {
|
|
|
357
360
|
path: 'http\\://test.com/groups/:group/users/:id',
|
|
358
361
|
schema: User,
|
|
359
362
|
paginationField: 'cursor',
|
|
360
|
-
})
|
|
361
|
-
.extend(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
getNextPage: resourceBase.getList.paginated('cursor'),
|
|
414
|
-
}));
|
|
363
|
+
}).extend(resourceBase => ({
|
|
364
|
+
getList: resourceBase.getList.extend({
|
|
365
|
+
path: ':blob',
|
|
366
|
+
searchParams: {} as { isAdmin?: boolean },
|
|
367
|
+
getOptimisticResponse(snap, params) {
|
|
368
|
+
params.isAdmin;
|
|
369
|
+
params.blob;
|
|
370
|
+
// @ts-expect-error
|
|
371
|
+
params.nothere;
|
|
372
|
+
return [] as User[];
|
|
373
|
+
},
|
|
374
|
+
process(users: User[]) {
|
|
375
|
+
if (!Array.isArray(users)) return users;
|
|
376
|
+
return users.slice(0, 7);
|
|
377
|
+
},
|
|
378
|
+
}),
|
|
379
|
+
partialUpdate: resourceBase.partialUpdate.extend({
|
|
380
|
+
getOptimisticResponse(snap, params, body) {
|
|
381
|
+
params.id;
|
|
382
|
+
params.group;
|
|
383
|
+
// @ts-expect-error
|
|
384
|
+
params.nothere;
|
|
385
|
+
return {
|
|
386
|
+
id: params.id,
|
|
387
|
+
...body,
|
|
388
|
+
};
|
|
389
|
+
},
|
|
390
|
+
}),
|
|
391
|
+
delete: resourceBase.delete.extend({
|
|
392
|
+
getOptimisticResponse(snap, params) {
|
|
393
|
+
return params;
|
|
394
|
+
},
|
|
395
|
+
}),
|
|
396
|
+
justget: resourceBase.get,
|
|
397
|
+
current: resourceBase.get.extend({
|
|
398
|
+
path: '/current',
|
|
399
|
+
searchParams: {} as { isAdmin?: boolean },
|
|
400
|
+
}),
|
|
401
|
+
toggleAdmin: resourceBase.get.extend({
|
|
402
|
+
path: '/toggle/:id',
|
|
403
|
+
method: 'POST',
|
|
404
|
+
body: undefined,
|
|
405
|
+
getOptimisticResponse(snap, params) {
|
|
406
|
+
params.id;
|
|
407
|
+
// @ts-expect-error
|
|
408
|
+
params.group;
|
|
409
|
+
return {
|
|
410
|
+
id: params.id,
|
|
411
|
+
};
|
|
412
|
+
},
|
|
413
|
+
}),
|
|
414
|
+
}));
|
|
415
|
+
|
|
415
416
|
() => UserResource.getList({ blob: '5', isAdmin: true });
|
|
416
417
|
() =>
|
|
417
|
-
UserResource.
|
|
418
|
-
|
|
418
|
+
UserResource.getList.getPage({
|
|
419
|
+
blob: '5',
|
|
420
|
+
isAdmin: true,
|
|
421
|
+
cursor: 'next',
|
|
422
|
+
});
|
|
423
|
+
() => UserResource.getList.getPage({ blob: '5', cursor: 'next' });
|
|
419
424
|
// @ts-expect-error
|
|
420
|
-
() => UserResource.
|
|
425
|
+
() => UserResource.getList.getPage({ blob: '5', isAdmin: true });
|
|
421
426
|
// @ts-expect-error
|
|
422
|
-
() => UserResource.
|
|
427
|
+
() => UserResource.getList.getPage({ cursor: 'next', isAdmin: true });
|
|
423
428
|
() => UserResource.get({ group: '1', id: '5' });
|
|
424
429
|
() => UserResource.getList.unshift({ blob: '5' }, { username: 'bob' });
|
|
425
430
|
() =>
|
package/src/createResource.ts
CHANGED
|
@@ -30,6 +30,7 @@ export default function createResource<O extends ResourceGenerics>({
|
|
|
30
30
|
schema,
|
|
31
31
|
Endpoint = RestEndpoint,
|
|
32
32
|
optimistic,
|
|
33
|
+
paginationField,
|
|
33
34
|
...extraOptions
|
|
34
35
|
}: Readonly<O> & ResourceOptions): Resource<O> {
|
|
35
36
|
const shortenedPath = shortenPath(path);
|
|
@@ -50,10 +51,13 @@ export default function createResource<O extends ResourceGenerics>({
|
|
|
50
51
|
}) as any;
|
|
51
52
|
if (optimistic) {
|
|
52
53
|
(extraMutateOptions as any).getOptimisticResponse = optimisticUpdate;
|
|
53
|
-
(extraPartialOptions as any).getOptimisticResponse = optimisticPartial(
|
|
54
|
+
(extraPartialOptions as any).getOptimisticResponse = optimisticPartial(
|
|
55
|
+
get as any,
|
|
56
|
+
);
|
|
54
57
|
}
|
|
55
58
|
const getList = new Endpoint({
|
|
56
59
|
...extraMutateOptions,
|
|
60
|
+
paginationField: paginationField as string,
|
|
57
61
|
path: shortenedPath,
|
|
58
62
|
schema: new Collection([schema as any]),
|
|
59
63
|
name: getName('getList'),
|
|
@@ -133,7 +137,7 @@ export default function createResource<O extends ResourceGenerics>({
|
|
|
133
137
|
function optimisticUpdate(snap: SnapshotInterface, params: any, body: any) {
|
|
134
138
|
return {
|
|
135
139
|
...params,
|
|
136
|
-
...body,
|
|
140
|
+
...ensureBodyPojo(body),
|
|
137
141
|
};
|
|
138
142
|
}
|
|
139
143
|
function optimisticPartial(
|
|
@@ -146,10 +150,15 @@ function optimisticPartial(
|
|
|
146
150
|
...params,
|
|
147
151
|
...data,
|
|
148
152
|
// even tho we don't always have two arguments, the extra one will simply be undefined which spreads fine
|
|
149
|
-
...body,
|
|
153
|
+
...ensureBodyPojo(body),
|
|
150
154
|
};
|
|
151
155
|
};
|
|
152
156
|
}
|
|
153
157
|
function optimisticDelete(snap: SnapshotInterface, params: any) {
|
|
154
158
|
return params;
|
|
155
159
|
}
|
|
160
|
+
function ensureBodyPojo(body: any) {
|
|
161
|
+
return body instanceof FormData
|
|
162
|
+
? Object.fromEntries((body as any).entries())
|
|
163
|
+
: body;
|
|
164
|
+
}
|
|
@@ -9,9 +9,9 @@ export function paginatedMerge(existing: any[], incoming: any[]) {
|
|
|
9
9
|
export const paginatedFilter =
|
|
10
10
|
<C extends (...args: readonly any[]) => readonly any[]>(removeCursor: C) =>
|
|
11
11
|
(...args: Parameters<C>) => {
|
|
12
|
-
const noCursorArgs = removeCursor(...args);
|
|
12
|
+
const noCursorArgs = removeCursor(...args)[0] ?? {};
|
|
13
13
|
return (collectionKey: Record<string, string>) =>
|
|
14
|
-
shallowEqual(collectionKey, noCursorArgs
|
|
14
|
+
shallowEqual(collectionKey, noCursorArgs);
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
function shallowEqual(
|
|
@@ -20,13 +20,17 @@ function shallowEqual(
|
|
|
20
20
|
) {
|
|
21
21
|
const keys1 = Object.keys(object1);
|
|
22
22
|
const keys2 = Object.keys(object2);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (object1[key]
|
|
23
|
+
let length2 = 0;
|
|
24
|
+
for (const key of keys2) {
|
|
25
|
+
// we should ignore any members with value undefined
|
|
26
|
+
if (object2[key] === undefined) continue;
|
|
27
|
+
if (object1[key] !== `${object2[key]}`) {
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
|
+
length2++;
|
|
31
|
+
}
|
|
32
|
+
if (keys1.length !== length2) {
|
|
33
|
+
return false;
|
|
30
34
|
}
|
|
31
35
|
return true;
|
|
32
36
|
}
|