@data-client/rest 0.1.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.
Files changed (158) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +110 -0
  3. package/dist/index.js +561 -0
  4. package/dist/index.umd.min.js +1 -0
  5. package/dist/next.js +520 -0
  6. package/dist/package.json +1 -0
  7. package/legacy/NetworkError.js +13 -0
  8. package/legacy/OptionsToFunction.js +2 -0
  9. package/legacy/RestEndpoint.js +225 -0
  10. package/legacy/RestEndpointTypeHelp.js +2 -0
  11. package/legacy/RestHelpers.js +33 -0
  12. package/legacy/createResource.js +88 -0
  13. package/legacy/extractCollection.js +14 -0
  14. package/legacy/getArrayPath.js +19 -0
  15. package/legacy/hookifyResource.js +24 -0
  16. package/legacy/index.js +10 -0
  17. package/legacy/mapCollection.js +14 -0
  18. package/legacy/next/OptionsToFunction.js +2 -0
  19. package/legacy/next/RestEndpoint.js +225 -0
  20. package/legacy/next/createResource.js +86 -0
  21. package/legacy/next/index.js +4 -0
  22. package/legacy/next/resourceTypes.js +2 -0
  23. package/legacy/paginatedCollections.js +29 -0
  24. package/legacy/paginationUpdate.js +36 -0
  25. package/legacy/paramsToString.js +6 -0
  26. package/legacy/paramsToString.native.js +9 -0
  27. package/legacy/pathTypes.js +2 -0
  28. package/legacy/test-fixtures.js +111 -0
  29. package/legacy/utiltypes.js +2 -0
  30. package/lib/NetworkError.d.ts +11 -0
  31. package/lib/NetworkError.d.ts.map +1 -0
  32. package/lib/NetworkError.js +13 -0
  33. package/lib/OptionsToFunction.d.ts +7 -0
  34. package/lib/OptionsToFunction.d.ts.map +1 -0
  35. package/lib/OptionsToFunction.js +2 -0
  36. package/lib/RestEndpoint.d.ts +548 -0
  37. package/lib/RestEndpoint.js +229 -0
  38. package/lib/RestEndpointTypeHelp.d.ts +10 -0
  39. package/lib/RestEndpointTypeHelp.d.ts.map +1 -0
  40. package/lib/RestEndpointTypeHelp.js +2 -0
  41. package/lib/RestHelpers.d.ts +7 -0
  42. package/lib/RestHelpers.d.ts.map +1 -0
  43. package/lib/RestHelpers.js +33 -0
  44. package/lib/createResource.d.ts +66 -0
  45. package/lib/createResource.d.ts.map +1 -0
  46. package/lib/createResource.js +104 -0
  47. package/lib/extractCollection.d.ts +11 -0
  48. package/lib/extractCollection.d.ts.map +1 -0
  49. package/lib/extractCollection.js +16 -0
  50. package/lib/getArrayPath.d.ts +3 -0
  51. package/lib/getArrayPath.d.ts.map +1 -0
  52. package/lib/getArrayPath.js +19 -0
  53. package/lib/hookifyResource.d.ts +14 -0
  54. package/lib/hookifyResource.d.ts.map +1 -0
  55. package/lib/hookifyResource.js +24 -0
  56. package/lib/index.d.ts +12 -0
  57. package/lib/index.d.ts.map +1 -0
  58. package/lib/index.js +10 -0
  59. package/lib/mapCollection.d.ts +16 -0
  60. package/lib/mapCollection.d.ts.map +1 -0
  61. package/lib/mapCollection.js +16 -0
  62. package/lib/next/OptionsToFunction.d.ts +7 -0
  63. package/lib/next/OptionsToFunction.d.ts.map +1 -0
  64. package/lib/next/OptionsToFunction.js +2 -0
  65. package/lib/next/RestEndpoint.d.ts +542 -0
  66. package/lib/next/RestEndpoint.js +229 -0
  67. package/lib/next/createResource.d.ts +86 -0
  68. package/lib/next/createResource.d.ts.map +1 -0
  69. package/lib/next/createResource.js +100 -0
  70. package/lib/next/index.d.ts +6 -0
  71. package/lib/next/index.d.ts.map +1 -0
  72. package/lib/next/index.js +4 -0
  73. package/lib/next/resourceTypes.d.ts +31 -0
  74. package/lib/next/resourceTypes.d.ts.map +1 -0
  75. package/lib/next/resourceTypes.js +2 -0
  76. package/lib/paginatedCollections.d.ts +3 -0
  77. package/lib/paginatedCollections.d.ts.map +1 -0
  78. package/lib/paginatedCollections.js +29 -0
  79. package/lib/paginationUpdate.d.ts +8 -0
  80. package/lib/paginationUpdate.d.ts.map +1 -0
  81. package/lib/paginationUpdate.js +40 -0
  82. package/lib/paramsToString.d.ts +2 -0
  83. package/lib/paramsToString.d.ts.map +1 -0
  84. package/lib/paramsToString.js +6 -0
  85. package/lib/paramsToString.native.d.ts +2 -0
  86. package/lib/paramsToString.native.d.ts.map +1 -0
  87. package/lib/paramsToString.native.js +9 -0
  88. package/lib/pathTypes.d.ts +20 -0
  89. package/lib/pathTypes.d.ts.map +1 -0
  90. package/lib/pathTypes.js +2 -0
  91. package/lib/test-fixtures.d.ts +141 -0
  92. package/lib/test-fixtures.d.ts.map +1 -0
  93. package/lib/test-fixtures.js +111 -0
  94. package/lib/utiltypes.d.ts +21 -0
  95. package/lib/utiltypes.d.ts.map +1 -0
  96. package/lib/utiltypes.js +2 -0
  97. package/node.mjs +1 -0
  98. package/package.json +121 -0
  99. package/src/NetworkError.ts +17 -0
  100. package/src/OptionsToFunction.ts +42 -0
  101. package/src/RestEndpoint.d.ts +548 -0
  102. package/src/RestEndpoint.js +262 -0
  103. package/src/RestEndpointTypeHelp.ts +20 -0
  104. package/src/RestHelpers.ts +48 -0
  105. package/src/__tests__/Resource.test.ts +317 -0
  106. package/src/__tests__/RestEndpoint.ts +1304 -0
  107. package/src/__tests__/__snapshots__/RestEndpoint.ts.snap +18 -0
  108. package/src/__tests__/__snapshots__/createResource.test.ts.snap +5 -0
  109. package/src/__tests__/createResource.test.ts +583 -0
  110. package/src/__tests__/getArrayPath.test.ts +30 -0
  111. package/src/__tests__/hookifyResource.ts +197 -0
  112. package/src/__tests__/types.test.ts +309 -0
  113. package/src/createResource.ts +172 -0
  114. package/src/extractCollection.ts +34 -0
  115. package/src/getArrayPath.ts +20 -0
  116. package/src/hookifyResource.ts +36 -0
  117. package/src/index.ts +42 -0
  118. package/src/mapCollection.ts +54 -0
  119. package/src/next/OptionsToFunction.ts +41 -0
  120. package/src/next/RestEndpoint.d.ts +542 -0
  121. package/src/next/RestEndpoint.js +262 -0
  122. package/src/next/__tests__/RestEndpoint.ts +1333 -0
  123. package/src/next/__tests__/__snapshots__/RestEndpoint.ts.snap +18 -0
  124. package/src/next/__tests__/types.test.ts +429 -0
  125. package/src/next/createResource.ts +194 -0
  126. package/src/next/index.ts +5 -0
  127. package/src/next/resourceTypes.ts +32 -0
  128. package/src/package.json +1 -0
  129. package/src/paginatedCollections.ts +32 -0
  130. package/src/paginationUpdate.ts +49 -0
  131. package/src/paramsToString.native.ts +10 -0
  132. package/src/paramsToString.ts +7 -0
  133. package/src/pathTypes.ts +58 -0
  134. package/src/test-fixtures.ts +128 -0
  135. package/src/utiltypes.ts +25 -0
  136. package/ts4.0/NetworkError.d.ts +11 -0
  137. package/ts4.0/OptionsToFunction.d.ts +16 -0
  138. package/ts4.0/RestEndpoint.d.ts +240 -0
  139. package/ts4.0/RestEndpointTypeHelp.d.ts +10 -0
  140. package/ts4.0/RestHelpers.d.ts +7 -0
  141. package/ts4.0/createResource.d.ts +66 -0
  142. package/ts4.0/extractCollection.d.ts +11 -0
  143. package/ts4.0/getArrayPath.d.ts +3 -0
  144. package/ts4.0/hookifyResource.d.ts +19 -0
  145. package/ts4.0/index.d.ts +12 -0
  146. package/ts4.0/mapCollection.d.ts +16 -0
  147. package/ts4.0/next/OptionsToFunction.d.ts +16 -0
  148. package/ts4.0/next/RestEndpoint.d.ts +226 -0
  149. package/ts4.0/next/createResource.d.ts +94 -0
  150. package/ts4.0/next/index.d.ts +6 -0
  151. package/ts4.0/next/resourceTypes.d.ts +31 -0
  152. package/ts4.0/paginatedCollections.d.ts +3 -0
  153. package/ts4.0/paginationUpdate.d.ts +10 -0
  154. package/ts4.0/paramsToString.d.ts +2 -0
  155. package/ts4.0/paramsToString.native.d.ts +2 -0
  156. package/ts4.0/pathTypes.d.ts +20 -0
  157. package/ts4.0/test-fixtures.d.ts +141 -0
  158. package/ts4.0/utiltypes.d.ts +5 -0
@@ -0,0 +1,18 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`RestEndpoint.fetch() should reject if content-type does not exist with schema 1`] = `[NetworkError: Unexpected text response for schema CoolerArticle]`;
4
+
5
+ exports[`RestEndpoint.fetch() should reject if content-type is not json with schema 1`] = `
6
+ [NetworkError: Unexpected html response for schema CoolerArticle
7
+ This likely means no API endpoint was configured for this request, resulting in an HTML fallback.
8
+
9
+ Response (first 300 characters): <body>this is html</body>]
10
+ `;
11
+
12
+ exports[`RestEndpoint.fetch() should reject with custom message if content type is set but json parsable 1`] = `
13
+ [NetworkError: "content-type" header does not include "json", but JSON response found.
14
+ See https://www.rfc-editor.org/rfc/rfc4627 for information on JSON responses
15
+
16
+ Using parsed JSON.
17
+ If text content was expected see https://resthooks.io/rest/api/RestEndpoint#parseResponse]
18
+ `;
@@ -0,0 +1,5 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`createResource() UserResource.delete should work with 1`] = `"not found"`;
4
+
5
+ exports[`createResource() UserResource.delete should work with {"id": 5} 1`] = `"not found"`;
@@ -0,0 +1,583 @@
1
+ import { Entity, schema } from '@data-client/endpoint';
2
+ import {
3
+ CacheProvider,
4
+ useCache,
5
+ useController,
6
+ Controller,
7
+ useSuspense,
8
+ } from '@data-client/react';
9
+ import { makeRenderRestHook } from '@data-client/test';
10
+ import { act } from '@testing-library/react-hooks';
11
+ import nock, { ReplyHeaders } from 'nock';
12
+
13
+ import createResource from '../createResource';
14
+ import RestEndpoint, { RestGenerics } from '../RestEndpoint';
15
+
16
+ describe('createResource()', () => {
17
+ const renderRestHook: ReturnType<typeof makeRenderRestHook> =
18
+ makeRenderRestHook(CacheProvider);
19
+ let mynock: nock.Scope;
20
+
21
+ class User extends Entity {
22
+ readonly id: number | undefined = undefined;
23
+ readonly username: string = '';
24
+ readonly email: string = '';
25
+ readonly isAdmin: boolean = false;
26
+
27
+ pk() {
28
+ return this.id?.toString();
29
+ }
30
+ }
31
+ class MyEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
32
+ parseResponse(response: Response): Promise<any> {
33
+ return super.parseResponse(response);
34
+ }
35
+
36
+ getRequestInit(body: any) {
37
+ if (typeof body === 'object') {
38
+ return super.getRequestInit({ ...body, email: 'always@always.com' });
39
+ }
40
+ return super.getRequestInit(body);
41
+ }
42
+
43
+ additional = 5;
44
+ }
45
+
46
+ const UserResource = createResource({
47
+ path: 'http\\://test.com/groups/:group/users/:id',
48
+ schema: User,
49
+ Endpoint: MyEndpoint,
50
+ });
51
+
52
+ const userPayload = {
53
+ id: 5,
54
+ username: 'ntucker',
55
+ email: 'bob@vila.com',
56
+ isAdmin: true,
57
+ };
58
+
59
+ beforeEach(() => {
60
+ nock(/.*/)
61
+ .persist()
62
+ .defaultReplyHeaders({
63
+ 'Access-Control-Allow-Origin': '*',
64
+ 'Content-Type': 'application/json',
65
+ })
66
+ .get(`/groups/five/users/${userPayload.id}`)
67
+ .reply(200, userPayload)
68
+ .get(`/groups/five/users`)
69
+ .reply(200, [userPayload])
70
+ .options(/.*/)
71
+ .reply(200);
72
+ mynock = nock(/.*/).defaultReplyHeaders({
73
+ 'Access-Control-Allow-Origin': '*',
74
+ 'Content-Type': 'application/json',
75
+ });
76
+ });
77
+
78
+ afterEach(() => {
79
+ nock.cleanAll();
80
+ });
81
+
82
+ it('UserResource.get should work', async () => {
83
+ const { result, waitForNextUpdate } = renderRestHook(() => {
84
+ return useSuspense(UserResource.get, { group: 'five', id: '5' });
85
+ });
86
+ await waitForNextUpdate();
87
+ expect(result.current).toEqual(User.fromJS(userPayload));
88
+ result.current.isAdmin;
89
+ //@ts-expect-error
90
+ expect(result.current.notaMember).toBeUndefined();
91
+
92
+ // @ts-expect-error
93
+ () => useSuspense(UserResource.get, { id: '5' });
94
+ // @ts-expect-error
95
+ () => useSuspense(UserResource.get, { group: 'five' });
96
+ });
97
+
98
+ it('UserResource.getList should work', async () => {
99
+ const { result, waitForNextUpdate } = renderRestHook(() => {
100
+ return useSuspense(UserResource.getList, { group: 'five' });
101
+ });
102
+ await waitForNextUpdate();
103
+ expect(result.current[0]).toEqual(User.fromJS(userPayload));
104
+ result.current[0].isAdmin;
105
+ //@ts-expect-error
106
+ expect(result.current[0].notaMember).toBeUndefined();
107
+ type A = Parameters<typeof UserResource.getList>;
108
+
109
+ // @ts-expect-error
110
+ () => useSuspense(UserResource.getList, { id: '5' });
111
+ // @ts-expect-error
112
+ () => useSuspense(UserResource.getList, {});
113
+ // @ts-expect-error
114
+ () => useSuspense(UserResource.getList);
115
+ });
116
+
117
+ it('UserResource.update should work', async () => {
118
+ mynock
119
+ .put(`/groups/five/users/${userPayload.id}`)
120
+ .reply(200, (uri, body: any) => ({
121
+ ...userPayload,
122
+ ...body,
123
+ }));
124
+
125
+ const { result, waitForNextUpdate } = renderRestHook(
126
+ () => {
127
+ return [
128
+ useSuspense(UserResource.get, { group: 'five', id: '5' }),
129
+ useController(),
130
+ ] as const;
131
+ },
132
+ {
133
+ initialFixtures: [
134
+ {
135
+ endpoint: UserResource.get,
136
+ args: [{ group: 'five', id: '5' }],
137
+ response: userPayload,
138
+ },
139
+ ],
140
+ },
141
+ );
142
+ // eslint-disable-next-line prefer-const
143
+ let [user, controller] = result.current;
144
+ expect(user.username).toBe(userPayload.username);
145
+ await act(() => {
146
+ controller.fetch(
147
+ UserResource.update,
148
+ { group: 'five', id: '5' },
149
+ { username: 'never' },
150
+ );
151
+ });
152
+ await waitForNextUpdate();
153
+ [user] = result.current;
154
+ expect(user.username).toBe('never');
155
+
156
+ () =>
157
+ controller.fetch(
158
+ UserResource.update,
159
+ { group: 'five', id: '5' },
160
+ { username: 'never' },
161
+ // @ts-expect-error
162
+ { username: 'never' },
163
+ );
164
+ // @ts-expect-error
165
+ () => controller.fetch(UserResource.update, { username: 'never' });
166
+ // @ts-expect-error
167
+ () => controller.fetch(UserResource.update, 1, 'hi');
168
+ () =>
169
+ controller.fetch(
170
+ UserResource.update,
171
+ { group: 'five', id: '5' },
172
+ // @ts-expect-error
173
+ { sdf: 'never' },
174
+ );
175
+ () =>
176
+ controller.fetch(
177
+ UserResource.update,
178
+ // @ts-expect-error
179
+ { sdf: 'five', id: '5' },
180
+ { username: 'never' },
181
+ );
182
+ });
183
+
184
+ it('UserResource.create should work', async () => {
185
+ mynock.post(`/groups/five/users`).reply(200, (uri, body: any) => ({
186
+ id: 5,
187
+ ...body,
188
+ }));
189
+
190
+ const { result, waitForNextUpdate } = renderRestHook(() => {
191
+ return [
192
+ useCache(UserResource.get, { group: 'five', id: '5' }),
193
+ useController(),
194
+ ] as const;
195
+ });
196
+ // eslint-disable-next-line prefer-const
197
+ let [_, controller] = result.current;
198
+ await act(() => {
199
+ controller.fetch(
200
+ UserResource.create,
201
+ { group: 'five' },
202
+ {
203
+ username: 'createduser',
204
+ email: 'haha@gmail.com',
205
+ },
206
+ );
207
+ });
208
+ await waitForNextUpdate();
209
+ const user = result.current[0];
210
+ expect(user).toBeDefined();
211
+ expect(user?.username).toBe('createduser');
212
+ // our custom endpoint ensures this
213
+ expect(user?.email).toBe('always@always.com');
214
+
215
+ () =>
216
+ controller.fetch(
217
+ UserResource.create,
218
+ // @ts-expect-error
219
+ { id: 'five' },
220
+ { username: 'never' },
221
+ );
222
+ // @ts-expect-error
223
+ () => controller.fetch(UserResource.create, { username: 'never' });
224
+ // @ts-expect-error
225
+ () => controller.fetch(UserResource.create, 1, 'hi');
226
+ () =>
227
+ controller.fetch(
228
+ UserResource.create,
229
+ { group: 'five' },
230
+ // @ts-expect-error
231
+ { sdf: 'never' },
232
+ );
233
+ () =>
234
+ controller.fetch(
235
+ UserResource.create,
236
+ // @ts-expect-error
237
+ { sdf: 'five' },
238
+ { username: 'never' },
239
+ );
240
+ });
241
+
242
+ it.each([
243
+ {
244
+ response: {
245
+ id: userPayload.id,
246
+ },
247
+ headers: {
248
+ 'Access-Control-Allow-Origin': '*',
249
+ 'Content-Type': 'application/json',
250
+ } as ReplyHeaders,
251
+ },
252
+ {
253
+ response: '',
254
+ headers: {
255
+ 'Access-Control-Allow-Origin': '*',
256
+ 'Content-Type': 'text',
257
+ } as ReplyHeaders,
258
+ },
259
+ ])(
260
+ 'UserResource.delete should work with $response',
261
+ async ({ response, headers }) => {
262
+ mynock
263
+ .delete(`/groups/five/users/${userPayload.id}`)
264
+ .reply(200, (uri, body: any) => response, headers);
265
+ const { result, waitForNextUpdate } = renderRestHook(
266
+ () => {
267
+ return [
268
+ useSuspense(UserResource.get, { group: 'five', id: '5' }),
269
+ useController(),
270
+ ] as const;
271
+ },
272
+ {
273
+ initialFixtures: [
274
+ {
275
+ endpoint: UserResource.get,
276
+ args: [{ group: 'five', id: '5' }],
277
+ response: userPayload,
278
+ },
279
+ ],
280
+ resolverFixtures: [
281
+ {
282
+ endpoint: UserResource.get,
283
+ args: [{ group: 'five', id: '5' }],
284
+ response: 'not found',
285
+ error: true,
286
+ },
287
+ ],
288
+ },
289
+ );
290
+ // eslint-disable-next-line prefer-const
291
+ let [user, controller] = result.current;
292
+ expect(user.username).toBe(userPayload.username);
293
+ await act(() => {
294
+ controller.fetch(UserResource.delete, { group: 'five', id: '5' });
295
+ });
296
+ await waitForNextUpdate();
297
+ // this means we suspended; so it hit the resolver fixture
298
+ expect(result.error).toMatchSnapshot();
299
+
300
+ () =>
301
+ controller.fetch(
302
+ UserResource.delete,
303
+ { group: 'five', id: '5' },
304
+ // @ts-expect-error
305
+ { username: 'never' },
306
+ );
307
+ // @ts-expect-error
308
+ () => controller.fetch(UserResource.delete);
309
+ // @ts-expect-error
310
+ () => controller.fetch(UserResource.delete, 1);
311
+ () =>
312
+ controller.fetch(
313
+ UserResource.delete,
314
+ // @ts-expect-error
315
+ { sdf: 'never' },
316
+ );
317
+ },
318
+ );
319
+
320
+ it('should allow complete overrides', async () => {
321
+ mynock
322
+ .get(`/groups/vi/users/5`)
323
+ .reply(200, { id: 5, title: 'hi', username2: 'bob' });
324
+
325
+ class User2 extends Entity {
326
+ readonly id: number | undefined = undefined;
327
+ readonly username2: string = '';
328
+ readonly email: string = '';
329
+ readonly isAdmin: boolean = false;
330
+
331
+ pk() {
332
+ return this.id?.toString();
333
+ }
334
+ }
335
+ const UserResourceExtend = {
336
+ ...UserResource,
337
+ get: UserResource.get.extend({
338
+ path: 'http\\://test.com/groups/:magic/users/:id',
339
+ schema: User2,
340
+ }),
341
+ };
342
+ const { result, waitForNextUpdate } = renderRestHook(() => {
343
+ return useSuspense(UserResourceExtend.get, { magic: 'vi', id: 5 });
344
+ });
345
+ await waitForNextUpdate();
346
+
347
+ expect(result.current.username2).toBe('bob');
348
+ // @ts-expect-error
349
+ expect(result.current.username).toBeUndefined();
350
+ });
351
+
352
+ describe('unions', () => {
353
+ const feedPayload = {
354
+ id: '5',
355
+ title: 'my first feed',
356
+ type: 'link' as const,
357
+ url: 'https://true.io',
358
+ };
359
+
360
+ class Feed extends Entity {
361
+ readonly id: string = '';
362
+ readonly title: string = '';
363
+ readonly type: 'link' | 'post' = 'post';
364
+ pk() {
365
+ return this.id;
366
+ }
367
+ }
368
+ class FeedLink extends Feed {
369
+ readonly url: string = '';
370
+ readonly type = 'link' as const;
371
+ }
372
+ class FeedPost extends Feed {
373
+ readonly content: string = '';
374
+ readonly type = 'post' as const;
375
+ }
376
+ const FeedUnion = new schema.Union(
377
+ { post: FeedPost, link: FeedLink },
378
+ 'type' as const,
379
+ );
380
+ const FeedResource = createResource({
381
+ path: 'http\\://test.com/feed/:id',
382
+ schema: FeedUnion,
383
+ Endpoint: MyEndpoint,
384
+ });
385
+
386
+ it('should work with detail', async () => {
387
+ mynock.get(`/feed/${feedPayload.id}`).reply(200, feedPayload);
388
+
389
+ const { result, waitForNextUpdate } = renderRestHook(() => {
390
+ return useSuspense(FeedResource.get, { id: '5' });
391
+ });
392
+ await waitForNextUpdate();
393
+ const feed = result.current;
394
+ if (feed.type === 'link') {
395
+ expect(feed.url).toBe(feedPayload.url);
396
+ // @ts-expect-error
397
+ expect(feed.content).toBeUndefined();
398
+ } else {
399
+ // this branch doesn't run - just a type test
400
+ feed.content;
401
+ // @ts-expect-error
402
+ expect(feed.url).toBeUndefined();
403
+ }
404
+ // another type test
405
+ // @ts-expect-error
406
+ () => useSuspense(FeedResource.get, { sdf: '5' });
407
+
408
+ // @ts-expect-error
409
+ () => FeedResource.get({ id: '5', sdf: '5' });
410
+ // @ts-expect-error
411
+ () => FeedResource.get({ id: '5' }, 5);
412
+ });
413
+
414
+ it('should work with list [no args]', async () => {
415
+ mynock.get(`/feed`).reply(200, [feedPayload]);
416
+
417
+ const { result, waitForNextUpdate } = renderRestHook(() => {
418
+ return useSuspense(FeedResource.getList);
419
+ });
420
+ await waitForNextUpdate();
421
+ const feed = result.current[0];
422
+ if (feed.type === 'link') {
423
+ expect(feed.url).toBe(feedPayload.url);
424
+ // @ts-expect-error
425
+ expect(feed.content).toBeUndefined();
426
+ } else {
427
+ // this branch doesn't run - just a type test
428
+ feed.content;
429
+ // @ts-expect-error
430
+ expect(feed.url).toBeUndefined();
431
+ }
432
+ // another type test
433
+ // @ts-expect-error
434
+ () => useSuspense(FeedResource.getList, 5);
435
+
436
+ // @ts-expect-error
437
+ () => FeedResource.getList({ id: '5' }, 5);
438
+ });
439
+
440
+ it('should work with create [no args]', async () => {
441
+ mynock.post(`/feed`).reply(200, (uri, body: any) => ({
442
+ id: 5,
443
+ ...body,
444
+ }));
445
+
446
+ const { result, waitForNextUpdate } = renderRestHook(() => {
447
+ return [
448
+ useCache(FeedResource.get, { id: '5', type: 'link' }),
449
+ useController(),
450
+ ] as const;
451
+ });
452
+ // eslint-disable-next-line prefer-const
453
+ let [_, controller] = result.current;
454
+ await act(() => {
455
+ controller.fetch(FeedResource.create, feedPayload);
456
+ });
457
+ await waitForNextUpdate();
458
+ const feed = result.current[0];
459
+ expect(feed).toBeDefined();
460
+ if (!feed) throw new Error('never');
461
+ if (feed.type === 'link') {
462
+ expect(feed.url).toBe(feedPayload.url);
463
+ // @ts-expect-error
464
+ expect(feed.content).toBeUndefined();
465
+ }
466
+
467
+ () =>
468
+ controller.fetch(
469
+ UserResource.create,
470
+ // @ts-expect-error
471
+ { id: 'five' },
472
+ { username: 'never' },
473
+ );
474
+ // @ts-expect-error
475
+ () => controller.fetch(UserResource.create, { username: 'never' });
476
+ // @ts-expect-error
477
+ () => controller.fetch(UserResource.create, 1, 'hi');
478
+ () =>
479
+ controller.fetch(
480
+ UserResource.create,
481
+ { group: 'five' },
482
+ // @ts-expect-error
483
+ { sdf: 'never' },
484
+ );
485
+ () =>
486
+ controller.fetch(
487
+ UserResource.create,
488
+ // @ts-expect-error
489
+ { sdf: 'five' },
490
+ { username: 'never' },
491
+ );
492
+ });
493
+ });
494
+
495
+ it('UserResource.create.extends() should work', async () => {
496
+ interface CreateDeviceBody {
497
+ username: string;
498
+ }
499
+ interface UserInterface {
500
+ readonly id: number | undefined;
501
+ readonly username: string;
502
+ readonly email: string;
503
+ readonly isAdmin: boolean;
504
+ }
505
+
506
+ const createUser = UserResource.create.extend({
507
+ update: (newId, params) => {
508
+ return {
509
+ [UserResource.getList.key({ group: params.group })]: (
510
+ prevResponse = { items: [] },
511
+ ) => ({
512
+ items: [...prevResponse.items, newId],
513
+ }),
514
+ };
515
+ },
516
+ //searchParams: undefined as any,
517
+ body: {} as CreateDeviceBody,
518
+ schema: User,
519
+ sideEffect: true,
520
+ process(...args: any) {
521
+ return UserResource.create.process.apply(this, args) as UserInterface;
522
+ },
523
+ });
524
+ const ctrl = new Controller();
525
+ () => ctrl.fetch(createUser, { group: 'hi' }, { username: 'bob' });
526
+ () => createUser({ group: 'hi' }, { username: 'bob' });
527
+ // @ts-expect-error
528
+ () => createUser({ group: 'hi', id: 'what' }, { username: 'bob' });
529
+ // @ts-expect-error
530
+ () => createUser({ group: 'hi' });
531
+ // @ts-expect-error
532
+ () => createUser.url({ group: 'hi', id: 'what' }, { username: 'bob' });
533
+ expect(createUser.url({ group: 'hi' }, {} as any)).toMatchInlineSnapshot(
534
+ `"http://test.com/groups/hi/users"`,
535
+ );
536
+ });
537
+
538
+ it('UserResource.create.extends() should work with zero urlParams', async () => {
539
+ const UserResource = createResource({
540
+ path: 'http\\://test.com/users/:id',
541
+ schema: User,
542
+ Endpoint: MyEndpoint,
543
+ });
544
+ interface CreateDeviceBody {
545
+ username: string;
546
+ }
547
+ interface UserInterface {
548
+ readonly id: number | undefined;
549
+ readonly username: string;
550
+ readonly email: string;
551
+ readonly isAdmin: boolean;
552
+ }
553
+
554
+ const createUser = UserResource.create.extend({
555
+ update: newId => {
556
+ return {
557
+ [UserResource.getList.key()]: (prevResponse = { items: [] }) => ({
558
+ items: [...prevResponse.items, newId],
559
+ }),
560
+ };
561
+ },
562
+ //searchParams: undefined as any,
563
+ body: {} as CreateDeviceBody,
564
+ schema: User,
565
+ sideEffect: true,
566
+ process(...args: any) {
567
+ return UserResource.create.process.apply(this, args) as UserInterface;
568
+ },
569
+ });
570
+ const ctrl = new Controller();
571
+ () => ctrl.fetch(createUser, { username: 'bob' });
572
+ () => createUser({ username: 'bob' });
573
+ // @ts-expect-error
574
+ () => createUser({ id: 'what' }, { username: 'bob' });
575
+ // @ts-expect-error
576
+ () => createUser({ id: 'what' });
577
+ // @ts-expect-error
578
+ () => createUser.url({ id: 'what' }, { username: 'bob' });
579
+ expect(createUser.url({} as any)).toMatchInlineSnapshot(
580
+ `"http://test.com/users"`,
581
+ );
582
+ });
583
+ });
@@ -0,0 +1,30 @@
1
+ import { CoolerArticle } from '__tests__/new';
2
+
3
+ import { schema } from '..';
4
+ import getArrayPath from '../getArrayPath';
5
+
6
+ describe('getArrayPath()', () => {
7
+ it('should be false when there is no array', () => {
8
+ expect(getArrayPath({ data: 5 })).toBe(false);
9
+ expect(getArrayPath({ data: 5, other: undefined, stringer: 'hi' })).toBe(
10
+ false,
11
+ );
12
+ });
13
+ it('should find entity inside pojo', () => {
14
+ expect(
15
+ getArrayPath({ data: 5, right: [CoolerArticle], stringer: 'hi' }),
16
+ ).toStrictEqual(['right']);
17
+ });
18
+ it('should find entity inside schema.Object', () => {
19
+ expect(
20
+ getArrayPath(
21
+ new schema.Object({ data: 5, right: [CoolerArticle], stringer: 'hi' }),
22
+ ),
23
+ ).toStrictEqual(['right']);
24
+ });
25
+ it('should be false when has entity but no array', () => {
26
+ expect(
27
+ getArrayPath({ data: 5, right: CoolerArticle, stringer: 'hi' }),
28
+ ).toStrictEqual(false);
29
+ });
30
+ });