feat: add dynamic fields to validation schema and debug problem with dependsOn field

This commit is contained in:
Kasia
2023-12-13 14:45:52 +00:00
committed by kasia.oczkowska
parent 725b38c697
commit 47caa5aa37
2 changed files with 115 additions and 64 deletions

View File

@@ -11,9 +11,6 @@ import IconButton from '@mui/material/IconButton';
import ErrorIcon from '@mui/icons-material/Error';
import CircularProgress from '@mui/material/CircularProgress';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { EditorContext } from 'contexts/Editor';
import { StepExecutionsProvider } from 'contexts/StepExecutions';
import TestSubstep from 'components/TestSubstep';
@@ -33,77 +30,17 @@ import {
Header,
Wrapper,
} from './style';
import isEmpty from 'helpers/isEmpty';
import { StepPropType } from 'propTypes/propTypes';
import useTriggers from 'hooks/useTriggers';
import useActions from 'hooks/useActions';
import useTriggerSubsteps from 'hooks/useTriggerSubsteps';
import useActionSubsteps from 'hooks/useActionSubsteps';
import useStepWithTestExecutions from 'hooks/useStepWithTestExecutions';
import { generateValidationSchema } from './validation';
const validIcon = <CheckCircleIcon color="success" />;
const errorIcon = <ErrorIcon color="error" />;
function generateValidationSchema(substeps) {
const fieldValidations = substeps?.reduce(
(allValidations, { arguments: args }) => {
if (!args || !Array.isArray(args)) return allValidations;
const substepArgumentValidations = {};
for (const arg of args) {
const { key, required } = arg;
// base validation for the field if not exists
if (!substepArgumentValidations[key]) {
substepArgumentValidations[key] = yup.mixed();
}
if (
typeof substepArgumentValidations[key] === 'object' &&
(arg.type === 'string' || arg.type === 'dropdown')
) {
// if the field is required, add the required validation
if (required) {
substepArgumentValidations[key] = substepArgumentValidations[key]
.required(`${key} is required.`)
.test(
'empty-check',
`${key} must be not empty`,
(value) => !isEmpty(value),
);
}
// if the field depends on another field, add the dependsOn required validation
if (Array.isArray(arg.dependsOn) && arg.dependsOn.length > 0) {
for (const dependsOnKey of arg.dependsOn) {
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
substepArgumentValidations[key] = substepArgumentValidations[
key
].when(`${dependsOnKey.replace('parameters.', '')}`, {
is: (value) => Boolean(value) === false,
then: (schema) =>
schema
.notOneOf([''], missingDependencyValueMessage)
.required(missingDependencyValueMessage),
});
}
}
}
}
return {
...allValidations,
...substepArgumentValidations,
};
},
{},
);
const validationSchema = yup.object({
parameters: yup.object(fieldValidations),
});
return yupResolver(validationSchema);
}
function FlowStep(props) {
const { collapsed, onChange, onContinue, flowId } = props;
const editorContext = React.useContext(EditorContext);

View File

@@ -0,0 +1,114 @@
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import isEmpty from 'helpers/isEmpty';
function addRequiredValidation({ required, schema, key }) {
// if the field is required, add the required validation
if (required) {
return schema
.required(`${key} is required.`)
.test(
'empty-check',
`${key} must be not empty`,
(value) => !isEmpty(value),
);
}
return schema;
}
function addDependsOnValidation({ schema, dependsOn, key, args }) {
// if the field depends on another field, add the dependsOn required validation
if (Array.isArray(dependsOn) && dependsOn.length > 0) {
for (const dependsOnKey of dependsOn) {
const dependsOnKeyShort = dependsOnKey.replace('parameters.', '');
const dependsOnField = args.find(({ key }) => key === dependsOnKeyShort);
if (dependsOnField?.required) {
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
return schema.when(dependsOnKeyShort, {
is: (dependsOnValue) => Boolean(dependsOnValue) === false,
then: (schema) =>
schema
.notOneOf([''], missingDependencyValueMessage)
.required(missingDependencyValueMessage),
});
}
}
}
return schema;
}
export function generateValidationSchema(substeps) {
const fieldValidations = substeps?.reduce(
(allValidations, { arguments: args }) => {
if (!args || !Array.isArray(args)) return allValidations;
const substepArgumentValidations = {};
for (const arg of args) {
const { key, required } = arg;
// base validation for the field if not exists
if (!substepArgumentValidations[key]) {
substepArgumentValidations[key] = yup.mixed();
}
if (arg.type === 'dynamic') {
const fieldsSchema = {};
for (const field of arg.fields) {
fieldsSchema[field.key] = yup.mixed();
fieldsSchema[field.key] = addRequiredValidation({
required: field.required,
schema: fieldsSchema[field.key],
key: field.key,
});
fieldsSchema[field.key] = addDependsOnValidation({
schema: fieldsSchema[field.key],
dependsOn: field.dependsOn,
key: field.key,
args,
});
}
substepArgumentValidations[key] = yup
.array()
.of(yup.object(fieldsSchema));
} else if (
typeof substepArgumentValidations[key] === 'object' &&
(arg.type === 'string' || arg.type === 'dropdown')
) {
substepArgumentValidations[key] = addRequiredValidation({
required,
schema: substepArgumentValidations[key],
key,
});
substepArgumentValidations[key] = addDependsOnValidation({
schema: substepArgumentValidations[key],
dependsOn: arg.dependsOn,
key,
args,
});
}
}
return {
...allValidations,
...substepArgumentValidations,
};
},
{},
);
const validationSchema = yup.object({
parameters: yup.object(fieldValidations),
});
return yupResolver(validationSchema);
}