@data-client/rest 0.9.9 → 0.11.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/README.md +20 -3
- package/dist/index.js +13 -7
- package/dist/index.umd.min.js +1 -1
- package/legacy/NetworkError.js +1 -1
- package/legacy/OptionsToFunction.js +1 -1
- package/legacy/RestEndpoint.js +7 -2
- package/legacy/RestEndpointTypeHelp.js +1 -1
- package/legacy/RestEndpointTypes.js +1 -1
- package/legacy/RestHelpers.js +1 -1
- package/legacy/createResource.js +7 -8
- package/legacy/extractCollection.js +1 -1
- package/legacy/extractObject.js +1 -1
- package/legacy/hookifyResource.js +1 -1
- package/legacy/index.js +2 -1
- package/legacy/mapCollection.js +1 -1
- package/legacy/next/index.js +1 -1
- package/legacy/paginatedCollections.js +1 -1
- package/legacy/paramsToString.js +1 -1
- package/legacy/paramsToString.native.js +1 -1
- package/legacy/pathTypes.js +1 -1
- package/legacy/resourceExtendable.js +1 -1
- package/legacy/resourceExtensionTypes.js +1 -1
- package/legacy/resourceTypes.js +1 -1
- package/legacy/test-fixtures.js +1 -1
- package/legacy/utiltypes.js +1 -1
- package/lib/NetworkError.js +1 -1
- package/lib/OptionsToFunction.d.ts.map +1 -1
- package/lib/OptionsToFunction.js +1 -1
- package/lib/RestEndpoint.js +7 -2
- package/lib/RestEndpointTypeHelp.js +1 -1
- package/lib/RestEndpointTypes.d.ts +32 -8
- package/lib/RestEndpointTypes.d.ts.map +1 -1
- package/lib/RestEndpointTypes.js +1 -1
- package/lib/RestHelpers.js +1 -1
- package/lib/createResource.d.ts.map +1 -1
- package/lib/createResource.js +7 -8
- package/lib/extractCollection.js +1 -1
- package/lib/extractObject.js +1 -1
- package/lib/hookifyResource.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/mapCollection.d.ts.map +1 -1
- package/lib/mapCollection.js +1 -1
- package/lib/next/index.js +1 -1
- package/lib/paginatedCollections.js +1 -1
- package/lib/paramsToString.d.ts +1 -1
- package/lib/paramsToString.d.ts.map +1 -1
- package/lib/paramsToString.js +1 -1
- package/lib/paramsToString.native.d.ts +1 -1
- package/lib/paramsToString.native.d.ts.map +1 -1
- package/lib/paramsToString.native.js +1 -1
- package/lib/pathTypes.js +1 -1
- package/lib/resourceExtendable.js +1 -1
- package/lib/resourceExtensionTypes.js +1 -1
- package/lib/resourceTypes.d.ts +4 -8
- package/lib/resourceTypes.d.ts.map +1 -1
- package/lib/resourceTypes.js +1 -1
- package/lib/test-fixtures.js +1 -1
- package/lib/utiltypes.js +1 -1
- package/package.json +2 -2
- package/src/OptionsToFunction.ts +44 -42
- package/src/RestEndpoint.js +6 -1
- package/src/RestEndpointTypes.ts +128 -110
- package/src/__tests__/RestEndpoint.ts +54 -2
- package/src/__tests__/createResource.test.ts +42 -2
- package/src/createResource.ts +7 -12
- package/src/index.ts +1 -0
- package/src/mapCollection.ts +5 -4
- package/src/paramsToString.native.ts +6 -1
- package/src/paramsToString.ts +7 -2
- package/src/resourceTypes.ts +16 -15
- package/ts4.0/RestEndpointTypes.d.ts +38 -8
- package/ts4.0/index.d.ts +1 -0
- package/ts4.0/paramsToString.d.ts +1 -1
- package/ts4.0/paramsToString.native.d.ts +1 -1
- package/ts4.0/resourceTypes.d.ts +4 -8
- package/ts4.1/RestEndpointTypes.d.ts +38 -8
- package/ts4.1/index.d.ts +1 -0
- package/ts4.1/paramsToString.d.ts +1 -1
- package/ts4.1/paramsToString.native.d.ts +1 -1
- package/ts4.1/resourceTypes.d.ts +4 -8
package/src/RestEndpointTypes.ts
CHANGED
|
@@ -42,31 +42,53 @@ export interface RestInstanceBase<
|
|
|
42
42
|
*/
|
|
43
43
|
readonly urlPrefix: string;
|
|
44
44
|
readonly requestInit: RequestInit;
|
|
45
|
-
/**
|
|
45
|
+
/** HTTP request method
|
|
46
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#method
|
|
47
|
+
*/
|
|
46
48
|
readonly method: (O & { method: string })['method'];
|
|
47
49
|
readonly signal: AbortSignal | undefined;
|
|
50
|
+
/** @see https://dataclient.io/rest/api/RestEndpoint#paginationField */
|
|
48
51
|
readonly paginationField?: string;
|
|
49
52
|
|
|
50
53
|
/* fetch lifecycles */
|
|
51
54
|
/* before-fetch */
|
|
55
|
+
/** Builds the URL to fetch
|
|
56
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#url
|
|
57
|
+
*/
|
|
52
58
|
url(...args: Parameters<F>): string;
|
|
53
|
-
/**
|
|
59
|
+
/** Encode the searchParams component of the url
|
|
60
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#searchToString
|
|
61
|
+
*/
|
|
62
|
+
searchToString(searchParams: Record<string, any>): string;
|
|
63
|
+
/** Prepares RequestInit used in fetch. This is sent to fetchResponse()
|
|
64
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#getRequestInit
|
|
65
|
+
*/
|
|
54
66
|
getRequestInit(
|
|
55
67
|
this: any,
|
|
56
68
|
body?: RequestInit['body'] | Record<string, unknown>,
|
|
57
69
|
): Promise<RequestInit> | RequestInit;
|
|
58
|
-
/**
|
|
70
|
+
/** Called by getRequestInit to determine HTTP Headers
|
|
71
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#getHeaders
|
|
72
|
+
*/
|
|
59
73
|
getHeaders(headers: HeadersInit): Promise<HeadersInit> | HeadersInit;
|
|
60
74
|
/* after-fetch */
|
|
61
|
-
/**
|
|
75
|
+
/** Performs the fetch call
|
|
76
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#fetchResponse
|
|
77
|
+
*/
|
|
62
78
|
fetchResponse(input: RequestInfo, init: RequestInit): Promise<Response>;
|
|
63
|
-
/**
|
|
79
|
+
/** Takes the Response and parses via .text() or .json()
|
|
80
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#parseResponse
|
|
81
|
+
*/
|
|
64
82
|
parseResponse(response: Response): Promise<any>;
|
|
65
|
-
/**
|
|
83
|
+
/** Perform any transforms with the parsed result.
|
|
84
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#process
|
|
85
|
+
*/
|
|
66
86
|
process(value: any, ...args: Parameters<F>): ResolveType<F>;
|
|
67
87
|
|
|
68
88
|
/* utilities */
|
|
69
|
-
/**
|
|
89
|
+
/** Returns true if the provided (fetch) key matches this endpoint.
|
|
90
|
+
* @see https://dataclient.io/rest/api/RestEndpoint#testKey
|
|
91
|
+
*/
|
|
70
92
|
testKey(key: string): boolean;
|
|
71
93
|
|
|
72
94
|
/* extenders */
|
|
@@ -186,89 +208,95 @@ type OptionsToRestEndpoint<
|
|
|
186
208
|
O extends PartialRestGenerics,
|
|
187
209
|
E extends RestInstanceBase & { body?: any; paginationField?: string },
|
|
188
210
|
F extends FetchFunction,
|
|
189
|
-
> =
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
> =
|
|
212
|
+
'path' extends keyof O ?
|
|
213
|
+
RestType<
|
|
214
|
+
'searchParams' extends keyof O ?
|
|
215
|
+
O['searchParams'] extends undefined ?
|
|
216
|
+
PathArgs<Exclude<O['path'], undefined>>
|
|
217
|
+
: O['searchParams'] & PathArgs<Exclude<O['path'], undefined>>
|
|
218
|
+
: PathArgs<Exclude<O['path'], undefined>>,
|
|
219
|
+
OptionsToBodyArgument<
|
|
220
|
+
'body' extends keyof O ? O : E,
|
|
221
|
+
'method' extends keyof O ? O['method'] : E['method']
|
|
222
|
+
>,
|
|
223
|
+
'schema' extends keyof O ? O['schema'] : E['schema'],
|
|
224
|
+
'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
|
|
225
|
+
O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
|
|
226
|
+
{
|
|
227
|
+
path: Exclude<O['path'], undefined>;
|
|
228
|
+
body: 'body' extends keyof O ? O['body'] : E['body'];
|
|
229
|
+
searchParams: 'searchParams' extends keyof O ? O['searchParams']
|
|
230
|
+
: E['searchParams'];
|
|
231
|
+
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
232
|
+
paginationField: 'paginationField' extends keyof O ?
|
|
233
|
+
O['paginationField']
|
|
234
|
+
: E['paginationField'];
|
|
235
|
+
}
|
|
236
|
+
>
|
|
237
|
+
: 'body' extends keyof O ?
|
|
238
|
+
RestType<
|
|
239
|
+
'searchParams' extends keyof O ?
|
|
240
|
+
O['searchParams'] extends undefined ?
|
|
241
|
+
PathArgs<Exclude<O['path'], undefined>>
|
|
242
|
+
: O['searchParams'] & PathArgs<Exclude<E['path'], undefined>>
|
|
243
|
+
: PathArgs<Exclude<E['path'], undefined>>,
|
|
244
|
+
OptionsToBodyArgument<
|
|
245
|
+
O,
|
|
246
|
+
'method' extends keyof O ? O['method'] : E['method']
|
|
247
|
+
>,
|
|
248
|
+
'schema' extends keyof O ? O['schema'] : E['schema'],
|
|
249
|
+
'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
|
|
250
|
+
O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
|
|
251
|
+
{
|
|
252
|
+
path: E['path'];
|
|
253
|
+
body: O['body'];
|
|
254
|
+
searchParams: 'searchParams' extends keyof O ? O['searchParams']
|
|
255
|
+
: E['searchParams'];
|
|
256
|
+
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
257
|
+
paginationField: 'paginationField' extends keyof O ?
|
|
258
|
+
O['paginationField']
|
|
259
|
+
: Extract<E['paginationField'], string>;
|
|
260
|
+
}
|
|
261
|
+
>
|
|
262
|
+
: 'searchParams' extends keyof O ?
|
|
263
|
+
RestType<
|
|
216
264
|
O['searchParams'] extends undefined ?
|
|
217
265
|
PathArgs<Exclude<O['path'], undefined>>
|
|
218
|
-
: O['searchParams'] & PathArgs<Exclude<E['path'], undefined
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
searchParams: O['searchParams'];
|
|
253
|
-
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
254
|
-
paginationField: 'paginationField' extends keyof O ? O['paginationField']
|
|
255
|
-
: Extract<E['paginationField'], string>;
|
|
256
|
-
}
|
|
257
|
-
>
|
|
258
|
-
: RestInstance<
|
|
259
|
-
F,
|
|
260
|
-
'schema' extends keyof O ? O['schema'] : E['schema'],
|
|
261
|
-
'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
|
|
262
|
-
{
|
|
263
|
-
path: 'path' extends keyof O ? Exclude<O['path'], undefined> : E['path'];
|
|
264
|
-
body: 'body' extends keyof O ? O['body'] : E['body'];
|
|
265
|
-
searchParams: 'searchParams' extends keyof O ? O['searchParams']
|
|
266
|
-
: E['searchParams'];
|
|
267
|
-
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
268
|
-
paginationField: 'paginationField' extends keyof O ? O['paginationField']
|
|
269
|
-
: E['paginationField'];
|
|
270
|
-
}
|
|
271
|
-
>;
|
|
266
|
+
: O['searchParams'] & PathArgs<Exclude<E['path'], undefined>>,
|
|
267
|
+
OptionsToBodyArgument<
|
|
268
|
+
E,
|
|
269
|
+
'method' extends keyof O ? O['method'] : E['method']
|
|
270
|
+
>,
|
|
271
|
+
'schema' extends keyof O ? O['schema'] : E['schema'],
|
|
272
|
+
'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
|
|
273
|
+
O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
|
|
274
|
+
{
|
|
275
|
+
path: E['path'];
|
|
276
|
+
body: E['body'];
|
|
277
|
+
searchParams: O['searchParams'];
|
|
278
|
+
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
279
|
+
paginationField: 'paginationField' extends keyof O ?
|
|
280
|
+
O['paginationField']
|
|
281
|
+
: Extract<E['paginationField'], string>;
|
|
282
|
+
}
|
|
283
|
+
>
|
|
284
|
+
: RestInstance<
|
|
285
|
+
F,
|
|
286
|
+
'schema' extends keyof O ? O['schema'] : E['schema'],
|
|
287
|
+
'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
|
|
288
|
+
{
|
|
289
|
+
path: 'path' extends keyof O ? Exclude<O['path'], undefined>
|
|
290
|
+
: E['path'];
|
|
291
|
+
body: 'body' extends keyof O ? O['body'] : E['body'];
|
|
292
|
+
searchParams: 'searchParams' extends keyof O ? O['searchParams']
|
|
293
|
+
: E['searchParams'];
|
|
294
|
+
method: 'method' extends keyof O ? O['method'] : E['method'];
|
|
295
|
+
paginationField: 'paginationField' extends keyof O ?
|
|
296
|
+
O['paginationField']
|
|
297
|
+
: E['paginationField'];
|
|
298
|
+
}
|
|
299
|
+
>;
|
|
272
300
|
|
|
273
301
|
export type RestExtendedEndpoint<
|
|
274
302
|
O extends PartialRestGenerics,
|
|
@@ -465,26 +493,7 @@ export type RestType<
|
|
|
465
493
|
} = { path: string; paginationField: string },
|
|
466
494
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
467
495
|
> = IfTypeScriptLooseNull<
|
|
468
|
-
RestInstance<
|
|
469
|
-
keyof UrlParams extends never ?
|
|
470
|
-
(this: EndpointInstanceInterface, body?: Body) => Promise<R>
|
|
471
|
-
: // even with loose null, this will only be true when all members are optional
|
|
472
|
-
{} extends UrlParams ?
|
|
473
|
-
| ((this: EndpointInstanceInterface, body?: Body) => Promise<R>)
|
|
474
|
-
| ((
|
|
475
|
-
this: EndpointInstanceInterface,
|
|
476
|
-
params: UrlParams,
|
|
477
|
-
body?: Body,
|
|
478
|
-
) => Promise<R>)
|
|
479
|
-
: (
|
|
480
|
-
this: EndpointInstanceInterface,
|
|
481
|
-
params: UrlParams,
|
|
482
|
-
body?: Body,
|
|
483
|
-
) => Promise<R>,
|
|
484
|
-
S,
|
|
485
|
-
M,
|
|
486
|
-
O
|
|
487
|
-
>,
|
|
496
|
+
RestInstance<RestFetch<UrlParams, Body, R>, S, M, O>,
|
|
488
497
|
Body extends {} ? RestTypeWithBody<UrlParams, S, M, Body, R, O>
|
|
489
498
|
: RestTypeNoBody<UrlParams, S, M, R, O>
|
|
490
499
|
>;
|
|
@@ -551,12 +560,21 @@ export type ParamFetchNoBody<P, R = any> =
|
|
|
551
560
|
| ((this: EndpointInstanceInterface) => Promise<R>)
|
|
552
561
|
: (this: EndpointInstanceInterface, params: P) => Promise<R>;
|
|
553
562
|
|
|
563
|
+
// same algorithm, but for Args (aka readonly any[])
|
|
564
|
+
export type ParamToArgs<P> =
|
|
565
|
+
P extends undefined ? []
|
|
566
|
+
: {} extends P ?
|
|
567
|
+
keyof P extends never ?
|
|
568
|
+
[]
|
|
569
|
+
: [] | [P]
|
|
570
|
+
: [P];
|
|
571
|
+
|
|
554
572
|
type IfTypeScriptLooseNull<Y, N> = 1 | undefined extends 1 ? Y : N;
|
|
555
573
|
|
|
556
574
|
export type KeyofRestEndpoint = keyof RestInstance;
|
|
557
575
|
|
|
558
|
-
export type FromFallBack<K extends keyof E, O, E> =
|
|
559
|
-
: E[K];
|
|
576
|
+
export type FromFallBack<K extends keyof E, O, E> =
|
|
577
|
+
K extends keyof O ? O[K] : E[K];
|
|
560
578
|
|
|
561
579
|
export type FetchMutate<
|
|
562
580
|
A extends readonly any[] = // eslint-disable-next-line @typescript-eslint/ban-types
|
|
@@ -761,7 +761,7 @@ describe('RestEndpoint', () => {
|
|
|
761
761
|
() => useSuspense(getUser);
|
|
762
762
|
});
|
|
763
763
|
|
|
764
|
-
it('should work with extends', async () => {
|
|
764
|
+
it('update should work with extends', async () => {
|
|
765
765
|
mynock.put('/6/user/5').reply(200, (uri, body: any) => ({
|
|
766
766
|
id: 5,
|
|
767
767
|
username: 'charles',
|
|
@@ -792,7 +792,7 @@ describe('RestEndpoint', () => {
|
|
|
792
792
|
`);
|
|
793
793
|
});
|
|
794
794
|
|
|
795
|
-
it('should work with extends', async () => {
|
|
795
|
+
it('get should work with extends', async () => {
|
|
796
796
|
mynock.get('/6/user/5').reply(200, (uri, body: any) => ({
|
|
797
797
|
id: 5,
|
|
798
798
|
username2: 'charles',
|
|
@@ -961,7 +961,59 @@ describe('RestEndpoint', () => {
|
|
|
961
961
|
).toMatchInlineSnapshot(`"/users?bigger=true"`);
|
|
962
962
|
expect(searchParams4.url()).toMatchInlineSnapshot(`"/users"`);
|
|
963
963
|
});
|
|
964
|
+
|
|
965
|
+
it('should work with custom searchToString', async () => {
|
|
966
|
+
class SearchEndpoint<O extends RestGenerics = any> extends MyEndpoint<O> {
|
|
967
|
+
searchToString(searchParams: Record<string, any>) {
|
|
968
|
+
return super.searchToString({ ...searchParams, bob: 5 });
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
mynock.get('/6/user/5').reply(200, (uri, body: any) => ({
|
|
973
|
+
id: 5,
|
|
974
|
+
username2: 'charles',
|
|
975
|
+
...body,
|
|
976
|
+
}));
|
|
977
|
+
class User2 extends Entity {
|
|
978
|
+
readonly id: number | undefined = undefined;
|
|
979
|
+
readonly username2: string = '';
|
|
980
|
+
readonly email: string = '';
|
|
981
|
+
readonly isAdmin: boolean = false;
|
|
982
|
+
|
|
983
|
+
pk() {
|
|
984
|
+
return this.id?.toString();
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
const getUserBase = new SearchEndpoint({
|
|
989
|
+
method: 'GET',
|
|
990
|
+
path: 'http\\://test.com/user/:id',
|
|
991
|
+
name: 'getuser',
|
|
992
|
+
schema: User,
|
|
993
|
+
});
|
|
994
|
+
const getUser = getUserBase.extend({
|
|
995
|
+
path: 'http\\://test.com/:group/user/:id',
|
|
996
|
+
schema: User2,
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
const searchParams = getUser.extend({
|
|
1000
|
+
path: 'http\\://test.com/:group/user/:id',
|
|
1001
|
+
searchParams: {} as { isAdmin?: boolean; sort: 'asc' | 'desc' },
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
expect(
|
|
1005
|
+
searchParams.url({
|
|
1006
|
+
group: 'hi',
|
|
1007
|
+
id: 'what',
|
|
1008
|
+
sort: 'desc',
|
|
1009
|
+
isAdmin: true,
|
|
1010
|
+
}),
|
|
1011
|
+
).toMatchInlineSnapshot(
|
|
1012
|
+
`"http://test.com/hi/user/what?bob=5&isAdmin=true&sort=desc"`,
|
|
1013
|
+
);
|
|
1014
|
+
});
|
|
964
1015
|
});
|
|
1016
|
+
|
|
965
1017
|
it('extending with name should work', () => {
|
|
966
1018
|
const endpoint = CoolerArticleResource.get.extend({ name: 'mything' });
|
|
967
1019
|
const endpoint2 = CoolerArticleResource.get.extend({ path: '/:bob' });
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Entity, schema } from '@data-client/endpoint';
|
|
1
|
+
import { Entity, schema, SchemaArgs } from '@data-client/endpoint';
|
|
2
2
|
import {
|
|
3
3
|
CacheProvider,
|
|
4
4
|
useCache,
|
|
5
5
|
useController,
|
|
6
6
|
Controller,
|
|
7
7
|
useSuspense,
|
|
8
|
+
useQuery,
|
|
8
9
|
} from '@data-client/react';
|
|
9
10
|
import { makeRenderDataClient } from '@data-client/test';
|
|
10
11
|
import { act } from '@testing-library/react-hooks';
|
|
@@ -205,6 +206,9 @@ describe('createResource()', () => {
|
|
|
205
206
|
expect(user.username).toBe('newbob');
|
|
206
207
|
expect(user).toBeInstanceOf(User);
|
|
207
208
|
expect(user.isAdmin).toBe(false);
|
|
209
|
+
|
|
210
|
+
// check types
|
|
211
|
+
() => controller.getResponse(UserResource.current, controller.getState());
|
|
208
212
|
});
|
|
209
213
|
|
|
210
214
|
it('can override endpoint options', async () => {
|
|
@@ -837,7 +841,7 @@ describe('createResource()', () => {
|
|
|
837
841
|
}
|
|
838
842
|
const FeedUnion = new schema.Union(
|
|
839
843
|
{ post: FeedPost, link: FeedLink },
|
|
840
|
-
'type'
|
|
844
|
+
'type',
|
|
841
845
|
);
|
|
842
846
|
const FeedResource = createResource({
|
|
843
847
|
path: 'http\\://test.com/feed/:id',
|
|
@@ -1094,4 +1098,40 @@ describe('createResource()', () => {
|
|
|
1094
1098
|
// this is set in our override
|
|
1095
1099
|
expect(result.current[0].isAdmin).toBe(true);
|
|
1096
1100
|
});
|
|
1101
|
+
|
|
1102
|
+
it('searchParams are used in Queries based on getList.schema', () => {
|
|
1103
|
+
class Todo extends Entity {
|
|
1104
|
+
id = '';
|
|
1105
|
+
readonly userId: number = 0;
|
|
1106
|
+
readonly title: string = '';
|
|
1107
|
+
readonly completed: boolean = false;
|
|
1108
|
+
|
|
1109
|
+
static key = 'Todo';
|
|
1110
|
+
|
|
1111
|
+
pk() {
|
|
1112
|
+
return this.id;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
const TodoResource = createResource({
|
|
1117
|
+
path: '/todos/:id',
|
|
1118
|
+
schema: Todo,
|
|
1119
|
+
optimistic: true,
|
|
1120
|
+
searchParams: {} as { userId?: string | number } | undefined,
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
const queryRemainingTodos = new schema.Query(
|
|
1124
|
+
TodoResource.getList.schema,
|
|
1125
|
+
entries => entries && entries.filter(todo => !todo.completed).length,
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
() => useQuery(queryRemainingTodos, { userId: 1 });
|
|
1129
|
+
() => useQuery(queryRemainingTodos);
|
|
1130
|
+
// @ts-expect-error
|
|
1131
|
+
() => useQuery(queryRemainingTodos, { user: 1 });
|
|
1132
|
+
// @ts-expect-error
|
|
1133
|
+
() => useQuery(queryRemainingTodos, 5);
|
|
1134
|
+
// @ts-expect-error
|
|
1135
|
+
() => useQuery(queryRemainingTodos, { userId: 1 }, 5);
|
|
1136
|
+
});
|
|
1097
1137
|
});
|
package/src/createResource.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
SnapshotInterface,
|
|
4
|
-
schema,
|
|
5
|
-
} from '@data-client/endpoint';
|
|
1
|
+
import { schema } from '@data-client/endpoint';
|
|
2
|
+
import type { SnapshotInterface, Queryable } from '@data-client/endpoint';
|
|
6
3
|
|
|
7
|
-
import { ResourcePath } from './pathTypes.js';
|
|
8
4
|
import {
|
|
9
5
|
ResourceGenerics,
|
|
10
6
|
ResourceOptions,
|
|
@@ -52,8 +48,9 @@ export default function createResource<O extends ResourceGenerics>({
|
|
|
52
48
|
}) as any;
|
|
53
49
|
if (optimistic) {
|
|
54
50
|
(extraMutateOptions as any).getOptimisticResponse = optimisticUpdate;
|
|
51
|
+
// TODO: Check that schema is a queryable, otherwise this doesn't make sense
|
|
55
52
|
(extraPartialOptions as any).getOptimisticResponse = optimisticPartial(
|
|
56
|
-
|
|
53
|
+
schema as any,
|
|
57
54
|
);
|
|
58
55
|
}
|
|
59
56
|
const getList = new Endpoint({
|
|
@@ -141,12 +138,10 @@ function optimisticUpdate(snap: SnapshotInterface, params: any, body: any) {
|
|
|
141
138
|
...ensurePojo(body),
|
|
142
139
|
};
|
|
143
140
|
}
|
|
144
|
-
function optimisticPartial(
|
|
145
|
-
getEndpoint: GetEndpoint<{ path: ResourcePath; schema: any }>,
|
|
146
|
-
) {
|
|
141
|
+
function optimisticPartial(schema: Queryable) {
|
|
147
142
|
return function (snap: SnapshotInterface, params: any, body: any) {
|
|
148
|
-
const
|
|
149
|
-
if (!data) throw
|
|
143
|
+
const data = snap.get(schema, params);
|
|
144
|
+
if (!data) throw snap.abort;
|
|
150
145
|
return {
|
|
151
146
|
...params,
|
|
152
147
|
...data,
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type {
|
|
|
20
20
|
PaginationFieldEndpoint,
|
|
21
21
|
PaginationEndpoint,
|
|
22
22
|
} from './RestEndpoint.js';
|
|
23
|
+
export { getUrlBase, getUrlTokens } from './RestHelpers.js';
|
|
23
24
|
export { default as createResource } from './createResource.js';
|
|
24
25
|
export type { Resource } from './resourceTypes.js';
|
|
25
26
|
export type { ResourceOptions, ResourceGenerics } from './resourceTypes.js';
|
package/src/mapCollection.ts
CHANGED
|
@@ -33,10 +33,11 @@ export default function mapCollection<
|
|
|
33
33
|
type MapCollection<
|
|
34
34
|
M extends <C extends schema.Collection>(collection: C) => any,
|
|
35
35
|
S extends Schema | undefined,
|
|
36
|
-
> =
|
|
37
|
-
|
|
38
|
-
: S extends
|
|
39
|
-
:
|
|
36
|
+
> =
|
|
37
|
+
S extends schema.Collection ? ReturnType<M>
|
|
38
|
+
: S extends schema.Object<infer T> ? MapCollection<M, T>
|
|
39
|
+
: S extends { [K: string]: any } ? MapObject<M, S>
|
|
40
|
+
: never;
|
|
40
41
|
|
|
41
42
|
export type MapObject<
|
|
42
43
|
M extends (collection: schema.Collection) => any,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export default function paramsToString(
|
|
2
|
-
searchParams
|
|
2
|
+
searchParams?:
|
|
3
|
+
| string
|
|
4
|
+
| URLSearchParams
|
|
5
|
+
| Record<string, string>
|
|
6
|
+
| string[][]
|
|
7
|
+
| undefined,
|
|
3
8
|
) {
|
|
4
9
|
const params = new URLSearchParams(searchParams as any);
|
|
5
10
|
try {
|
package/src/paramsToString.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export default function paramsToString(
|
|
2
|
-
searchParams
|
|
2
|
+
searchParams?:
|
|
3
|
+
| string
|
|
4
|
+
| URLSearchParams
|
|
5
|
+
| Record<string, string>
|
|
6
|
+
| string[][]
|
|
7
|
+
| undefined,
|
|
3
8
|
) {
|
|
4
|
-
const params = new URLSearchParams(searchParams
|
|
9
|
+
const params = new URLSearchParams(searchParams);
|
|
5
10
|
params.sort();
|
|
6
11
|
return params.toString();
|
|
7
12
|
}
|
package/src/resourceTypes.ts
CHANGED
|
@@ -2,11 +2,17 @@ import type { Collection, schema } from '@data-client/endpoint';
|
|
|
2
2
|
import type { Schema } from '@data-client/endpoint';
|
|
3
3
|
import type { Denormalize } from '@data-client/endpoint';
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
KeysToArgs,
|
|
7
|
+
PathArgs,
|
|
8
|
+
ResourcePath,
|
|
9
|
+
ShortenPath,
|
|
10
|
+
} from './pathTypes.js';
|
|
6
11
|
import { Extendable } from './resourceExtendable.js';
|
|
7
12
|
import RestEndpoint, {
|
|
8
13
|
GetEndpoint,
|
|
9
14
|
MutateEndpoint,
|
|
15
|
+
ParamToArgs,
|
|
10
16
|
RestInstanceBase,
|
|
11
17
|
RestTypeNoBody,
|
|
12
18
|
} from './RestEndpoint.js';
|
|
@@ -74,13 +80,11 @@ export interface Resource<
|
|
|
74
80
|
path: ShortenPath<O['path']>;
|
|
75
81
|
schema: schema.Collection<
|
|
76
82
|
[O['schema']],
|
|
77
|
-
|
|
78
|
-
'searchParams' extends
|
|
79
|
-
O['
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
: PathArgs<ShortenPath<O['path']>>,
|
|
83
|
-
]
|
|
83
|
+
ParamToArgs<
|
|
84
|
+
O['searchParams'] extends undefined ?
|
|
85
|
+
KeysToArgs<ShortenPath<O['path']>>
|
|
86
|
+
: O['searchParams'] & PathArgs<ShortenPath<O['path']>>
|
|
87
|
+
>
|
|
84
88
|
>;
|
|
85
89
|
body: 'body' extends keyof O ? O['body']
|
|
86
90
|
: Partial<Denormalize<O['schema']>>;
|
|
@@ -92,13 +96,10 @@ export interface Resource<
|
|
|
92
96
|
path: ShortenPath<O['path']>;
|
|
93
97
|
schema: schema.Collection<
|
|
94
98
|
[O['schema']],
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
O['
|
|
98
|
-
|
|
99
|
-
: O['searchParams'] & PathArgs<ShortenPath<O['path']>>
|
|
100
|
-
: PathArgs<ShortenPath<O['path']>>,
|
|
101
|
-
]
|
|
99
|
+
ParamToArgs<
|
|
100
|
+
(Record<string, number | string | boolean> | undefined) &
|
|
101
|
+
PathArgs<ShortenPath<O['path']>>
|
|
102
|
+
>
|
|
102
103
|
>;
|
|
103
104
|
body: 'body' extends keyof O ? O['body']
|
|
104
105
|
: Partial<Denormalize<O['schema']>>;
|