Skip to content

Commit cf770de

Browse files
authored
feat: Add controller.set() (#3105)
1 parent dc67eff commit cf770de

File tree

34 files changed

+624
-293
lines changed

34 files changed

+624
-293
lines changed

.changeset/big-jars-drum.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@data-client/test': patch
3+
'@data-client/img': patch
4+
'@data-client/ssr': patch
5+
---
6+
7+
Support 0.13 of @data-client/react

.changeset/empty-shoes-crash.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@data-client/react': minor
3+
'@data-client/core': minor
4+
---
5+
6+
Add controller.set()
7+
8+
```ts
9+
ctrl.set(Todo, { id: '5' }, { id: '5', title: 'tell me friends how great Data Client is' });
10+
```
11+
12+
BREAKING CHANGE:
13+
- actionTypes.SET_TYPE -> actionTypes.SET_RESPONSE_TYPE
14+
- SetAction -> SetResponseAction

docs/core/api/Controller.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class Controller {
3131
invalidate(endpoint, ...args): Promise<void>;
3232
invalidateAll({ testKey }): Promise<void>;
3333
resetEntireStore(): Promise<void>;
34+
set(queryable, ...args, response): Promise<void>;
3435
setResponse(endpoint, ...args, response): Promise<void>;
3536
setError(endpoint, ...args, error): Promise<void>;
3637
resolve(endpoint, { args, response, fetchedAt, error }): Promise<void>;
@@ -358,6 +359,14 @@ function UserName() {
358359
}
359360
```
360361

362+
## set(queryable, ...args, value) {#set}
363+
364+
Updates any [Queryable](/rest/api/schema#queryable) [Schema](/rest/api/schema#schema-overview).
365+
366+
```ts
367+
ctrl.set(Todo, { id: '5' }, { id: '5', title: 'tell me friends how great Data Client is' });
368+
```
369+
361370
## setResponse(endpoint, ...args, response) {#setResponse}
362371

363372
Stores `response` in cache for given [Endpoint](/rest/api/Endpoint) and args.

packages/core/src/actionTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const FETCH_TYPE = 'rdc/fetch' as const;
22
export const SET_TYPE = 'rdc/set' as const;
3+
export const SET_RESPONSE_TYPE = 'rdc/setresponse' as const;
34
export const OPTIMISTIC_TYPE = 'rdc/optimistic' as const;
45
export const RESET_TYPE = 'rdc/reset' as const;
56
export const SUBSCRIBE_TYPE = 'rdc/subscribe' as const;

packages/core/src/actions.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type {
2+
Denormalize,
23
EndpointInterface,
4+
Queryable,
35
ResolveType,
46
UnknownError,
57
} from '@data-client/normalizr';
@@ -15,6 +17,7 @@ import type {
1517
OPTIMISTIC_TYPE,
1618
INVALIDATEALL_TYPE,
1719
EXPIREALL_TYPE,
20+
SET_RESPONSE_TYPE,
1821
} from './actionTypes.js';
1922
import type { EndpointUpdateFunction } from './controller/types.js';
2023

@@ -27,33 +30,48 @@ type EndpointDefault = EndpointInterface & {
2730

2831
/* SET */
2932
export interface SetMeta {
33+
args: readonly any[];
34+
fetchedAt: number;
35+
date: number;
36+
expiresAt: number;
37+
}
38+
39+
export interface SetAction<S extends Queryable = any> {
40+
type: typeof SET_TYPE;
41+
schema: S;
42+
meta: SetMeta;
43+
value: Denormalize<S>;
44+
}
45+
46+
/* setResponse */
47+
export interface SetResponseMeta {
3048
args: readonly any[];
3149
key: string;
3250
fetchedAt: number;
3351
date: number;
3452
expiresAt: number;
3553
}
36-
export interface SetActionSuccess<
54+
export interface SetResponseActionSuccess<
3755
E extends EndpointAndUpdate<E> = EndpointDefault,
3856
> {
39-
type: typeof SET_TYPE;
57+
type: typeof SET_RESPONSE_TYPE;
4058
endpoint: E;
41-
meta: SetMeta;
59+
meta: SetResponseMeta;
4260
payload: ResolveType<E>;
4361
error?: false;
4462
}
45-
export interface SetActionError<
63+
export interface SetResponseActionError<
4664
E extends EndpointAndUpdate<E> = EndpointDefault,
4765
> {
48-
type: typeof SET_TYPE;
66+
type: typeof SET_RESPONSE_TYPE;
4967
endpoint: E;
50-
meta: SetMeta;
68+
meta: SetResponseMeta;
5169
payload: UnknownError;
5270
error: true;
5371
}
54-
export type SetAction<E extends EndpointAndUpdate<E> = EndpointDefault> =
55-
| SetActionSuccess<E>
56-
| SetActionError<E>;
72+
export type SetResponseAction<
73+
E extends EndpointAndUpdate<E> = EndpointDefault,
74+
> = SetResponseActionSuccess<E> | SetResponseActionError<E>;
5775

5876
/* FETCH */
5977
export interface FetchMeta<A extends readonly any[] = readonly any[]> {
@@ -81,7 +99,7 @@ export interface OptimisticAction<
8199
> {
82100
type: typeof OPTIMISTIC_TYPE;
83101
endpoint: E;
84-
meta: SetMeta;
102+
meta: SetResponseMeta;
85103
error?: false;
86104
}
87105

@@ -144,6 +162,7 @@ export type ActionTypes =
144162
| FetchAction
145163
| OptimisticAction
146164
| SetAction
165+
| SetResponseAction
147166
| SubscribeAction
148167
| UnsubscribeAction
149168
| InvalidateAction

packages/core/src/controller/Controller.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import type {
55
Denormalize,
66
Queryable,
77
SchemaArgs,
8-
NI,
98
} from '@data-client/normalizr';
109
import {
1110
ExpiryStatus,
@@ -27,6 +26,7 @@ import createInvalidate from './createInvalidate.js';
2726
import createInvalidateAll from './createInvalidateAll.js';
2827
import createReset from './createReset.js';
2928
import createSet from './createSet.js';
29+
import createSetResponse from './createSetResponse.js';
3030
import {
3131
createUnsubscription,
3232
createSubscription,
@@ -183,9 +183,26 @@ export default class Controller<
183183
resetEntireStore = (): Promise<void> => this.dispatch(createReset());
184184

185185
/**
186-
* Stores response in cache for given Endpoint and args.
186+
* Sets value for the Queryable and args.
187187
* @see https://dataclient.io/docs/api/Controller#set
188188
*/
189+
set = <S extends Queryable>(
190+
schema: S,
191+
...rest: readonly [...SchemaArgs<S>, any]
192+
): Promise<void> => {
193+
const value: Denormalize<S> = rest[rest.length - 1];
194+
const action = createSet(schema, {
195+
args: rest.slice(0, rest.length - 1) as SchemaArgs<S>,
196+
value,
197+
});
198+
// TODO: reject with error if this fails in reducer
199+
return this.dispatch(action);
200+
};
201+
202+
/**
203+
* Sets response for the Endpoint and args.
204+
* @see https://dataclient.io/docs/api/Controller#setResponse
205+
*/
189206
setResponse = <
190207
E extends EndpointInterface & {
191208
update?: EndpointUpdateFunction<E>;
@@ -195,15 +212,15 @@ export default class Controller<
195212
...rest: readonly [...Parameters<E>, any]
196213
): Promise<void> => {
197214
const response: ResolveType<E> = rest[rest.length - 1];
198-
const action = createSet(endpoint, {
215+
const action = createSetResponse(endpoint, {
199216
args: rest.slice(0, rest.length - 1) as Parameters<E>,
200217
response,
201218
});
202219
return this.dispatch(action);
203220
};
204221

205222
/**
206-
* Stores the result of Endpoint and args as the error provided.
223+
* Sets an error response for the Endpoint and args.
207224
* @see https://dataclient.io/docs/api/Controller#setError
208225
*/
209226
setError = <
@@ -215,7 +232,7 @@ export default class Controller<
215232
...rest: readonly [...Parameters<E>, Error]
216233
): Promise<void> => {
217234
const response: Error = rest[rest.length - 1];
218-
const action = createSet(endpoint, {
235+
const action = createSetResponse(endpoint, {
219236
args: rest.slice(0, rest.length - 1) as Parameters<E>,
220237
response,
221238
error: true,
@@ -247,7 +264,7 @@ export default class Controller<
247264
error?: false | undefined;
248265
},
249266
): Promise<void> => {
250-
return this.dispatch(createSet(endpoint, meta as any));
267+
return this.dispatch(createSetResponse(endpoint, meta as any));
251268
};
252269

253270
/**
Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,22 @@
1-
import type { EndpointInterface, ResolveType } from '@data-client/normalizr';
1+
import type { Queryable, SchemaArgs } from '@data-client/normalizr';
22

33
import ensurePojo from './ensurePojo.js';
4-
import { EndpointUpdateFunction } from './types.js';
54
import { SET_TYPE } from '../actionTypes.js';
65
import type { SetAction, SetMeta } from '../types.js';
76

8-
export default function createSet<
9-
E extends EndpointInterface & {
10-
update?: EndpointUpdateFunction<E>;
11-
},
12-
>(
13-
endpoint: E,
14-
options: {
15-
args: readonly [...Parameters<E>];
16-
response: Error;
17-
fetchedAt?: number;
18-
error: true;
19-
},
20-
): SetAction<E>;
21-
22-
export default function createSet<
23-
E extends EndpointInterface & {
24-
update?: EndpointUpdateFunction<E>;
25-
},
26-
>(
27-
endpoint: E,
28-
options: {
29-
args: readonly [...Parameters<E>];
30-
response: ResolveType<E>;
31-
fetchedAt?: number;
32-
error?: false;
33-
},
34-
): SetAction<E>;
35-
36-
export default function createSet<
37-
E extends EndpointInterface & {
38-
update?: EndpointUpdateFunction<E>;
39-
},
40-
>(
41-
endpoint: E,
7+
export default function createSet<S extends Queryable>(
8+
schema: S,
429
{
4310
args,
4411
fetchedAt,
45-
response,
46-
error = false,
12+
value,
4713
}: {
48-
args: readonly [...Parameters<E>];
49-
response: any;
14+
args: readonly [...SchemaArgs<S>];
15+
value: any;
5016
fetchedAt?: number;
51-
error?: boolean;
5217
},
53-
): SetAction<E> {
54-
const expiryLength: number =
55-
error ?
56-
endpoint.errorExpiryLength ?? 1000
57-
: endpoint.dataExpiryLength ?? 60000;
18+
): SetAction<S> {
19+
const expiryLength: number = 60000;
5820
/* istanbul ignore next */
5921
if (process.env.NODE_ENV === 'development' && expiryLength < 0) {
6022
throw new Error('Negative expiry length are not allowed.');
@@ -65,15 +27,13 @@ export default function createSet<
6527
fetchedAt: fetchedAt ?? now,
6628
date: now,
6729
expiresAt: now + expiryLength,
68-
key: endpoint.key(...args),
6930
};
7031

71-
const action: SetAction<E> = {
32+
const action: SetAction<S> = {
7233
type: SET_TYPE,
73-
payload: response,
74-
endpoint: endpoint,
34+
value,
35+
schema,
7536
meta,
7637
};
77-
if (error) (action as any).error = true;
7838
return action;
7939
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { EndpointInterface, ResolveType } from '@data-client/normalizr';
2+
3+
import ensurePojo from './ensurePojo.js';
4+
import { EndpointUpdateFunction } from './types.js';
5+
import { SET_RESPONSE_TYPE } from '../actionTypes.js';
6+
import type { SetResponseAction, SetResponseMeta } from '../types.js';
7+
8+
export default function createSetResponse<
9+
E extends EndpointInterface & {
10+
update?: EndpointUpdateFunction<E>;
11+
},
12+
>(
13+
endpoint: E,
14+
options: {
15+
args: readonly [...Parameters<E>];
16+
response: Error;
17+
fetchedAt?: number;
18+
error: true;
19+
},
20+
): SetResponseAction<E>;
21+
22+
export default function createSetResponse<
23+
E extends EndpointInterface & {
24+
update?: EndpointUpdateFunction<E>;
25+
},
26+
>(
27+
endpoint: E,
28+
options: {
29+
args: readonly [...Parameters<E>];
30+
response: ResolveType<E>;
31+
fetchedAt?: number;
32+
error?: false;
33+
},
34+
): SetResponseAction<E>;
35+
36+
export default function createSetResponse<
37+
E extends EndpointInterface & {
38+
update?: EndpointUpdateFunction<E>;
39+
},
40+
>(
41+
endpoint: E,
42+
{
43+
args,
44+
fetchedAt,
45+
response,
46+
error = false,
47+
}: {
48+
args: readonly [...Parameters<E>];
49+
response: any;
50+
fetchedAt?: number;
51+
error?: boolean;
52+
},
53+
): SetResponseAction<E> {
54+
const expiryLength: number =
55+
error ?
56+
endpoint.errorExpiryLength ?? 1000
57+
: endpoint.dataExpiryLength ?? 60000;
58+
/* istanbul ignore next */
59+
if (process.env.NODE_ENV === 'development' && expiryLength < 0) {
60+
throw new Error('Negative expiry length are not allowed.');
61+
}
62+
const now = Date.now();
63+
const meta: SetResponseMeta = {
64+
args: args.map(ensurePojo),
65+
fetchedAt: fetchedAt ?? now,
66+
date: now,
67+
expiresAt: now + expiryLength,
68+
key: endpoint.key(...args),
69+
};
70+
71+
const action: SetResponseAction<E> = {
72+
type: SET_RESPONSE_TYPE,
73+
payload: response,
74+
endpoint: endpoint,
75+
meta,
76+
};
77+
if (error) (action as any).error = true;
78+
return action;
79+
}

0 commit comments

Comments
 (0)