Skip to content

Commit e49dc30

Browse files
committed
fix(davinci-client): replace reducer throws with collector.error state writes
Eliminates thrown errors from updateCollectorValues — all validation failures now write to collector.error instead, keeping Redux state predictable and observable rather than blowing up the call stack. Also adds an explicit PollingCollector type guard before the category branches so that polling collectors (which are categorized as SingleValueAutoCollector) are correctly rejected as read-only.
1 parent 934cf6f commit e49dc30

1 file changed

Lines changed: 81 additions & 39 deletions

File tree

packages/davinci-client/src/lib/node.reducer.ts

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,41 @@ export const nodeCollectorReducer = createReducer(initialCollectorValues, (build
191191
*/
192192
.addCase(updateCollectorValues, (state, action) => {
193193
const collector = state.find((collector) => collector.id === action.payload.id);
194+
194195
if (!collector) {
195-
throw new Error('No collector found to update');
196+
state.push({
197+
category: 'UnknownCollector',
198+
error: 'No collector found to update',
199+
type: 'UnknownCollector',
200+
id: action.payload.id,
201+
name: action.payload.id,
202+
output: {
203+
key: action.payload.id,
204+
label: action.payload.id,
205+
type: 'UnknownCollector',
206+
},
207+
});
208+
return;
196209
}
210+
197211
if (collector.category === 'ActionCollector') {
198-
throw new Error('ActionCollectors are read-only');
212+
collector.error = 'ActionCollectors are read-only';
213+
return;
199214
}
215+
200216
if (collector.category === 'NoValueCollector') {
201-
throw new Error('NoValueCollectors, like ReadOnlyCollectors, are read-only');
217+
collector.error = 'NoValueCollectors, like ReadOnlyCollectors, are read-only';
218+
return;
202219
}
220+
221+
if (collector.type === 'PollingCollector') {
222+
collector.error = 'This collector type does not support value updates';
223+
return;
224+
}
225+
203226
if (action.payload.value === undefined) {
204-
throw new Error('Value argument cannot be undefined');
227+
collector.error = 'Value argument cannot be undefined';
228+
return;
205229
}
206230

207231
if (
@@ -221,15 +245,17 @@ export const nodeCollectorReducer = createReducer(initialCollectorValues, (build
221245
}
222246

223247
if (typeof action.payload.value !== 'string') {
224-
throw new Error('Value argument must be a string');
248+
collector.error = 'Value argument must be a string';
249+
return;
225250
}
226251
collector.input.value = action.payload.value;
227252
return;
228253
}
229254

230255
if (collector.category === 'MultiValueCollector') {
231256
if (typeof action.payload.value !== 'string' && !Array.isArray(action.payload.value)) {
232-
throw new Error('MultiValueCollector does not accept an object');
257+
collector.error = 'MultiValueCollector does not accept an object';
258+
return;
233259
}
234260
if (Array.isArray(action.payload.value)) {
235261
collector.input.value = [...action.payload.value];
@@ -240,99 +266,115 @@ export const nodeCollectorReducer = createReducer(initialCollectorValues, (build
240266
}
241267

242268
if (collector.type === 'DeviceAuthenticationCollector') {
243-
const inputValue = action.payload.value;
244-
if (typeof inputValue !== 'string') {
245-
throw new Error('Value argument must be a string');
269+
if (typeof action.payload.value !== 'string') {
270+
collector.error = 'Value argument must be a string';
271+
return;
246272
}
247-
248-
// Iterate through the options object and find option to update
249-
const option = collector.output.options.find((option) => option.value === inputValue);
250-
273+
const option = collector.output.options.find(
274+
(option) => option.value === action.payload.value,
275+
);
251276
if (!option) {
252-
throw new Error('No option found matching value to update');
277+
collector.error = 'No option found matching value to update';
278+
return;
253279
}
254-
255-
// Remap values back to DaVinci spec
256280
collector.input.value = {
257281
type: option.type,
258282
id: option.value,
259283
value: option.content,
260284
};
285+
return;
261286
}
262287

263288
if (collector.type === 'DeviceRegistrationCollector') {
264-
const inputValue = action.payload.value;
265-
if (typeof inputValue !== 'string') {
266-
throw new Error('Value argument must be a string');
289+
if (typeof action.payload.value !== 'string') {
290+
collector.error = 'Value argument must be a string';
291+
return;
267292
}
268-
269-
// Iterate through the options object and find option to update
270-
const option = collector.output.options.find((option) => option.value === inputValue);
271-
293+
const option = collector.output.options.find(
294+
(option) => option.value === action.payload.value,
295+
);
272296
if (!option) {
273-
throw new Error('No option found matching value to update');
297+
collector.error = 'No option found matching value to update';
298+
return;
274299
}
275-
276300
collector.input.value = option.type;
301+
return;
277302
}
278303

279304
if (collector.type === 'PhoneNumberCollector') {
280305
if (typeof action.payload.id !== 'string') {
281-
throw new Error('Index argument must be a string');
306+
collector.error = 'Index argument must be a string';
307+
return;
282308
}
283309
if (typeof action.payload.value !== 'object') {
284-
throw new Error('Value argument must be an object');
310+
collector.error = 'Value argument must be an object';
311+
return;
285312
}
286313
if (!('phoneNumber' in action.payload.value) || !('countryCode' in action.payload.value)) {
287-
throw new Error('Value argument must contain a phoneNumber and countryCode property');
314+
collector.error = 'Value argument must contain a phoneNumber and countryCode property';
315+
return;
288316
}
289317
collector.input.value = action.payload.value;
318+
return;
290319
}
291320

292321
if (collector.type === 'PhoneNumberExtensionCollector') {
293322
if (typeof action.payload.id !== 'string') {
294-
throw new Error('Index argument must be a string');
323+
collector.error = 'Index argument must be a string';
324+
return;
295325
}
296326
if (typeof action.payload.value !== 'object') {
297-
throw new Error('Value argument must be an object');
327+
collector.error = 'Value argument must be an object';
328+
return;
298329
}
299330
if (
300331
!('phoneNumber' in action.payload.value) ||
301332
!('countryCode' in action.payload.value) ||
302333
!('extension' in action.payload.value)
303334
) {
304-
throw new Error(
305-
'Value argument must contain a phoneNumber, countryCode, and extension property',
306-
);
335+
collector.error =
336+
'Value argument must contain a phoneNumber, countryCode, and extension property';
337+
return;
307338
}
308339
collector.input.value = action.payload.value;
340+
return;
309341
}
310342

311343
if (collector.type === 'FidoRegistrationCollector') {
312344
if (typeof action.payload.id !== 'string') {
313-
throw new Error('Index argument must be a string');
345+
collector.error = 'Index argument must be a string';
346+
return;
314347
}
315348
if (typeof action.payload.value !== 'object') {
316-
throw new Error('Value argument must be an object');
349+
collector.error = 'Value argument must be an object';
350+
return;
317351
}
318352
if (!('attestationValue' in action.payload.value)) {
319-
throw new Error('Value argument must contain an attestationValue property');
353+
collector.error = 'Value argument must contain an attestationValue property';
354+
return;
320355
}
321356
collector.input.value = action.payload.value;
357+
return;
322358
}
323359

324360
if (collector.type === 'FidoAuthenticationCollector') {
325361
if (typeof action.payload.id !== 'string') {
326-
throw new Error('Index argument must be a string');
362+
collector.error = 'Index argument must be a string';
363+
return;
327364
}
328365
if (typeof action.payload.value !== 'object') {
329-
throw new Error('Value argument must be an object');
366+
collector.error = 'Value argument must be an object';
367+
return;
330368
}
331369
if (!('assertionValue' in action.payload.value)) {
332-
throw new Error('Value argument must contain an assertionValue property');
370+
collector.error = 'Value argument must contain an assertionValue property';
371+
return;
333372
}
334373
collector.input.value = action.payload.value;
374+
return;
335375
}
376+
377+
collector.error = 'This collector type does not support value updates';
336378
})
337379
/**
338380
* Using the `pollCollectorValues` const (e.g. `'node/poll'`) to add the case

0 commit comments

Comments
 (0)