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
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions cypress/e2e/NewPage/NewPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,9 @@ And('you see the enrollment minimap', () => {
});

And('you delete the recently added tracked entity', () => {
cy.get('[data-test="profile-widget"]')
.contains('Person profile')
.should('exist');
cy.get('[data-test="widget-profile-overflow-menu"]')
.click();
cy.contains('Delete Person')
Expand All @@ -646,6 +649,9 @@ And('you delete the recently added tracked entity', () => {
});

And('you delete the recently added malaria entity', () => {
cy.get('[data-test="profile-widget"]')
.contains('Malaria Entity profile')
.should('exist');
cy.get('[data-test="widget-profile-overflow-menu"]')
.click();
cy.contains('Delete Malaria Entity')
Expand Down
124 changes: 62 additions & 62 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-11-07T11:57:59.094Z\n"
"PO-Revision-Date: 2024-11-07T11:57:59.094Z\n"
"POT-Creation-Date: 2024-12-03T10:58:18.077Z\n"
"PO-Revision-Date: 2024-12-03T10:58:18.077Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -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"

Expand All @@ -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"

Expand All @@ -197,6 +143,9 @@ msgstr "{{ stageName }} - Status"
msgid "Please select {{categoryName}}"
msgstr "Please select {{categoryName}}"

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

msgid "Saving a new enrollment in {{programName}} in {{orgUnitName}}."
msgstr "Saving a new enrollment in {{programName}} in {{orgUnitName}}."

Expand Down Expand Up @@ -1195,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"

Expand Down Expand Up @@ -1454,6 +1406,9 @@ msgstr "Report date"
msgid "Please enter a date"
msgstr "Please enter a 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"

Expand Down Expand Up @@ -1930,6 +1885,51 @@ 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 "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 "Please provide a valid positive integer"
msgstr "Please provide a valid positive integer"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const makeMapStateToProps = () => {
const getRulesMessages = makeGetMessages();
const getCompulsory = makeGetCompulsory();
const getDisabled = makeGetDisabled();
const mapStateToProps = (state: Object, props: { formId: string }) => ({
const mapStateToProps = (state: Object, props: { formId: string, fieldsMetaData: any }) => ({
values: getSectionValues(state, props),
rulesHiddenFields: getHiddenFields(state, props),
rulesMessages: getRulesMessages(state, props),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,17 @@ 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 { validatorTypes } from '../field/validators/constants';
import { getValidators, validateValue, validatorTypes } 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, internalError?: ?{
error?: ?string,
errorCode?: ?string,
}) => 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,
};
Expand All @@ -51,7 +40,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,
|}

Expand Down Expand Up @@ -100,7 +89,7 @@ export type FieldCommitOptions = {|
errorCode?: string,
|};

type FieldCommitOptionsExtended = {|
export type FieldCommitOptionsExtended = {|
...FieldCommitOptions,
plugin?: ?boolean,
|};
Expand All @@ -109,59 +98,6 @@ type FieldCommitOptionsExtended = {|
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,
commitOptions?: ?FieldCommitOptions,
): 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,
{ error: commitOptions?.error, errorCode: commitOptions?.errorCode },
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];
Expand Down Expand Up @@ -234,12 +170,12 @@ export class FormBuilder extends React.Component<Props> {

let validationData;
try {
validationData = await FormBuilder.validateField(
field,
values[field.id],
validationData = await validateValue({
validators: field.validators,
value: values[field.id],
validationContext,
handleIsValidatingInternal,
);
postProcessAsyncValidatonInitiation: handleIsValidatingInternal,
});
} catch (reason) {
if (reason && isObject(reason) && reason.isCanceled) {
validationData = null;
Expand Down Expand Up @@ -468,14 +404,13 @@ export class FormBuilder extends React.Component<Props> {
errorMessage: options.error,
errorType: validatorTypes.TYPE_BASE,
errorData: undefined }) :
(await FormBuilder.validateField(
{ validators },
(await validateValue({
validators,
value,
onGetValidationContext && onGetValidationContext(),
handleIsValidatingInternal,
// $FlowFixMe
options,
)
validationContext: onGetValidationContext && onGetValidationContext(),
postProcessAsyncValidatonInitiation: handleIsValidatingInternal,
commitOptions: options,
})
// $FlowFixMe[prop-missing] automated comment
.then(({ valid, errorMessage, errorType, errorData }) => {
updateField({ valid, errorMessage, errorType, errorData });
Expand Down
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, FieldCommitOptionsExtended } 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';

Expand Down

This file was deleted.

1 change: 1 addition & 0 deletions src/core_modules/capture-core/components/D2Form/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow
export { asyncHandlerActionTypes, asyncUpdateFieldEpic } from './asyncHandlerHOC';
export { D2Form } from './D2Form.container';
export type { FieldCommitOptionsExtended } from './FormBuilder';
Loading
Loading