@data-client/core 0.14.4 → 0.14.5
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 +64 -95
- package/dist/index.umd.min.js +1 -1
- package/legacy/index.js +1 -1
- package/legacy/manager/DevtoolsManager.js +50 -52
- package/legacy/manager/LogoutManager.js +3 -8
- package/legacy/manager/NetworkManager.js +9 -21
- package/legacy/manager/SubscriptionManager.js +2 -15
- package/legacy/manager/applyManager.js +5 -3
- package/legacy/middlewareTypes.js +1 -1
- package/legacy/types.js +13 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/manager/DevtoolsManager.d.ts +4 -7
- package/lib/manager/DevtoolsManager.d.ts.map +1 -1
- package/lib/manager/DevtoolsManager.js +50 -52
- package/lib/manager/LogoutManager.d.ts +5 -8
- package/lib/manager/LogoutManager.d.ts.map +1 -1
- package/lib/manager/LogoutManager.js +3 -8
- package/lib/manager/NetworkManager.d.ts +3 -11
- package/lib/manager/NetworkManager.d.ts.map +1 -1
- package/lib/manager/NetworkManager.js +9 -21
- package/lib/manager/SubscriptionManager.d.ts +1 -10
- package/lib/manager/SubscriptionManager.d.ts.map +1 -1
- package/lib/manager/SubscriptionManager.js +2 -15
- package/lib/manager/applyManager.d.ts +8 -5
- package/lib/manager/applyManager.d.ts.map +1 -1
- package/lib/manager/applyManager.js +5 -3
- package/lib/middlewareTypes.d.ts +6 -10
- package/lib/middlewareTypes.d.ts.map +1 -1
- package/lib/middlewareTypes.js +1 -1
- package/lib/types.d.ts +16 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +13 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/manager/DevtoolsManager.ts +37 -40
- package/src/manager/LogoutManager.ts +15 -27
- package/src/manager/NetworkManager.ts +57 -70
- package/src/manager/SubscriptionManager.ts +18 -32
- package/src/manager/__tests__/applyManager.ts +61 -1
- package/src/manager/__tests__/logoutManager.ts +8 -10
- package/src/manager/__tests__/manager.ts +2 -3
- package/src/manager/__tests__/networkManager.ts +15 -18
- package/src/manager/__tests__/subscriptionManager.ts +14 -16
- package/src/manager/applyManager.ts +20 -8
- package/src/middlewareTypes.ts +7 -17
- package/src/types.ts +16 -1
- package/ts3.4/index.d.ts +1 -1
- package/ts3.4/manager/DevtoolsManager.d.ts +3 -6
- package/ts3.4/manager/LogoutManager.d.ts +4 -7
- package/ts3.4/manager/NetworkManager.d.ts +3 -11
- package/ts3.4/manager/SubscriptionManager.d.ts +1 -10
- package/ts3.4/manager/applyManager.d.ts +8 -5
- package/ts3.4/middlewareTypes.d.ts +5 -9
- package/ts3.4/types.d.ts +16 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-inner-declarations */
|
|
2
2
|
import type { DevToolsConfig } from './devtoolsTypes.js';
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type { Controller, EndpointInterface } from '../index.js';
|
|
4
|
+
import type { Middleware } from '../middlewareTypes.js';
|
|
5
5
|
import createReducer from '../state/reducer/createReducer.js';
|
|
6
6
|
import type { Manager, State, ActionTypes } from '../types.js';
|
|
7
7
|
|
|
@@ -89,11 +89,12 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
89
89
|
* @see https://dataclient.io/docs/api/DevToolsManager
|
|
90
90
|
*/
|
|
91
91
|
export default class DevToolsManager implements Manager {
|
|
92
|
-
|
|
92
|
+
declare middleware: Middleware;
|
|
93
93
|
protected declare devTools: undefined | any;
|
|
94
94
|
protected started = false;
|
|
95
95
|
protected actions: [ActionTypes, State<unknown>][] = [];
|
|
96
96
|
protected declare controller: Controller;
|
|
97
|
+
declare skipLogging?: (action: ActionTypes) => boolean;
|
|
97
98
|
maxBufferLength = 100;
|
|
98
99
|
|
|
99
100
|
constructor(
|
|
@@ -110,40 +111,20 @@ export default class DevToolsManager implements Manager {
|
|
|
110
111
|
});
|
|
111
112
|
// we cut it in half so we should double so we don't lose
|
|
112
113
|
if (config?.maxAge) this.maxBufferLength = config.maxAge * 2;
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
switch (msg.type) {
|
|
116
|
-
case 'START':
|
|
117
|
-
this.started = true;
|
|
118
|
-
|
|
119
|
-
if (this.actions.length) {
|
|
120
|
-
this.actions.forEach(([action, state]) => {
|
|
121
|
-
this.handleAction(action, state);
|
|
122
|
-
});
|
|
123
|
-
this.actions = [];
|
|
124
|
-
}
|
|
125
|
-
break;
|
|
126
|
-
case 'STOP':
|
|
127
|
-
this.started = false;
|
|
128
|
-
break;
|
|
129
|
-
case 'DISPATCH':
|
|
130
|
-
if (msg.payload.type === 'RESET') {
|
|
131
|
-
this.controller.resetEntireStore();
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
114
|
+
if (skipLogging) this.skipLogging = skipLogging;
|
|
115
|
+
}
|
|
137
116
|
|
|
117
|
+
static {
|
|
138
118
|
/* istanbul ignore if */
|
|
139
119
|
/* istanbul ignore next */
|
|
140
|
-
if (
|
|
141
|
-
this.middleware = controller
|
|
120
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
121
|
+
this.prototype.middleware = function (controller) {
|
|
122
|
+
if (!this.devTools) return next => action => next(action);
|
|
142
123
|
this.controller = controller;
|
|
143
124
|
const reducer = createReducer(controller as any);
|
|
144
125
|
let state = controller.getState();
|
|
145
126
|
return next => action => {
|
|
146
|
-
const shouldSkip = skipLogging?.(action);
|
|
127
|
+
const shouldSkip = this.skipLogging?.(action);
|
|
147
128
|
const ret = next(action);
|
|
148
129
|
if (this.started) {
|
|
149
130
|
// we track state changes here since getState() will only update after a batch commit
|
|
@@ -159,7 +140,7 @@ export default class DevToolsManager implements Manager {
|
|
|
159
140
|
};
|
|
160
141
|
};
|
|
161
142
|
} else {
|
|
162
|
-
this.middleware = () => next => action => next(action);
|
|
143
|
+
this.prototype.middleware = () => next => action => next(action);
|
|
163
144
|
}
|
|
164
145
|
}
|
|
165
146
|
|
|
@@ -178,17 +159,33 @@ export default class DevToolsManager implements Manager {
|
|
|
178
159
|
|
|
179
160
|
/** Called when initial state is ready */
|
|
180
161
|
init(state: State<any>) {
|
|
181
|
-
|
|
182
|
-
|
|
162
|
+
if (process.env.NODE_ENV !== 'production' && this.devTools) {
|
|
163
|
+
this.devTools.init(state);
|
|
164
|
+
this.devTools.subscribe((msg: any) => {
|
|
165
|
+
switch (msg.type) {
|
|
166
|
+
case 'START':
|
|
167
|
+
this.started = true;
|
|
168
|
+
|
|
169
|
+
if (this.actions.length) {
|
|
170
|
+
this.actions.forEach(([action, state]) => {
|
|
171
|
+
this.handleAction(action, state);
|
|
172
|
+
});
|
|
173
|
+
this.actions = [];
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case 'STOP':
|
|
177
|
+
this.started = false;
|
|
178
|
+
break;
|
|
179
|
+
case 'DISPATCH':
|
|
180
|
+
if (msg.payload.type === 'RESET') {
|
|
181
|
+
this.controller.resetEntireStore();
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
183
187
|
}
|
|
184
188
|
|
|
185
189
|
/** Ensures all subscriptions are cleaned up. */
|
|
186
190
|
cleanup() {}
|
|
187
|
-
|
|
188
|
-
/** Attaches Manager to store
|
|
189
|
-
*
|
|
190
|
-
*/
|
|
191
|
-
getMiddleware() {
|
|
192
|
-
return this.middleware;
|
|
193
|
-
}
|
|
194
191
|
}
|
|
@@ -1,54 +1,42 @@
|
|
|
1
1
|
import { SET_RESPONSE_TYPE } from '../actionTypes.js';
|
|
2
|
-
import Controller from '../controller/Controller.js';
|
|
2
|
+
import type Controller from '../controller/Controller.js';
|
|
3
3
|
import { UnknownError } from '../index.js';
|
|
4
|
-
import {
|
|
4
|
+
import type { Manager, Middleware } from '../types.js';
|
|
5
5
|
|
|
6
6
|
/** Handling network unauthorized indicators like HTTP 401
|
|
7
7
|
*
|
|
8
8
|
* @see https://dataclient.io/docs/api/LogoutManager
|
|
9
9
|
*/
|
|
10
10
|
export default class LogoutManager implements Manager {
|
|
11
|
-
protected declare middleware: Middleware;
|
|
12
|
-
|
|
13
11
|
constructor({ handleLogout, shouldLogout }: Props = {}) {
|
|
14
12
|
if (handleLogout) this.handleLogout = handleLogout;
|
|
15
13
|
if (shouldLogout) this.shouldLogout = shouldLogout;
|
|
16
|
-
this.middleware = controller => next => async action => {
|
|
17
|
-
await next(action);
|
|
18
|
-
if (
|
|
19
|
-
action.type === SET_RESPONSE_TYPE &&
|
|
20
|
-
action.error &&
|
|
21
|
-
this.shouldLogout(action.response)
|
|
22
|
-
) {
|
|
23
|
-
this.handleLogout(controller);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
14
|
}
|
|
27
15
|
|
|
28
|
-
|
|
16
|
+
middleware: Middleware = controller => next => async action => {
|
|
17
|
+
await next(action);
|
|
18
|
+
if (
|
|
19
|
+
action.type === SET_RESPONSE_TYPE &&
|
|
20
|
+
action.error &&
|
|
21
|
+
this.shouldLogout(action.response)
|
|
22
|
+
) {
|
|
23
|
+
this.handleLogout(controller);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
return this.middleware;
|
|
32
|
-
}
|
|
27
|
+
cleanup() {}
|
|
33
28
|
|
|
34
29
|
protected shouldLogout(error: UnknownError) {
|
|
35
30
|
// 401 indicates reauthorization is needed
|
|
36
31
|
return error.status === 401;
|
|
37
32
|
}
|
|
38
33
|
|
|
39
|
-
handleLogout(controller: Controller
|
|
34
|
+
handleLogout(controller: Controller) {
|
|
40
35
|
controller.resetEntireStore();
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
type
|
|
45
|
-
|
|
46
|
-
// this further restricts the types to be future compatible
|
|
47
|
-
export type Middleware = <C extends Controller<Dispatch>>(
|
|
48
|
-
controller: C,
|
|
49
|
-
) => (next: C['dispatch']) => C['dispatch'];
|
|
50
|
-
|
|
51
|
-
type HandleLogout = (controller: Controller<Dispatch>) => void;
|
|
39
|
+
type HandleLogout = (controller: Controller) => void;
|
|
52
40
|
|
|
53
41
|
interface Props {
|
|
54
42
|
handleLogout?: HandleLogout;
|
|
@@ -34,76 +34,68 @@ export default class NetworkManager implements Manager {
|
|
|
34
34
|
protected fetchedAt: { [k: string]: number } = {};
|
|
35
35
|
declare readonly dataExpiryLength: number;
|
|
36
36
|
declare readonly errorExpiryLength: number;
|
|
37
|
-
protected declare middleware: Middleware;
|
|
38
37
|
protected controller: Controller = new Controller();
|
|
39
38
|
declare cleanupDate?: number;
|
|
40
39
|
|
|
41
40
|
constructor({ dataExpiryLength = 60000, errorExpiryLength = 1000 } = {}) {
|
|
42
41
|
this.dataExpiryLength = dataExpiryLength;
|
|
43
42
|
this.errorExpiryLength = errorExpiryLength;
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
middleware: Middleware = controller => {
|
|
46
|
+
this.controller = controller;
|
|
47
|
+
return next => action => {
|
|
48
|
+
switch (action.type) {
|
|
49
|
+
case FETCH_TYPE:
|
|
50
|
+
this.handleFetch(action);
|
|
51
|
+
// This is the only case that causes any state change
|
|
52
|
+
// It's important to intercept other fetches as we don't want to trigger reducers during
|
|
53
|
+
// render - so we need to stop 'readonly' fetches which can be triggered in render
|
|
54
|
+
if (
|
|
55
|
+
action.endpoint.getOptimisticResponse !== undefined &&
|
|
56
|
+
action.endpoint.sideEffect
|
|
57
|
+
) {
|
|
58
|
+
return next(action);
|
|
59
|
+
}
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
case SET_RESPONSE_TYPE:
|
|
62
|
+
// only set after new state is computed
|
|
63
|
+
return next(action).then(() => {
|
|
64
|
+
if (action.key in this.fetched) {
|
|
65
|
+
// Note: meta *must* be set by reducer so this should be safe
|
|
66
|
+
const error = controller.getState().meta[action.key]?.error;
|
|
67
|
+
// processing errors result in state meta having error, so we should reject the promise
|
|
68
|
+
if (error) {
|
|
69
|
+
this.handleSet(
|
|
70
|
+
createSetResponse(action.endpoint, {
|
|
71
|
+
args: action.args,
|
|
72
|
+
response: error,
|
|
73
|
+
fetchedAt: action.meta.fetchedAt,
|
|
74
|
+
error: true,
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
} else {
|
|
78
|
+
this.handleSet(action);
|
|
60
79
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (action.key in this.fetched) {
|
|
66
|
-
// Note: meta *must* be set by reducer so this should be safe
|
|
67
|
-
const error = controller.getState().meta[action.key]?.error;
|
|
68
|
-
// processing errors result in state meta having error, so we should reject the promise
|
|
69
|
-
if (error) {
|
|
70
|
-
this.handleSet(
|
|
71
|
-
createSetResponse(action.endpoint, {
|
|
72
|
-
args: action.args,
|
|
73
|
-
response: error,
|
|
74
|
-
fetchedAt: action.meta.fetchedAt,
|
|
75
|
-
error: true,
|
|
76
|
-
}),
|
|
77
|
-
);
|
|
78
|
-
} else {
|
|
79
|
-
this.handleSet(action);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
case RESET_TYPE: {
|
|
84
|
-
const rejectors = { ...this.rejectors };
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
case RESET_TYPE: {
|
|
83
|
+
const rejectors = { ...this.rejectors };
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
});
|
|
85
|
+
this.clearAll();
|
|
86
|
+
return next(action).then(() => {
|
|
87
|
+
// there could be external listeners to the promise
|
|
88
|
+
// this must happen after commit so our own rejector knows not to dispatch an error based on this
|
|
89
|
+
for (const k in rejectors) {
|
|
90
|
+
rejectors[k](new ResetError());
|
|
94
91
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
return next(action);
|
|
96
|
+
}
|
|
99
97
|
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Used by DevtoolsManager to determine whether to log an action */
|
|
103
|
-
skipLogging(action: ActionTypes) {
|
|
104
|
-
/* istanbul ignore next */
|
|
105
|
-
return action.type === FETCH_TYPE && action.key in this.fetched;
|
|
106
|
-
}
|
|
98
|
+
};
|
|
107
99
|
|
|
108
100
|
/** On mount */
|
|
109
101
|
init() {
|
|
@@ -117,6 +109,12 @@ export default class NetworkManager implements Manager {
|
|
|
117
109
|
this.cleanupDate = Date.now();
|
|
118
110
|
}
|
|
119
111
|
|
|
112
|
+
/** Used by DevtoolsManager to determine whether to log an action */
|
|
113
|
+
skipLogging(action: ActionTypes) {
|
|
114
|
+
/* istanbul ignore next */
|
|
115
|
+
return action.type === FETCH_TYPE && action.key in this.fetched;
|
|
116
|
+
}
|
|
117
|
+
|
|
120
118
|
allSettled() {
|
|
121
119
|
const fetches = Object.values(this.fetched);
|
|
122
120
|
if (fetches.length) return Promise.allSettled(fetches);
|
|
@@ -241,17 +239,6 @@ export default class NetworkManager implements Manager {
|
|
|
241
239
|
}
|
|
242
240
|
}
|
|
243
241
|
|
|
244
|
-
/** Attaches NetworkManager to store
|
|
245
|
-
*
|
|
246
|
-
* Intercepts 'rdc/fetch' actions to start requests.
|
|
247
|
-
*
|
|
248
|
-
* Resolve/rejects a request when matching 'rdc/set' event
|
|
249
|
-
* is seen.
|
|
250
|
-
*/
|
|
251
|
-
getMiddleware() {
|
|
252
|
-
return this.middleware;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
242
|
/** Ensures only one request for a given key is in flight at any time
|
|
256
243
|
*
|
|
257
244
|
* Uses key to either retrieve in-flight promise, or if not
|
|
@@ -41,33 +41,31 @@ export default class SubscriptionManager<
|
|
|
41
41
|
} = {};
|
|
42
42
|
|
|
43
43
|
protected declare readonly Subscription: S;
|
|
44
|
-
protected declare middleware: Middleware;
|
|
45
44
|
protected controller: Controller = new Controller();
|
|
46
45
|
|
|
47
46
|
constructor(Subscription: S) {
|
|
48
47
|
this.Subscription = Subscription;
|
|
48
|
+
}
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
console.error(e);
|
|
60
|
-
}
|
|
61
|
-
return Promise.resolve();
|
|
62
|
-
case UNSUBSCRIBE_TYPE:
|
|
63
|
-
this.handleUnsubscribe(action);
|
|
64
|
-
return Promise.resolve();
|
|
65
|
-
default:
|
|
66
|
-
return next(action);
|
|
50
|
+
middleware: Middleware = controller => {
|
|
51
|
+
this.controller = controller;
|
|
52
|
+
return next => action => {
|
|
53
|
+
switch (action.type) {
|
|
54
|
+
case SUBSCRIBE_TYPE:
|
|
55
|
+
try {
|
|
56
|
+
this.handleSubscribe(action);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.error(e);
|
|
67
59
|
}
|
|
68
|
-
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
case UNSUBSCRIBE_TYPE:
|
|
62
|
+
this.handleUnsubscribe(action);
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
default:
|
|
65
|
+
return next(action);
|
|
66
|
+
}
|
|
69
67
|
};
|
|
70
|
-
}
|
|
68
|
+
};
|
|
71
69
|
|
|
72
70
|
/** Ensures all subscriptions are cleaned up. */
|
|
73
71
|
cleanup() {
|
|
@@ -110,16 +108,4 @@ export default class SubscriptionManager<
|
|
|
110
108
|
console.error(`Mismatched unsubscribe: ${key} is not subscribed`);
|
|
111
109
|
}
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
/** Attaches Manager to store
|
|
115
|
-
*
|
|
116
|
-
* Intercepts 'rdc/subscribe'/'rest-hordc/ribe' to register resources that
|
|
117
|
-
* need to be kept up to date.
|
|
118
|
-
*
|
|
119
|
-
* Will possibly dispatch 'rdc/fetch' or 'rest-hordc/' to keep resources fresh
|
|
120
|
-
*
|
|
121
|
-
*/
|
|
122
|
-
getMiddleware() {
|
|
123
|
-
return this.middleware;
|
|
124
|
-
}
|
|
125
111
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import { Article } from '__tests__/new';
|
|
2
|
+
|
|
3
|
+
import { createSet } from '../../controller/actions';
|
|
1
4
|
import Controller from '../../controller/Controller';
|
|
2
|
-
import
|
|
5
|
+
import { Dispatch, Middleware } from '../../middlewareTypes';
|
|
6
|
+
import { Manager } from '../../types';
|
|
7
|
+
import applyManager, {
|
|
8
|
+
ReduxMiddleware,
|
|
9
|
+
ReduxMiddlewareAPI,
|
|
10
|
+
} from '../applyManager';
|
|
3
11
|
import NetworkManager from '../NetworkManager';
|
|
4
12
|
|
|
5
13
|
function onError(e: any) {
|
|
@@ -36,3 +44,55 @@ it('applyManagers should not console.warn() when NetworkManager is provided', ()
|
|
|
36
44
|
warnspy.mockRestore();
|
|
37
45
|
}
|
|
38
46
|
});
|
|
47
|
+
it('applyManagers should handle legacy Manager.getMiddleware()', () => {
|
|
48
|
+
let initCount = 0;
|
|
49
|
+
let actionCount = 0;
|
|
50
|
+
class MyManager implements Manager {
|
|
51
|
+
getMiddleware = (): Middleware => ctrl => {
|
|
52
|
+
initCount++;
|
|
53
|
+
return next => action => {
|
|
54
|
+
actionCount++;
|
|
55
|
+
return next(action);
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
cleanup() {}
|
|
60
|
+
}
|
|
61
|
+
const middlewares = applyManager(
|
|
62
|
+
[new MyManager(), new NetworkManager()],
|
|
63
|
+
new Controller(),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const rootDispatch = jest.fn((action: any) => {
|
|
67
|
+
return Promise.resolve();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const dispatch = middlewareDispatch(middlewares, rootDispatch);
|
|
71
|
+
|
|
72
|
+
expect(initCount).toBe(1);
|
|
73
|
+
expect(actionCount).toBe(0);
|
|
74
|
+
expect(rootDispatch.mock.calls.length).toBe(0);
|
|
75
|
+
dispatch(
|
|
76
|
+
createSet(Article, { args: [{ id: 1 }], value: { id: 1, title: 'hi' } }),
|
|
77
|
+
);
|
|
78
|
+
expect(initCount).toBe(1);
|
|
79
|
+
expect(actionCount).toBe(1);
|
|
80
|
+
expect(rootDispatch.mock.calls.length).toBe(1);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
function middlewareDispatch(
|
|
84
|
+
middlewares: ReduxMiddleware[],
|
|
85
|
+
rootDispatch: Dispatch,
|
|
86
|
+
) {
|
|
87
|
+
const middlewareAPI: ReduxMiddlewareAPI = {
|
|
88
|
+
getState: () => ({}),
|
|
89
|
+
dispatch: action => rootDispatch(action),
|
|
90
|
+
};
|
|
91
|
+
const comp = compose(
|
|
92
|
+
middlewares.map(middleware => middleware(middlewareAPI)),
|
|
93
|
+
);
|
|
94
|
+
const dispatch = comp(rootDispatch);
|
|
95
|
+
return dispatch;
|
|
96
|
+
}
|
|
97
|
+
const compose = (fns: ((...args: any) => any)[]) => (initial: any) =>
|
|
98
|
+
fns.reduceRight((v, f) => f(v), initial);
|
|
@@ -21,11 +21,10 @@ describe('LogoutManager', () => {
|
|
|
21
21
|
const manager = new LogoutManager();
|
|
22
22
|
const getState = () => initialState;
|
|
23
23
|
|
|
24
|
-
describe('
|
|
24
|
+
describe('middleware', () => {
|
|
25
25
|
it('should return the same value every call', () => {
|
|
26
|
-
const a = manager.
|
|
27
|
-
expect(a).toBe(manager.
|
|
28
|
-
expect(a).toBe(manager.getMiddleware());
|
|
26
|
+
const a = manager.middleware;
|
|
27
|
+
expect(a).toBe(manager.middleware);
|
|
29
28
|
});
|
|
30
29
|
});
|
|
31
30
|
|
|
@@ -33,7 +32,6 @@ describe('LogoutManager', () => {
|
|
|
33
32
|
afterEach(() => {
|
|
34
33
|
jest.useRealTimers();
|
|
35
34
|
});
|
|
36
|
-
const middleware = manager.getMiddleware();
|
|
37
35
|
const next = jest.fn();
|
|
38
36
|
const dispatch = jest.fn(action => Promise.resolve());
|
|
39
37
|
const controller = new Controller({ dispatch, getState });
|
|
@@ -48,7 +46,7 @@ describe('LogoutManager', () => {
|
|
|
48
46
|
args: [{ id: 5 }],
|
|
49
47
|
response: { id: 5, title: 'hi' },
|
|
50
48
|
});
|
|
51
|
-
await middleware(API)(next)(action);
|
|
49
|
+
await manager.middleware(API)(next)(action);
|
|
52
50
|
|
|
53
51
|
expect(dispatch.mock.calls.length).toBe(0);
|
|
54
52
|
});
|
|
@@ -60,7 +58,7 @@ describe('LogoutManager', () => {
|
|
|
60
58
|
response: error,
|
|
61
59
|
error: true,
|
|
62
60
|
});
|
|
63
|
-
await middleware(API)(next)(action);
|
|
61
|
+
await manager.middleware(API)(next)(action);
|
|
64
62
|
|
|
65
63
|
expect(dispatch.mock.calls.length).toBe(0);
|
|
66
64
|
});
|
|
@@ -72,7 +70,7 @@ describe('LogoutManager', () => {
|
|
|
72
70
|
response: error,
|
|
73
71
|
error: true,
|
|
74
72
|
});
|
|
75
|
-
await middleware(API)(next)(action);
|
|
73
|
+
await manager.middleware(API)(next)(action);
|
|
76
74
|
|
|
77
75
|
expect(dispatch.mock.calls.length).toBe(1);
|
|
78
76
|
expect(dispatch.mock.calls[0][0]?.type).toBe(RESET_TYPE);
|
|
@@ -85,7 +83,7 @@ describe('LogoutManager', () => {
|
|
|
85
83
|
return error.status === 403;
|
|
86
84
|
},
|
|
87
85
|
handleLogout,
|
|
88
|
-
}).
|
|
86
|
+
}).middleware;
|
|
89
87
|
const error: any = new Error('network failed');
|
|
90
88
|
error.status = 403;
|
|
91
89
|
const action = createSetResponse(CoolerArticleResource.get, {
|
|
@@ -102,7 +100,7 @@ describe('LogoutManager', () => {
|
|
|
102
100
|
const action = { type: FETCH_TYPE };
|
|
103
101
|
next.mockReset();
|
|
104
102
|
|
|
105
|
-
await middleware(API)(next)(action as any);
|
|
103
|
+
await manager.middleware(API)(next)(action as any);
|
|
106
104
|
|
|
107
105
|
expect(next.mock.calls.length).toBe(1);
|
|
108
106
|
});
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import Controller from '../../controller/Controller';
|
|
2
|
-
import { Middleware } from '../../middlewareTypes';
|
|
3
2
|
import { ActionTypes } from '../../types';
|
|
4
3
|
import NetworkManager from '../NetworkManager';
|
|
5
4
|
|
|
6
|
-
const
|
|
5
|
+
const netMgr = new NetworkManager();
|
|
7
6
|
it('middlewares should compose with non-data-client middlewares', () => {
|
|
8
7
|
type AnotherAction = {
|
|
9
8
|
type: 'BOB';
|
|
@@ -30,7 +29,7 @@ it('middlewares should compose with non-data-client middlewares', () => {
|
|
|
30
29
|
counter++;
|
|
31
30
|
};
|
|
32
31
|
|
|
33
|
-
const [a, b] = [middleware(API), nonRHMiddleware(API)];
|
|
32
|
+
const [a, b] = [netMgr.middleware(API), nonRHMiddleware(API)];
|
|
34
33
|
const dispA = a(b(dispatch));
|
|
35
34
|
const dispB = b(a(dispatch));
|
|
36
35
|
expect(dispatch.mock.calls.length).toBe(0);
|