Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: [DHIS2-17854] validate the assigned values from rules engine #3783

Merged
merged 31 commits into from
Dec 5, 2024
Merged
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3d237e0
fix: validate the assigned values from rules engine
simonadomnisoru Aug 29, 2024
c367c1f
Merge branch 'master' into DHIS2-17854
simonadomnisoru Aug 29, 2024
3e8a59d
fix: validate the assigned values from rules engine
simonadomnisoru Sep 2, 2024
dd8f672
fix: validate the assigned values from rules engine
simonadomnisoru Sep 3, 2024
5df8667
chore: small improvements
simonadomnisoru Sep 3, 2024
71b5fa6
feat: extract validateField from FormBuilder into common external fun…
simonadomnisoru Sep 4, 2024
e3f6894
chore: improve ValidatorContainer type
simonadomnisoru Sep 9, 2024
ffa3ed8
chore: replace EMPTY with of()
simonadomnisoru Oct 3, 2024
5091965
chore: rollback formSectionFields checks
simonadomnisoru Oct 3, 2024
e855660
chore: replace assigned effects with a one-element array & lastIndex …
simonadomnisoru Oct 3, 2024
067bb56
Merge branch 'master' into DHIS2-17854
simonadomnisoru Oct 3, 2024
fcf1b3b
chore: variable rename
simonadomnisoru Oct 3, 2024
4d01a72
chore: rename validateField to validateValue
simonadomnisoru Oct 9, 2024
98dea41
chore: move the files to capture-core/rules and capture-core/utils/va…
simonadomnisoru Oct 10, 2024
0c6a995
chore: rename callback
simonadomnisoru Oct 10, 2024
f18f2c7
chore: remove object destructering
simonadomnisoru Oct 10, 2024
87d0d78
fix: properly update dataEntriesInProgressList while async validation…
simonadomnisoru Oct 23, 2024
64db572
fix: disable save button in WidgetProfile while async validation is r…
simonadomnisoru Oct 23, 2024
245bee6
Merge branch 'master' into DHIS2-17854
simonadomnisoru Oct 23, 2024
44990c1
Merge branch 'master' into DHIS2-17854
simonadomnisoru Nov 4, 2024
3b0a8fb
Merge branch 'master' into DHIS2-17854
simonadomnisoru Nov 6, 2024
54d42c7
Merge branch 'master' into DHIS2-17854
simonadomnisoru Nov 13, 2024
86bbb6e
chore: skip assigned values validation when opening forms
simonadomnisoru Nov 21, 2024
2ecf300
chore: skip assigned values validation when opening forms
simonadomnisoru Nov 21, 2024
8dfd816
Merge branch 'master' into DHIS2-17854
simonadomnisoru Nov 21, 2024
360933e
chore: skip assigned values validation when opening new event forms
simonadomnisoru Nov 25, 2024
7cf7867
Merge branch 'master' into DHIS2-17915
simonadomnisoru Nov 25, 2024
ed637cb
Merge branch 'master' into DHIS2-17854
simonadomnisoru Dec 3, 2024
fefbdc6
fix: flow errors
simonadomnisoru Dec 3, 2024
a19ed6e
fix: flow errors
simonadomnisoru Dec 3, 2024
d7ee45c
fix: stabilize flacky cypress tests
simonadomnisoru Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 60 additions & 60 deletions i18n/en.pot
Original file line number Diff line number Diff line change
@@ -113,66 +113,6 @@ msgstr "This value is validating"
msgid "Async field update failed"
msgstr "Async field update failed"

msgid "A value is required"
msgstr "A value is required"

msgid "Please provide a valid number"
msgstr "Please provide a valid number"

msgid "Please provide a valid integer"
msgstr "Please provide a valid integer"

msgid "Please provide a positive integer"
msgstr "Please provide a positive integer"

msgid "Please provide zero or a positive integer"
msgstr "Please provide zero or a positive integer"

msgid "Please provide a negative integer"
msgstr "Please provide a negative integer"

msgid "Please provide a valid date"
msgstr "Please provide a valid date"

msgid "A date in the future is not allowed"
msgstr "A date in the future is not allowed"

msgid "Please provide a valid date and time"
msgstr "Please provide a valid date and time"

msgid "Please provide a valid time"
msgstr "Please provide a valid time"

msgid "Please provide an integer between 0 and 100"
msgstr "Please provide an integer between 0 and 100"

msgid "Please provide a valid url"
msgstr "Please provide a valid url"

msgid "Please provide a valid email address"
msgstr "Please provide a valid email address"

msgid "Please provide a valid age"
msgstr "Please provide a valid age"

msgid "Please provide a valid phone number"
msgstr "Please provide a valid phone number"

msgid "Please provide a valid organisation unit"
msgstr "Please provide a valid organisation unit"

msgid "Please provide valid coordinates"
msgstr "Please provide valid coordinates"

msgid "This value already exists"
msgstr "This value already exists"

msgid "\"From\" cannot be greater than \"To\""
msgstr "\"From\" cannot be greater than \"To\""

msgid "Checking..."
msgstr "Checking..."

msgid "Area"
msgstr "Area"

@@ -185,6 +125,12 @@ msgstr "Enrollment"
msgid "Complete event"
msgstr "Complete event"

msgid "A value is required"
msgstr "A value is required"

msgid "Please provide a valid date"
msgstr "Please provide a valid date"

msgid "{{ stageName }} - Basic info"
msgstr "{{ stageName }} - Basic info"

@@ -1198,6 +1144,9 @@ msgstr "Set coordinates"
msgid "Coordinates"
msgstr "Coordinates"

msgid "Please provide valid coordinates"
msgstr "Please provide valid coordinates"

msgid "Delete polygon"
msgstr "Delete polygon"

@@ -1451,6 +1400,9 @@ msgstr "Scheduled date"
msgid "Report date"
msgstr "Report date"

msgid "Please provide a valid organisation unit"
msgstr "Please provide a valid organisation unit"

msgid "Please select a valid event"
msgstr "Please select a valid event"

@@ -1936,6 +1888,54 @@ msgstr "Error editing the event, the changes made were not saved"
msgid "Error updating the Assignee"
msgstr "Error updating the Assignee"

msgid "Please provide a valid number"
msgstr "Please provide a valid number"

msgid "Please provide a valid integer"
msgstr "Please provide a valid integer"

msgid "Please provide a positive integer"
msgstr "Please provide a positive integer"

msgid "Please provide zero or a positive integer"
msgstr "Please provide zero or a positive integer"

msgid "Please provide a negative integer"
msgstr "Please provide a negative integer"

msgid "A date in the future is not allowed"
msgstr "A date in the future is not allowed"

msgid "Please provide a valid date and time"
msgstr "Please provide a valid date and time"

msgid "Please provide a valid time"
msgstr "Please provide a valid time"

msgid "Please provide an integer between 0 and 100"
msgstr "Please provide an integer between 0 and 100"

msgid "Please provide a valid url"
msgstr "Please provide a valid url"

msgid "Please provide a valid email address"
msgstr "Please provide a valid email address"

msgid "Please provide a valid age"
msgstr "Please provide a valid age"

msgid "Please provide a valid phone number"
msgstr "Please provide a valid phone number"

msgid "This value already exists"
msgstr "This value already exists"

msgid "\"From\" cannot be greater than \"To\""
msgstr "\"From\" cannot be greater than \"To\""

msgid "Checking..."
msgstr "Checking..."

msgid "Set coordinate"
msgstr "Set coordinate"

Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import { buildField } from './field/buildField';
import { validationStrategies } from '../../metaData/RenderFoundation/renderFoundation.const';
import type { CustomForm, DataElement } from '../../metaData';
import { messageStateKeys } from '../../reducers/descriptions/rulesEffects.reducerDescription';
import { validatorTypes } from './field/validators/constants';
import { validatorTypes } from '../../utils/validation/constants';
import type { QuerySingleResource } from '../../utils/api/api.types';
import { FormFieldPlugin } from './FormFieldPlugin';
import { FormFieldPluginConfig } from '../../metaData/FormFieldPluginConfig';
Original file line number Diff line number Diff line change
@@ -11,24 +11,18 @@ import isObject from 'd2-utilizr/lib/isObject';
import defaultClasses from './formBuilder.module.css';
import type { ErrorData, PostProcessErrorMessage } from './formbuilder.types';
import type { PluginContext } from '../FormFieldPlugin/FormFieldPlugin.types';
import { getValidators } from '../field/validators';
import { getValidators, validateValue } from '../../../utils/validation';
import type { ValidatorContainer } from '../../../utils/validation';
import type { DataElement } from '../../../metaData';
import type { QuerySingleResource } from '../../../utils/api';

export type ValidatorContainer = {
validator: (value: any, validationContext: ?Object) => boolean | Promise<boolean>,
message: string,
validatingMessage?: ?string,
type?: ?string,
async?: ?boolean,
};

export type FieldConfig = {
id: string,
component: React.ComponentType<any>,
plugin?: boolean,
props: Object,
validators?: ?Array<ValidatorContainer>,
validators?: Array<ValidatorContainer>,
commitEvent?: ?string,
onIsEqual?: ?(newValue: any, oldValue: any) => boolean,
};
@@ -47,7 +41,7 @@ type GetContainerPropsFn = (index: number, fieldsCount: number, field: FieldConf

type FieldCommitConfig = {|
fieldId: string,
validators?: ?Array<ValidatorContainer>,
validators?: Array<ValidatorContainer>,
onIsEqual?: ?(newValue: any, oldValue: any) => boolean,
|}

@@ -99,54 +93,6 @@ type FieldCommitOptions = {
type FieldsValidatingPromiseContainer = { [fieldId: string]: ?{ cancelableValidatingPromise?: ?CancelablePromise<any>, validatingCompleteUid: string } };

export class FormBuilder extends React.Component<Props> {
static async validateField(
{ validators }: { validators?: ?Array<ValidatorContainer> },
value: any,
validationContext: ?Object,
onIsValidatingInternal: ?Function,
): Promise<{ valid: boolean, errorMessage?: ?string, errorType?: ?string }> {
if (!validators || validators.length === 0) {
return {
valid: true,
};
}

const validatorResult = await validators
.reduce(async (passPromise, currentValidator) => {
const pass = await passPromise;
if (pass === true) {
let result = currentValidator.validator(value, validationContext);
if (result instanceof Promise) {
result = onIsValidatingInternal ? onIsValidatingInternal(currentValidator.validatingMessage, result) : result;
result = await result;
}

if (result === true || (result && result.valid)) {
return true;
}
return {
message: (result && result.errorMessage) || currentValidator.message,
type: currentValidator.type,
data: result && result.data,
};
}
return pass;
}, Promise.resolve(true));

if (validatorResult !== true) {
return {
valid: false,
errorMessage: validatorResult.message,
errorType: validatorResult.type,
errorData: validatorResult.data,
};
}

return {
valid: true,
};
}

static getAsyncUIState(fieldsUI: { [id: string]: FieldUI }) {
return Object.keys(fieldsUI).reduce((accAsyncUIState, fieldId) => {
const fieldUI = fieldsUI[fieldId];
@@ -219,8 +165,9 @@ export class FormBuilder extends React.Component<Props> {

let validationData;
try {
validationData = await FormBuilder.validateField(
field,
const { validators } = field;
validationData = await validateValue(
validators,
values[field.id],
validationContext,
handleIsValidatingInternal,
@@ -429,8 +376,8 @@ export class FormBuilder extends React.Component<Props> {
};

this.commitUpdateTriggeredForFields[fieldId] = true;
const updatePromise = FormBuilder.validateField(
{ validators },
const updatePromise = validateValue(
validators,
value,
onGetValidationContext && onGetValidationContext(),
handleIsValidatingInternal,
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @flow
export { FormBuilder } from './FormBuilder.component';
export type { PostProcessErrorMessage, ErrorData } from './formbuilder.types';
export type { FieldConfig, ValidatorContainer } from './FormBuilder.component';
export type { FieldConfig } from './FormBuilder.component';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow
import { type ComponentType } from 'react';
import type { ValidatorContainer } from '../../../FormBuilder';
import { getValidators } from '../../validators';
import type { ValidatorContainer } from '../../../../../utils/validation';
import { getValidators } from '../../../../../utils/validation';
import type { DataElement } from '../../../../../metaData';
import type { QuerySingleResource } from '../../../../../utils/api/api.types';

This file was deleted.

Original file line number Diff line number Diff line change
@@ -419,17 +419,38 @@ export class EnrollmentDataEntryComponent extends React.Component<PreEnrollmentD

handleUpdateField = (...args: Array<any>) => {
const { programId, orgUnit, firstStageMetaData, formFoundation } = this.props;
this.props.onUpdateField(...args, programId, orgUnit, firstStageMetaData?.stage, formFoundation);
}
this.props.onUpdateField(
...args,
programId,
orgUnit,
firstStageMetaData?.stage,
formFoundation,
this.getValidationContext,
);
};

handleUpdateDataEntryField = (...args: Array<any>) => {
const { programId, orgUnit, firstStageMetaData, formFoundation } = this.props;
this.props.onUpdateDataEntryField(...args, programId, orgUnit, firstStageMetaData?.stage, formFoundation);
this.props.onUpdateDataEntryField(
...args,
programId,
orgUnit,
firstStageMetaData?.stage,
formFoundation,
this.getValidationContext,
);
}

handleStartAsyncUpdateField = (...args: Array<any>) => {
const { programId, orgUnit, firstStageMetaData, formFoundation } = this.props;
this.props.onStartAsyncUpdateField(...args, programId, orgUnit, firstStageMetaData?.stage, formFoundation);
this.props.onStartAsyncUpdateField(
...args,
programId,
orgUnit,
firstStageMetaData?.stage,
formFoundation,
this.getValidationContext,
);
}

render() {
Original file line number Diff line number Diff line change
@@ -14,17 +14,21 @@ const mapDispatchToProps = (dispatch: ReduxDispatch) => ({
orgUnit: OrgUnit,
stage?: ProgramStage,
formFoundation: RenderFoundation,
onGetValidationContext: () => Object,
) => {
dispatch(updateDataEntryFieldBatch(innerAction, programId, orgUnit, stage, formFoundation));
dispatch(
updateDataEntryFieldBatch(innerAction, programId, orgUnit, stage, formFoundation, onGetValidationContext),
);
},
onUpdateField: (
innerAction: ReduxAction<any, any>,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
formFoundation: RenderFoundation,
onGetValidationContext: () => Object,
) => {
dispatch(updateFieldBatch(innerAction, programId, orgUnit, stage, formFoundation));
dispatch(updateFieldBatch(innerAction, programId, orgUnit, stage, formFoundation, onGetValidationContext));
},
onStartAsyncUpdateField: (
innerAction: ReduxAction<any, any>,
@@ -34,9 +38,10 @@ const mapDispatchToProps = (dispatch: ReduxDispatch) => ({
orgUnit: OrgUnit,
stage?: ProgramStage,
formFoundation: RenderFoundation,
onGetValidationContext: () => Object,
) => {
const onAsyncUpdateSuccess = (successInnerAction: ReduxAction<any, any>) =>
asyncUpdateSuccessBatch(successInnerAction, dataEntryId, itemId, programId, orgUnit, stage, formFoundation);
asyncUpdateSuccessBatch(successInnerAction, dataEntryId, itemId, programId, orgUnit, stage, formFoundation, onGetValidationContext);
const onAsyncUpdateError = (errorInnerAction: ReduxAction<any, any>) => errorInnerAction;

dispatch(startAsyncUpdateFieldForNewEnrollment(innerAction, onAsyncUpdateSuccess, onAsyncUpdateError));
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow
import { hasValue } from 'capture-core-utils/validators/form';
import i18n from '@dhis2/d2-i18n';
import { isValidDate } from '../../../../../utils/validators/form';
import { isValidDate } from '../../../../../utils/validation/validators/form';

const preValidateDate = (value?: ?string) => {
if (!value) {
Loading