From 5ad461ae36c0e377f9de8cf0cff863a357040b31 Mon Sep 17 00:00:00 2001 From: brendanjbond Date: Tue, 19 Mar 2024 09:45:13 +0100 Subject: [PATCH 1/5] replace ValidatorError with more generic ProcessorError --- src/error/DereferenceError.ts | 1 - src/error/ProcessorError.ts | 10 ++ src/error/ValidatorError.ts | 1 - src/error/index.ts | 3 +- src/process/dereference/index.ts | 6 +- .../rules/validateAvailableItems.ts | 113 ++++++++++-------- .../validation/rules/validateCaptcha.ts | 6 +- .../validation/rules/validateMaximumDay.ts | 6 +- .../rules/validateMaximumSelectedCount.ts | 49 ++++---- .../validation/rules/validateMaximumValue.ts | 10 +- .../validation/rules/validateMaximumYear.ts | 8 +- .../validation/rules/validateMinimumDay.ts | 6 +- .../rules/validateMinimumSelectedCount.ts | 49 ++++---- .../validation/rules/validateMinimumValue.ts | 8 +- .../validation/rules/validateMinimumYear.ts | 8 +- .../rules/validateRemoteSelectValue.ts | 10 +- .../validation/rules/validateRequiredDay.ts | 6 +- .../rules/validateRequiredSurvey.ts | 4 +- src/process/validation/rules/validateTime.ts | 4 +- .../validation/rules/validateUnique.ts | 6 +- 20 files changed, 177 insertions(+), 137 deletions(-) delete mode 100644 src/error/DereferenceError.ts create mode 100644 src/error/ProcessorError.ts delete mode 100644 src/error/ValidatorError.ts diff --git a/src/error/DereferenceError.ts b/src/error/DereferenceError.ts deleted file mode 100644 index 27f2097c..00000000 --- a/src/error/DereferenceError.ts +++ /dev/null @@ -1 +0,0 @@ -export class DereferenceError extends Error {}; diff --git a/src/error/ProcessorError.ts b/src/error/ProcessorError.ts new file mode 100644 index 00000000..7f3a882a --- /dev/null +++ b/src/error/ProcessorError.ts @@ -0,0 +1,10 @@ +import { ProcessorContext } from "types"; +export class ProcessorError extends Error { + context: Omit, 'scope'>; + constructor(message: string, context: ProcessorContext, processor: string = 'unknown') { + super(message); + this.message = `${message}\nin ${processor} at ${context.path}`; + const { component, path, data, row } = context; + this.context = {component, path, data, row}; + } +}; diff --git a/src/error/ValidatorError.ts b/src/error/ValidatorError.ts deleted file mode 100644 index 0af16886..00000000 --- a/src/error/ValidatorError.ts +++ /dev/null @@ -1 +0,0 @@ -export class ValidatorError extends Error {} diff --git a/src/error/index.ts b/src/error/index.ts index 4e2f6a33..ca6076db 100644 --- a/src/error/index.ts +++ b/src/error/index.ts @@ -1,3 +1,2 @@ export * from './FieldError'; -export * from './ValidatorError'; -export * from './DereferenceError'; +export * from './ProcessorError'; diff --git a/src/process/dereference/index.ts b/src/process/dereference/index.ts index a3a2be14..9ac3ac47 100644 --- a/src/process/dereference/index.ts +++ b/src/process/dereference/index.ts @@ -1,4 +1,4 @@ -import { DereferenceError } from "error"; +import { ProcessorError } from "error"; import { ProcessorFn, ProcessorScope, @@ -37,7 +37,7 @@ export const dereferenceProcess: ProcessorFn = async (context) return; } if (!config?.database) { - throw new DereferenceError('Cannot dereference resource value without a database config object'); + throw new ProcessorError('Cannot dereference resource value without a database config object'); } try { @@ -49,7 +49,7 @@ export const dereferenceProcess: ProcessorFn = async (context) component.components = vmCompatibleComponents; } catch (err: any) { - throw new DereferenceError(err.message || err); + throw new ProcessorError(err.message || err); } } diff --git a/src/process/validation/rules/validateAvailableItems.ts b/src/process/validation/rules/validateAvailableItems.ts index 61481322..9ac3c3d8 100644 --- a/src/process/validation/rules/validateAvailableItems.ts +++ b/src/process/validation/rules/validateAvailableItems.ts @@ -1,5 +1,5 @@ import isEmpty from 'lodash/isEmpty'; -import { FieldError, ValidatorError } from 'error'; +import { FieldError, ProcessorError } from 'error'; import { Evaluator } from 'utils'; import { RadioComponent, SelectComponent, RuleFn, RuleFnSync, ValidationContext } from 'types'; import { isObject, isPromise } from '../util'; @@ -42,7 +42,7 @@ async function getAvailableSelectValues(component: SelectComponent) { if (Array.isArray(component.data.values)) { return mapStaticValues(component.data.values); } - throw new ValidatorError( + throw new Error( `Failed to validate available values in static values select component '${component.key}': the values are not an array`, ); case 'json': { @@ -50,7 +50,7 @@ async function getAvailableSelectValues(component: SelectComponent) { try { return mapDynamicValues(component, JSON.parse(component.data.json)); } catch (err) { - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': ${err}` ); } @@ -58,7 +58,7 @@ async function getAvailableSelectValues(component: SelectComponent) { // TODO: need to retype this return mapDynamicValues(component, component.data.json as Record[]); } else { - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': the values are not an array` ); } @@ -76,19 +76,19 @@ async function getAvailableSelectValues(component: SelectComponent) { if (Array.isArray(resolvedCustomItems)) { return resolvedCustomItems; } - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': the values are not an array` ); } if (Array.isArray(customItems)) { return customItems; } else { - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': the values are not an array` ); } default: - throw new ValidatorError( + throw new Error( `Failed to validate available values in select component '${component.key}': data source ${component.dataSrc} is not valid}` ); } @@ -100,7 +100,7 @@ function getAvailableSelectValuesSync(component: SelectComponent) { if (Array.isArray(component.data?.values)) { return mapStaticValues(component.data.values); } - throw new ValidatorError( + throw new Error( `Failed to validate available values in static values select component '${component.key}': the values are not an array` ); case 'json': { @@ -108,7 +108,7 @@ function getAvailableSelectValuesSync(component: SelectComponent) { try { return mapDynamicValues(component, JSON.parse(component.data.json)); } catch (err) { - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': ${err}` ); } @@ -116,7 +116,7 @@ function getAvailableSelectValuesSync(component: SelectComponent) { // TODO: need to retype this return mapDynamicValues(component, component.data.json as Record[]); } else { - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': the values are not an array` ); } @@ -132,12 +132,12 @@ function getAvailableSelectValuesSync(component: SelectComponent) { if (Array.isArray(customItems)) { return customItems; } else { - throw new ValidatorError( + throw new Error( `Failed to validate available values in JSON select component '${component.key}': the values are not an array` ); } default: - throw new ValidatorError( + throw new Error( `Failed to validate available values in select component '${component.key}': data source ${component.dataSrc} is not valid}` ); } @@ -153,41 +153,45 @@ function compareComplexValues(valueA: unknown, valueB: unknown) { // this won't work return JSON.stringify(valueA) === JSON.stringify(valueB); } catch (err) { - throw new ValidatorError(`Error while comparing available values: ${err}`); + throw new Error(`Error while comparing available values: ${err}`); } } export const validateAvailableItems: RuleFn = async (context: ValidationContext) => { const { component, value } = context; const error = new FieldError('invalidOption', context); - if (isValidatableRadioComponent(component)) { - if (value == null || isEmpty(value)) { - return null; - } - - const values = component.values; - if (values) { - return values.findIndex(({ value: optionValue }) => optionValue === value) !== -1 - ? null - : error; - } + try { + if (isValidatableRadioComponent(component)) { + if (value == null || isEmpty(value)) { + return null; + } - return null; - } else if (isValidateableSelectComponent(component)) { - if (value == null || isEmpty(value)) { - return null; - } - const values = await getAvailableSelectValues(component); - if (values) { - if (isObject(value)) { - return values.find((optionValue) => compareComplexValues(optionValue, value)) !== - undefined + const values = component.values; + if (values) { + return values.findIndex(({ value: optionValue }) => optionValue === value) !== -1 ? null : error; } - return values.find((optionValue) => optionValue === value) !== undefined ? null : error; + return null; + } else if (isValidateableSelectComponent(component)) { + if (value == null || isEmpty(value)) { + return null; + } + const values = await getAvailableSelectValues(component); + if (values) { + if (isObject(value)) { + return values.find((optionValue) => compareComplexValues(optionValue, value)) !== + undefined + ? null + : error; + } + + return values.find((optionValue) => optionValue === value) !== undefined ? null : error; + } } + } catch (err: any) { + throw new ProcessorError(err.message || err, context, 'validate:validateAvailableItems'); } return null; }; @@ -209,28 +213,33 @@ export const shouldValidate = (context: any) => { export const validateAvailableItemsSync: RuleFnSync = (context: ValidationContext) => { const { component, value } = context; const error = new FieldError('invalidOption', context); - if (!shouldValidate(context)) { - return null; - } - if (isValidatableRadioComponent(component)) { - const values = component.values; - if (values) { - return values.findIndex(({ value: optionValue }) => optionValue === value) !== -1 - ? null - : error; + try { + + if (!shouldValidate(context)) { + return null; } - return null; - } else if (isValidateableSelectComponent(component)) { - const values = getAvailableSelectValuesSync(component); - if (values) { - if (isObject(value)) { - return values.find((optionValue) => compareComplexValues(optionValue, value)) !== - undefined + if (isValidatableRadioComponent(component)) { + const values = component.values; + if (values) { + return values.findIndex(({ value: optionValue }) => optionValue === value) !== -1 ? null : error; } - return values.find((optionValue) => optionValue === value) !== undefined ? null : error; + return null; + } else if (isValidateableSelectComponent(component)) { + const values = getAvailableSelectValuesSync(component); + if (values) { + if (isObject(value)) { + return values.find((optionValue) => compareComplexValues(optionValue, value)) !== + undefined + ? null + : error; + } + return values.find((optionValue) => optionValue === value) !== undefined ? null : error; + } } + } catch (err: any) { + throw new ProcessorError(err.message || err, context, 'validate:validateAvailableItems'); } return null; }; diff --git a/src/process/validation/rules/validateCaptcha.ts b/src/process/validation/rules/validateCaptcha.ts index 3b312b45..24a8148c 100644 --- a/src/process/validation/rules/validateCaptcha.ts +++ b/src/process/validation/rules/validateCaptcha.ts @@ -1,6 +1,6 @@ import { FieldError } from '../../../error/FieldError'; import { RuleFn, ValidationContext } from '../../../types/index'; -import { ValidatorError } from 'error'; +import { ProcessorError } from 'error'; import { ProcessorInfo } from 'types/process/ProcessorInfo'; export const shouldValidate = (context: ValidationContext) => { @@ -18,7 +18,7 @@ export const validateCaptcha: RuleFn = async (context: ValidationContext) => { } if (!config || !config.database) { - throw new ValidatorError("Can't test for recaptcha success without a database config object"); + throw new ProcessorError("Can't test for recaptcha success without a database config object", context, 'validate:validateCaptcha'); } try { if (!value || !value.token) { @@ -31,7 +31,7 @@ export const validateCaptcha: RuleFn = async (context: ValidationContext) => { return (captchaResult === true) ? null : new FieldError('captchaFailure', context); } catch (err: any) { - throw new ValidatorError(err.message || err); + throw new ProcessorError(err.message || err, context, 'validate:validateCaptcha'); } }; diff --git a/src/process/validation/rules/validateMaximumDay.ts b/src/process/validation/rules/validateMaximumDay.ts index 7fda9288..bc55280d 100644 --- a/src/process/validation/rules/validateMaximumDay.ts +++ b/src/process/validation/rules/validateMaximumDay.ts @@ -1,4 +1,4 @@ -import { ValidatorError, FieldError } from 'error'; +import { ProcessorError, FieldError } from 'error'; import { DayComponent, RuleFn, RuleFnSync, ValidationContext } from 'types'; import { dayjs, isPartialDay, getDateValidationFormat, getDateSetting } from 'utils/date'; import { ProcessorInfo } from 'types/process/ProcessorInfo'; @@ -31,7 +31,7 @@ export const validateMaximumDaySync: RuleFnSync = (context: ValidationContext) = return null; } if (typeof value !== 'string') { - throw new ValidatorError(`Cannot validate day value ${value} because it is not a string`); + throw new ProcessorError(`Cannot validate day value ${value} because it is not a string`, context, 'validate:validateMaximumDay'); } // TODO: this validation probably goes for dates and days const format = getDateValidationFormat(component as DayComponent); @@ -52,4 +52,4 @@ export const validateMaximumDayInfo: ProcessorInfo { function validateValue(value: DataObject[any]): asserts value is Record { if (value == null || typeof value !== 'object') { - throw new ValidatorError( + throw new Error( `Cannot validate maximum selected count for value ${value} as it is not an object` ); } const subValues = Object.values(value); if (!subValues.every((value) => typeof value === 'boolean')) { - throw new ValidatorError( + throw new Error( `Cannot validate maximum selected count for value ${value} because it has non-boolean members` ); } @@ -48,27 +48,32 @@ export const validateMaximumSelectedCount: RuleFn = async (context: ValidationCo export const validateMaximumSelectedCountSync: RuleFnSync = (context: ValidationContext) => { const { component, value } = context; - if (!shouldValidate(context)) { - return null; - } - validateValue(value); - const max = getValidationSetting(component as SelectBoxesComponent); - if (!max) { - return null; - } - const count = Object.keys(value).reduce((sum, key) => (value[key] ? ++sum : sum), 0); + try { + + if (!shouldValidate(context)) { + return null; + } + validateValue(value); + const max = getValidationSetting(component as SelectBoxesComponent); + if (!max) { + return null; + } + const count = Object.keys(value).reduce((sum, key) => (value[key] ? ++sum : sum), 0); - // Should not be triggered if there is no options selected at all - if (count <= 0) { - return null; + // Should not be triggered if there is no options selected at all + if (count <= 0) { + return null; + } + return count > max + ? new FieldError((component as SelectBoxesComponent).maxSelectedCountMessage || 'maxSelectedCount', { + ...context, + maxCount: String(max), + setting: String(max), + }) + : null; + } catch (err: any) { + throw new ProcessorError(err.message || err, context, 'validate:validateMaximumSelectedCount'); } - return count > max - ? new FieldError((component as SelectBoxesComponent).maxSelectedCountMessage || 'maxSelectedCount', { - ...context, - maxCount: String(max), - setting: String(max), - }) - : null; } export const validateMaximumSelectedCountInfo: ProcessorInfo = { diff --git a/src/process/validation/rules/validateMaximumValue.ts b/src/process/validation/rules/validateMaximumValue.ts index 9a13d031..8fc279c7 100644 --- a/src/process/validation/rules/validateMaximumValue.ts +++ b/src/process/validation/rules/validateMaximumValue.ts @@ -1,4 +1,4 @@ -import { FieldError, ValidatorError } from 'error'; +import { FieldError, ProcessorError } from 'error'; import { NumberComponent, RuleFn, RuleFnSync, ValidationContext } from 'types'; import { ProcessorInfo } from 'types/process/ProcessorInfo'; @@ -43,8 +43,10 @@ export const validateMaximumValueSync: RuleFnSync = (context: ValidationContext) } const parsedValue = typeof value === 'string' ? parseFloat(value) : Number(value); if (Number.isNaN(parsedValue)) { - throw new ValidatorError( - `Cannot validate value ${parsedValue} because it is invalid` + throw new ProcessorError( + `Cannot validate value ${parsedValue} because it is invalid`, + context, + 'validate:validateMaximumValue' ); } @@ -58,4 +60,4 @@ export const validateMaximumValueInfo: ProcessorInfo { function validateValue(value: DataObject[any]): asserts value is Record { if (value == null || typeof value !== 'object') { - throw new ValidatorError( + throw new Error( `Cannot validate maximum selected count for value ${value} as it is not an object` ); } const subValues = Object.values(value); if (!subValues.every((value) => typeof value === 'boolean')) { - throw new ValidatorError( + throw new Error( `Cannot validate maximum selected count for value ${value} because it has non-boolean members` ); } @@ -48,27 +48,32 @@ export const validateMinimumSelectedCount: RuleFn = async (context: ValidationCo export const validateMinimumSelectedCountSync: RuleFnSync = (context: ValidationContext) => { const { component, value } = context; - if (!shouldValidate(context)) { - return null; - } - validateValue(value); - const min = getValidationSetting((component as SelectBoxesComponent)); - if (!min) { - return null; - } - const count = Object.keys(value).reduce((sum, key) => (value[key] ? ++sum : sum), 0); + try { + + if (!shouldValidate(context)) { + return null; + } + validateValue(value); + const min = getValidationSetting((component as SelectBoxesComponent)); + if (!min) { + return null; + } + const count = Object.keys(value).reduce((sum, key) => (value[key] ? ++sum : sum), 0); - // Should not be triggered if there are no options selected at all - if (count <= 0) { - return null; + // Should not be triggered if there are no options selected at all + if (count <= 0) { + return null; + } + return count < min + ? new FieldError((component as SelectBoxesComponent).minSelectedCountMessage || 'minSelectedCount', { + ...context, + minCount: String(min), + setting: String(min), + }) + : null; + } catch (err: any) { + throw new ProcessorError(err.message || err, context, 'validate:validateMinimumSelectedCount'); } - return count < min - ? new FieldError((component as SelectBoxesComponent).minSelectedCountMessage || 'minSelectedCount', { - ...context, - minCount: String(min), - setting: String(min), - }) - : null; }; export const validateMinimumSelectedCountInfo: ProcessorInfo = { diff --git a/src/process/validation/rules/validateMinimumValue.ts b/src/process/validation/rules/validateMinimumValue.ts index 648373fc..79d41323 100644 --- a/src/process/validation/rules/validateMinimumValue.ts +++ b/src/process/validation/rules/validateMinimumValue.ts @@ -1,4 +1,4 @@ -import { FieldError, ValidatorError } from 'error'; +import { FieldError, ProcessorError } from 'error'; import { NumberComponent, RuleFn, RuleFnSync, ValidationContext } from 'types'; import { ProcessorInfo } from 'types/process/ProcessorInfo'; @@ -43,8 +43,10 @@ export const validateMinimumValueSync: RuleFnSync = (context: ValidationContext) } const parsedValue = typeof value === 'string' ? parseFloat(value) : Number(value); if (Number.isNaN(parsedValue)) { - throw new ValidatorError( + throw new ProcessorError( `Cannot validate value ${parsedValue} because it is invalid`, + context, + 'validate:validateMinimumValue' ); } @@ -58,4 +60,4 @@ export const validateMinimumValueInfo: ProcessorInfo { return isValid ? null : new FieldError('time', context); } catch (err) { - throw new ValidatorError(`Could not validate time component ${component.key} with value ${value}`); + throw new ProcessorError(`Could not validate time component ${component.key} with value ${value}`, context, 'validate:validateTime'); } } diff --git a/src/process/validation/rules/validateUnique.ts b/src/process/validation/rules/validateUnique.ts index bedb0fbf..7cfe2b4f 100644 --- a/src/process/validation/rules/validateUnique.ts +++ b/src/process/validation/rules/validateUnique.ts @@ -1,7 +1,7 @@ import { FieldError } from '../../../error/FieldError'; import { RuleFn, ValidationContext } from '../../../types/index'; import { isEmptyObject } from '../util'; -import { ValidatorError } from 'error'; +import { ProcessorError} from 'error'; import { ProcessorInfo } from 'types/process/ProcessorInfo'; export const shouldValidate = (context: ValidationContext) => { @@ -23,7 +23,7 @@ export const validateUnique: RuleFn = async (context: ValidationContext) => { } if (!config || !config.database) { - throw new ValidatorError("Can't test for unique value without a database config object"); + throw new ProcessorError("Can't test for unique value without a database config object", context, 'validate:validateUnique'); } try { const isUnique = await config.database?.isUnique(context, value); @@ -36,7 +36,7 @@ export const validateUnique: RuleFn = async (context: ValidationContext) => { return (isUnique === true) ? null : new FieldError('unique', context); } catch (err: any) { - throw new ValidatorError(err.message || err); + throw new ProcessorError(err.message || err, context, 'validate:validateUnique'); } }; From f56bd691098048fd6291d4f00ffe34aa6076d1ea Mon Sep 17 00:00:00 2001 From: brendanjbond Date: Tue, 19 Mar 2024 10:27:07 +0100 Subject: [PATCH 2/5] minor --- src/process/dereference/index.ts | 4 +- .../validation/rules/validateCustom.ts | 68 ++++++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/process/dereference/index.ts b/src/process/dereference/index.ts index 9ac3ac47..b0850c90 100644 --- a/src/process/dereference/index.ts +++ b/src/process/dereference/index.ts @@ -37,7 +37,7 @@ export const dereferenceProcess: ProcessorFn = async (context) return; } if (!config?.database) { - throw new ProcessorError('Cannot dereference resource value without a database config object'); + throw new ProcessorError('Cannot dereference resource value without a database config object', context, 'dereference'); } try { @@ -49,7 +49,7 @@ export const dereferenceProcess: ProcessorFn = async (context) component.components = vmCompatibleComponents; } catch (err: any) { - throw new ProcessorError(err.message || err); + throw new ProcessorError(err.message || err, context, 'dereference'); } } diff --git a/src/process/validation/rules/validateCustom.ts b/src/process/validation/rules/validateCustom.ts index c1f618ff..e2d15d2d 100644 --- a/src/process/validation/rules/validateCustom.ts +++ b/src/process/validation/rules/validateCustom.ts @@ -1,16 +1,14 @@ import { isEmpty } from 'lodash'; -import { RuleFn, RuleFnSync } from 'types/RuleFn'; -import { FieldError } from 'error/FieldError'; +import { RuleFn, RuleFnSync, ProcessorInfo, ValidationContext } from 'types'; +import { FieldError, ProcessorError } from 'error'; import { Evaluator } from 'utils'; -import { ValidationContext } from 'types'; -import { ProcessorInfo } from 'types/process/ProcessorInfo'; export const validateCustom: RuleFn = async (context: ValidationContext) => { return validateCustomSync(context); }; export const shouldValidate = (context: ValidationContext) => { - const { component, value } = context; + const { component } = context; const customValidation = component.validate?.custom; if (!customValidation) { return false; @@ -21,35 +19,39 @@ export const shouldValidate = (context: ValidationContext) => { export const validateCustomSync: RuleFnSync = (context: ValidationContext) => { const { component, data, row, value, index, instance, evalContext } = context; const customValidation = component.validate?.custom; - if (!shouldValidate(context)) { - return null; + try { + if (!shouldValidate(context)) { + return null; + } + + const evalContextValue = { + ...(instance?.evalContext ? instance.evalContext() : (evalContext ? evalContext(context) : context)), + component, + data, + row, + rowIndex: index, + instance, + valid: true, + input: value, + } + + const isValid = Evaluator.evaluate( + customValidation, + evalContextValue, + 'valid', + true, + {}, + {} + ); + + if (isValid === null || isValid === true) { + return null; + } + + return new FieldError(typeof isValid === 'string' ? isValid : 'custom', {...context, hasLabel: false }); + } catch (err: any) { + throw new ProcessorError(err.message || err, context, 'validate:validateCustom'); } - - const evalContextValue = { - ...(instance?.evalContext ? instance.evalContext() : (evalContext ? evalContext(context) : context)), - component, - data, - row, - rowIndex: index, - instance, - valid: true, - input: value, - } - - const isValid = Evaluator.evaluate( - customValidation, - evalContextValue, - 'valid', - true, - {}, - {} - ); - - if (isValid === null || isValid === true) { - return null; - } - - return new FieldError(typeof isValid === 'string' ? isValid : 'custom', {...context, hasLabel: false }); }; From b18b7befd211b04430b53ee3e6e7f9c20bbc3baf Mon Sep 17 00:00:00 2001 From: brendanjbond Date: Mon, 8 Apr 2024 11:09:07 -0500 Subject: [PATCH 3/5] fix omission of processorerror --- .../rules/validateAvailableItems.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/process/validation/rules/validateAvailableItems.ts b/src/process/validation/rules/validateAvailableItems.ts index 9ac3c3d8..991f5b85 100644 --- a/src/process/validation/rules/validateAvailableItems.ts +++ b/src/process/validation/rules/validateAvailableItems.ts @@ -36,7 +36,7 @@ function mapStaticValues(values: { label: string; value: string }[]) { return values.map((obj) => obj.value); } -async function getAvailableSelectValues(component: SelectComponent) { +async function getAvailableSelectValues(component: SelectComponent, context: ValidationContext) { switch (component.dataSrc) { case 'values': if (Array.isArray(component.data.values)) { @@ -76,8 +76,10 @@ async function getAvailableSelectValues(component: SelectComponent) { if (Array.isArray(resolvedCustomItems)) { return resolvedCustomItems; } - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': the values are not an array` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); } if (Array.isArray(customItems)) { @@ -94,7 +96,7 @@ async function getAvailableSelectValues(component: SelectComponent) { } } -function getAvailableSelectValuesSync(component: SelectComponent) { +function getAvailableSelectValuesSync(component: SelectComponent, context: ValidationContext) { switch (component.dataSrc) { case 'values': if (Array.isArray(component.data?.values)) { @@ -108,16 +110,20 @@ function getAvailableSelectValuesSync(component: SelectComponent) { try { return mapDynamicValues(component, JSON.parse(component.data.json)); } catch (err) { - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': ${err}` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': ${err}`, + context, + 'validate:validateAvailableItems' ); } } else if (Array.isArray(component.data.json)) { // TODO: need to retype this return mapDynamicValues(component, component.data.json as Record[]); } else { - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': the values are not an array` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); } } @@ -178,7 +184,7 @@ export const validateAvailableItems: RuleFn = async (context: ValidationContext) if (value == null || isEmpty(value)) { return null; } - const values = await getAvailableSelectValues(component); + const values = await getAvailableSelectValues(component, context); if (values) { if (isObject(value)) { return values.find((optionValue) => compareComplexValues(optionValue, value)) !== @@ -227,7 +233,7 @@ export const validateAvailableItemsSync: RuleFnSync = (context: ValidationContex } return null; } else if (isValidateableSelectComponent(component)) { - const values = getAvailableSelectValuesSync(component); + const values = getAvailableSelectValuesSync(component, context); if (values) { if (isObject(value)) { return values.find((optionValue) => compareComplexValues(optionValue, value)) !== From ac961eb8ac45f1e61db62ca2810a883ddc2932c2 Mon Sep 17 00:00:00 2001 From: brendanjbond Date: Mon, 8 Apr 2024 12:15:13 -0500 Subject: [PATCH 4/5] add more processorerrors --- .../rules/validateAvailableItems.ts | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/process/validation/rules/validateAvailableItems.ts b/src/process/validation/rules/validateAvailableItems.ts index 991f5b85..75fcf27d 100644 --- a/src/process/validation/rules/validateAvailableItems.ts +++ b/src/process/validation/rules/validateAvailableItems.ts @@ -42,24 +42,30 @@ async function getAvailableSelectValues(component: SelectComponent, context: Val if (Array.isArray(component.data.values)) { return mapStaticValues(component.data.values); } - throw new Error( + throw new ProcessorError( `Failed to validate available values in static values select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); case 'json': { if (typeof component.data.json === 'string') { try { return mapDynamicValues(component, JSON.parse(component.data.json)); } catch (err) { - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': ${err}` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': ${err}`, + context, + 'validate:validateAvailableItems' ); } } else if (Array.isArray(component.data.json)) { // TODO: need to retype this return mapDynamicValues(component, component.data.json as Record[]); } else { - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': the values are not an array` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); } } @@ -85,13 +91,17 @@ async function getAvailableSelectValues(component: SelectComponent, context: Val if (Array.isArray(customItems)) { return customItems; } else { - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': the values are not an array` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); } default: - throw new Error( - `Failed to validate available values in select component '${component.key}': data source ${component.dataSrc} is not valid}` + throw new ProcessorError( + `Failed to validate available values in select component '${component.key}': data source ${component.dataSrc} is not valid}`, + context, + 'validate:validateAvailableItems' ); } } @@ -102,8 +112,10 @@ function getAvailableSelectValuesSync(component: SelectComponent, context: Valid if (Array.isArray(component.data?.values)) { return mapStaticValues(component.data.values); } - throw new Error( - `Failed to validate available values in static values select component '${component.key}': the values are not an array` + throw new ProcessorError( + `Failed to validate available values in static values select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); case 'json': { if (typeof component.data.json === 'string') { @@ -138,18 +150,22 @@ function getAvailableSelectValuesSync(component: SelectComponent, context: Valid if (Array.isArray(customItems)) { return customItems; } else { - throw new Error( - `Failed to validate available values in JSON select component '${component.key}': the values are not an array` + throw new ProcessorError( + `Failed to validate available values in JSON select component '${component.key}': the values are not an array`, + context, + 'validate:validateAvailableItems' ); } default: - throw new Error( - `Failed to validate available values in select component '${component.key}': data source ${component.dataSrc} is not valid}` + throw new ProcessorError( + `Failed to validate available values in select component '${component.key}': data source ${component.dataSrc} is not valid}`, + context, + 'validate:validateAvailableItems' ); } } -function compareComplexValues(valueA: unknown, valueB: unknown) { +function compareComplexValues(valueA: unknown, valueB: unknown, context: ValidationContext) { if (!isObject(valueA) || !isObject(valueB)) { return false; } @@ -159,7 +175,7 @@ function compareComplexValues(valueA: unknown, valueB: unknown) { // this won't work return JSON.stringify(valueA) === JSON.stringify(valueB); } catch (err) { - throw new Error(`Error while comparing available values: ${err}`); + throw new ProcessorError(`Error while comparing available values: ${err}`, context, 'validate:validateAvailableItems'); } } @@ -187,7 +203,7 @@ export const validateAvailableItems: RuleFn = async (context: ValidationContext) const values = await getAvailableSelectValues(component, context); if (values) { if (isObject(value)) { - return values.find((optionValue) => compareComplexValues(optionValue, value)) !== + return values.find((optionValue) => compareComplexValues(optionValue, value, context)) !== undefined ? null : error; @@ -236,7 +252,7 @@ export const validateAvailableItemsSync: RuleFnSync = (context: ValidationContex const values = getAvailableSelectValuesSync(component, context); if (values) { if (isObject(value)) { - return values.find((optionValue) => compareComplexValues(optionValue, value)) !== + return values.find((optionValue) => compareComplexValues(optionValue, value, context)) !== undefined ? null : error; From 9d5d1d9123a535e81f9541a80900c405cd7e89ae Mon Sep 17 00:00:00 2001 From: brendanjbond Date: Tue, 16 Apr 2024 09:44:57 -0500 Subject: [PATCH 5/5] finally remove all Error objects to ProcessorError --- .../rules/validateMaximumSelectedCount.ts | 16 ++++++++++------ .../rules/validateMinimumSelectedCount.ts | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/process/validation/rules/validateMaximumSelectedCount.ts b/src/process/validation/rules/validateMaximumSelectedCount.ts index cb4562cd..d093dfe3 100644 --- a/src/process/validation/rules/validateMaximumSelectedCount.ts +++ b/src/process/validation/rules/validateMaximumSelectedCount.ts @@ -28,16 +28,20 @@ export const shouldValidate = (context: ValidationContext) => { return true; }; -function validateValue(value: DataObject[any]): asserts value is Record { +function validateValue(value: DataObject[any], context: ValidationContext): asserts value is Record { if (value == null || typeof value !== 'object') { - throw new Error( - `Cannot validate maximum selected count for value ${value} as it is not an object` + throw new ProcessorError( + `Cannot validate maximum selected count for value ${value} as it is not an object`, + context, + 'validate:validateMaximumSelectedCount' ); } const subValues = Object.values(value); if (!subValues.every((value) => typeof value === 'boolean')) { - throw new Error( - `Cannot validate maximum selected count for value ${value} because it has non-boolean members` + throw new ProcessorError( + `Cannot validate maximum selected count for value ${value} because it has non-boolean members`, + context, + 'validate:validateMaximumSelectedCount' ); } } @@ -53,7 +57,7 @@ export const validateMaximumSelectedCountSync: RuleFnSync = (context: Validation if (!shouldValidate(context)) { return null; } - validateValue(value); + validateValue(value, context); const max = getValidationSetting(component as SelectBoxesComponent); if (!max) { return null; diff --git a/src/process/validation/rules/validateMinimumSelectedCount.ts b/src/process/validation/rules/validateMinimumSelectedCount.ts index 1e1b2c31..70efcf40 100644 --- a/src/process/validation/rules/validateMinimumSelectedCount.ts +++ b/src/process/validation/rules/validateMinimumSelectedCount.ts @@ -28,16 +28,20 @@ export const shouldValidate = (context: ValidationContext) => { return true; }; -function validateValue(value: DataObject[any]): asserts value is Record { +function validateValue(value: DataObject[any], context: ValidationContext): asserts value is Record { if (value == null || typeof value !== 'object') { - throw new Error( - `Cannot validate maximum selected count for value ${value} as it is not an object` + throw new ProcessorError( + `Cannot validate maximum selected count for value ${value} as it is not an object`, + context, + 'validate:validateMinimumSelectedCount' ); } const subValues = Object.values(value); if (!subValues.every((value) => typeof value === 'boolean')) { - throw new Error( - `Cannot validate maximum selected count for value ${value} because it has non-boolean members` + throw new ProcessorError( + `Cannot validate maximum selected count for value ${value} because it has non-boolean members`, + context, + 'validate:validateMinimumSelectedCount' ); } } @@ -53,7 +57,7 @@ export const validateMinimumSelectedCountSync: RuleFnSync = (context: Validation if (!shouldValidate(context)) { return null; } - validateValue(value); + validateValue(value, context); const min = getValidationSetting((component as SelectBoxesComponent)); if (!min) { return null;