diff --git a/app/client/src/ce/workers/Evaluation/evaluationUtils.test.ts b/app/client/src/ce/workers/Evaluation/evaluationUtils.test.ts index 2a8e0c0d17..9b383f3433 100644 --- a/app/client/src/ce/workers/Evaluation/evaluationUtils.test.ts +++ b/app/client/src/ce/workers/Evaluation/evaluationUtils.test.ts @@ -252,10 +252,8 @@ describe("2. privateWidgets", () => { Text3: true, }; - const actualPrivateWidgetsList = getAllPrivateWidgetsInDataTree( - testDataTree, - testConfigTree, - ); + const actualPrivateWidgetsList = + getAllPrivateWidgetsInDataTree(testConfigTree); expect(expectedPrivateWidgetsList).toStrictEqual(actualPrivateWidgetsList); }); @@ -833,7 +831,6 @@ describe("7. Test addErrorToEntityProperty method", () => { } as EvaluationError; addErrorToEntityProperty({ errors: [error], - dataTree: dataTreeEvaluator.evalTree, evalProps: dataTreeEvaluator.evalProps, fullPropertyPath: "Api1.data", configTree: dataTreeEvaluator.oldConfigTree, diff --git a/app/client/src/ce/workers/Evaluation/evaluationUtils.ts b/app/client/src/ce/workers/Evaluation/evaluationUtils.ts index 891249ea07..55eb846804 100644 --- a/app/client/src/ce/workers/Evaluation/evaluationUtils.ts +++ b/app/client/src/ce/workers/Evaluation/evaluationUtils.ts @@ -579,31 +579,25 @@ export function getSafeToRenderDataTree( export const addErrorToEntityProperty = ({ configTree, - dataTree, errors, evalProps, fullPropertyPath, }: { errors: EvaluationError[]; - dataTree: DataTree; fullPropertyPath: string; evalProps: EvalProps; configTree: ConfigTree; }) => { const { entityName, propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath); - const isPrivateEntityPath = getAllPrivateWidgetsInDataTree( - dataTree, - configTree, - )[entityName]; + const isPrivateEntityPath = + getAllPrivateWidgetsInDataTree(configTree)[entityName]; const logBlackList = get(configTree, `${entityName}.logBlackList`, {}); if (propertyPath && !(propertyPath in logBlackList) && !isPrivateEntityPath) { const errorPath = `${entityName}.${EVAL_ERROR_PATH}['${propertyPath}']`; const existingErrors = get(evalProps, errorPath, []) as EvaluationError[]; set(evalProps, errorPath, existingErrors.concat(errors)); } - - return dataTree; }; export const resetValidationErrorsForEntityProperty = ({ @@ -712,15 +706,13 @@ export const isPrivateEntityPath = ( }; export const getAllPrivateWidgetsInDataTree = ( - dataTree: DataTree, configTree: ConfigTree, ): PrivateWidgets => { let privateWidgets: PrivateWidgets = {}; - Object.keys(dataTree).forEach((entityName) => { - const entity = dataTree[entityName]; + Object.keys(configTree).forEach((entityName) => { const entityConfig = configTree[entityName] as WidgetEntityConfig; - if (isWidget(entity) && !_.isEmpty(entityConfig.privateWidgets)) { + if (isWidget(entityConfig) && !_.isEmpty(entityConfig.privateWidgets)) { privateWidgets = { ...privateWidgets, ...entityConfig.privateWidgets }; } }); @@ -732,7 +724,7 @@ export const getDataTreeWithoutPrivateWidgets = ( dataTree: DataTree, configTree: ConfigTree, ): DataTree => { - const privateWidgets = getAllPrivateWidgetsInDataTree(dataTree, configTree); + const privateWidgets = getAllPrivateWidgetsInDataTree(configTree); const privateWidgetNames = Object.keys(privateWidgets); const treeWithoutPrivateWidgets = _.omit(dataTree, privateWidgetNames); return treeWithoutPrivateWidgets; diff --git a/app/client/src/workers/common/DataTreeEvaluator/index.ts b/app/client/src/workers/common/DataTreeEvaluator/index.ts index a87a3fb5da..3627abd4ce 100644 --- a/app/client/src/workers/common/DataTreeEvaluator/index.ts +++ b/app/client/src/workers/common/DataTreeEvaluator/index.ts @@ -129,6 +129,7 @@ export type EvalProps = { [entityName: string]: DataTreeEvaluationProps; }; +export type EvalPathsIdenticalToState = Record; export default class DataTreeEvaluator { dependencyMap: DependencyMap = new DependencyMap(); sortedDependencies: SortedDependencies = []; @@ -158,7 +159,6 @@ export default class DataTreeEvaluator { */ validationDependencyMap: DependencyMap = new DependencyMap(); validationDependencies: Record = {}; - sortedValidationDependencies: SortedDependencies = []; inverseValidationDependencies: Record = {}; /** @@ -167,7 +167,7 @@ export default class DataTreeEvaluator { evalProps: EvalProps = {}; //when attaching values to __evaluations__ segment of the state there are cases where this value is identical to the widget property //in those cases do not it to the dataTree and update this map. The main thread can decompress these updates and we can minimise the data transfer - evalPathsIdenticalToState: any = {}; + evalPathsIdenticalToState: EvalPathsIdenticalToState = {}; undefinedEvalValuesMap: Record = {}; prevState = {}; @@ -188,7 +188,7 @@ export default class DataTreeEvaluator { this.widgetConfigMap = widgetConfigMap; } - getEvalPathsIdenticalToState(): Record { + getEvalPathsIdenticalToState(): EvalPathsIdenticalToState { return this.evalPathsIdenticalToState || {}; } getEvalTree() { @@ -283,9 +283,6 @@ export default class DataTreeEvaluator { const sortDependenciesStartTime = performance.now(); this.sortedDependencies = this.sortDependencies(this.dependencyMap); - this.sortedValidationDependencies = this.sortDependencies( - this.validationDependencyMap, - ); const sortDependenciesEndTime = performance.now(); const secondCloneStartTime = performance.now(); @@ -349,23 +346,24 @@ export default class DataTreeEvaluator { undefined, this.oldConfigTree, ); + const evaluationEndTime = performance.now(); const validationStartTime = performance.now(); - // Validate Widgets - this.setEvalTree( - getValidatedTree( - evaluatedTree, - { - evalProps: this.evalProps, - evalPathsIdenticalToState: this.evalPathsIdenticalToState, - }, - this.oldConfigTree, - ), + const validatedEvalTree = getValidatedTree( + evaluatedTree, + { + evalProps: this.evalProps, + evalPathsIdenticalToState: this.evalPathsIdenticalToState, + pathsValidated: evaluationOrder, + }, + this.oldConfigTree, ); const validationEndTime = performance.now(); + this.setEvalTree(validatedEvalTree); + const timeTakenForEvalAndValidateFirstTree = { evaluation: getFixedTimeDifference( evaluationEndTime, @@ -468,13 +466,13 @@ export default class DataTreeEvaluator { configTree, ); - const oldUnEvalTreeWithStrigifiedJSFunctions = Object.assign( + const oldUnEvalTreeWithStringifiedJSFunctions = Object.assign( {}, this.oldUnEvalTree, stringifiedOldUnEvalTreeJSCollections, ); - const localUnEvalTreeWithStrigifiedJSFunctions = Object.assign( + const localUnEvalTreeWithStringifiedJSFunctions = Object.assign( {}, localUnEvalTree, stringifiedLocalUnEvalTreeJSCollection, @@ -482,8 +480,8 @@ export default class DataTreeEvaluator { const differences: Diff[] = diff( - oldUnEvalTreeWithStrigifiedJSFunctions, - localUnEvalTreeWithStrigifiedJSFunctions, + oldUnEvalTreeWithStringifiedJSFunctions, + localUnEvalTreeWithStringifiedJSFunctions, ) || []; // Since eval tree is listening to possible events that don't cause differences // We want to check if no diffs are present and bail out early @@ -524,7 +522,7 @@ export default class DataTreeEvaluator { const updateDependencyStartTime = performance.now(); // TODO => Optimize using dataTree diff - this.allKeys = getAllPaths(localUnEvalTreeWithStrigifiedJSFunctions); + this.allKeys = getAllPaths(localUnEvalTreeWithStringifiedJSFunctions); // Find all the paths that have changed as part of the difference and update the // global dependency map if an existing dynamic binding has now become legal const { @@ -795,15 +793,14 @@ export default class DataTreeEvaluator { this.setEvalTree(newEvalTree); const reValidateStartTime = performance.now(); - const validationOrder = new Set([ + + const pathsChanged = new Set([ ...evaluationOrder, ...nonDynamicFieldValidationOrder, ]); - const reValidatedPaths = this.reValidateTree( - [...validationOrder], - newEvalTree, - configTree, - ); + const pathsToValidate = this.getPathsToValidate([...pathsChanged]); + this.validateEvalDependentPaths(pathsToValidate); + const reValidateEndTime = performance.now(); const timeTakenForEvalAndValidateSubTree = { @@ -820,7 +817,7 @@ export default class DataTreeEvaluator { return { evalMetaUpdates, staleMetaIds, - reValidatedPaths, + reValidatedPaths: pathsToValidate, }; } @@ -1039,7 +1036,6 @@ export default class DataTreeEvaluator { const parsedValue = validateAndParseWidgetProperty({ fullPropertyPath, widget: widgetEntity, - currentTree, configTree: oldConfigTree, evalPropertyValue, unEvalPropertyValue, @@ -1055,10 +1051,16 @@ export default class DataTreeEvaluator { fullPropertyPath, parsedValue, propertyPath, - evalPropertyValue, isNewWidget, }); + // setting evalPropertyValue in unParsedEvalTree + set( + this.getUnParsedEvalTree(), + fullPropertyPath, + evalPropertyValue, + ); + staleMetaIds = staleMetaIds.concat( getStaleMetaStateIds({ entity: widgetEntity, @@ -1103,7 +1105,7 @@ export default class DataTreeEvaluator { evalPathsIdenticalToState: this.evalPathsIdenticalToState, evalProps: this.evalProps, isParsedValueTheSame: true, - statePath: fullPropertyPath, + fullPropertyPath, value: evalPropertyValue, }); @@ -1148,7 +1150,7 @@ export default class DataTreeEvaluator { evalPathsIdenticalToState: this.evalPathsIdenticalToState, evalProps: this.evalProps, isParsedValueTheSame: true, - statePath: fullPropertyPath, + fullPropertyPath, value: evalValue, }); @@ -1313,7 +1315,6 @@ export default class DataTreeEvaluator { ), evalProps: this.evalProps, fullPropertyPath, - dataTree: data, configTree, }); } @@ -1355,7 +1356,6 @@ export default class DataTreeEvaluator { ], evalProps: this.evalProps, fullPropertyPath, - dataTree: data, configTree, }); } @@ -1439,7 +1439,6 @@ export default class DataTreeEvaluator { currentTree, entity, evalMetaUpdates, - evalPropertyValue, fullPropertyPath, isNewWidget, parsedValue, @@ -1453,7 +1452,6 @@ export default class DataTreeEvaluator { isNewWidget: boolean; parsedValue: unknown; propertyPath: string; - evalPropertyValue: unknown; }) { const overwriteObj = overrideWidgetProperties({ entity, @@ -1471,69 +1469,51 @@ export default class DataTreeEvaluator { } // setting parseValue in dataTree set(currentTree, fullPropertyPath, parsedValue); - // setting evalPropertyValue in unParsedEvalTree - set(this.getUnParsedEvalTree(), fullPropertyPath, evalPropertyValue); } - reValidateWidgetDependentProperty({ - configTree, - currentTree, + validateEntityDependentProperty({ fullPropertyPath, - widget, }: { fullPropertyPath: string; - widget: WidgetEntity; - currentTree: DataTree; - configTree: ConfigTree; - }): string[] { - const reValidatedPaths: string[] = []; - if (this.inverseValidationDependencies[fullPropertyPath]) { - const pathsToRevalidate = - this.inverseValidationDependencies[fullPropertyPath]; + }) { + const evalTree = this.getEvalTree(); + const configTree = this.getConfigTree(); + const { entityName } = getEntityNameAndPropertyPath(fullPropertyPath); + const entity = evalTree[entityName]; - reValidatedPaths.push(...pathsToRevalidate); - - pathsToRevalidate.forEach((fullPath) => { - validateAndParseWidgetProperty({ - fullPropertyPath: fullPath, - widget, - currentTree, - configTree, - // we supply non-transformed evaluated value - evalPropertyValue: get(this.getUnParsedEvalTree(), fullPath), - unEvalPropertyValue: get( - this.oldUnEvalTree, - fullPath, - ) as unknown as string, - evalProps: this.evalProps, - evalPathsIdenticalToState: this.evalPathsIdenticalToState, - }); - }); - } - return reValidatedPaths; + validateAndParseWidgetProperty({ + fullPropertyPath, + widget: entity as WidgetEntity, + configTree, + // we supply non-transformed evaluated value + evalPropertyValue: get(this.getUnParsedEvalTree(), fullPropertyPath), + unEvalPropertyValue: get( + this.oldUnEvalTree, + fullPropertyPath, + ) as unknown as string, + evalProps: this.evalProps, + evalPathsIdenticalToState: this.evalPathsIdenticalToState, + }); } - reValidateTree( - validationOrder: string[], - currentTree: DataTree, - configTree: ConfigTree, - ) { - const reValidatedPaths: string[] = []; - validationOrder.forEach((fullPropertyPath) => { - const { entityName, propertyPath } = - getEntityNameAndPropertyPath(fullPropertyPath); - const entity = currentTree[entityName]; - if (isWidget(entity) && !isPathDynamicTrigger(entity, propertyPath)) { - const pathsValidated = this.reValidateWidgetDependentProperty({ - widget: entity, - fullPropertyPath, - currentTree, - configTree, - }); - reValidatedPaths.push(...pathsValidated); + getPathsToValidate(pathsEvaluated: string[]) { + const pathsToValidate = new Set(); + pathsEvaluated.forEach((fullPropertyPath) => { + if (this.inverseValidationDependencies[fullPropertyPath]) { + const pathsToRevalidate = + this.inverseValidationDependencies[fullPropertyPath]; + pathsToRevalidate.forEach((path) => pathsToValidate.add(path)); } }); - return reValidatedPaths; + return [...pathsToValidate]; + } + + validateEvalDependentPaths(pathsToValidate: string[]) { + pathsToValidate.forEach((fullPropertyPath) => { + this.validateEntityDependentProperty({ + fullPropertyPath, + }); + }); } // validates the user input saved as action property based on a validationConfig @@ -1572,7 +1552,6 @@ export default class DataTreeEvaluator { errors: evalErrors, evalProps: this.evalProps, fullPropertyPath, - dataTree: currentTree, configTree, }); } diff --git a/app/client/src/workers/common/DataTreeEvaluator/validationUtils.ts b/app/client/src/workers/common/DataTreeEvaluator/validationUtils.ts index 4dc2aa1df8..81c3955e2f 100644 --- a/app/client/src/workers/common/DataTreeEvaluator/validationUtils.ts +++ b/app/client/src/workers/common/DataTreeEvaluator/validationUtils.ts @@ -6,7 +6,7 @@ import type { WidgetEntity, WidgetEntityConfig, } from "entities/DataTree/dataTreeFactory"; -import { get, isObject, isUndefined, set } from "lodash"; +import { isObject, isUndefined, set, get } from "lodash"; import type { EvaluationError } from "utils/DynamicBindingUtils"; import { getEvalValuePath, @@ -20,7 +20,7 @@ import { resetValidationErrorsForEntityProperty, } from "@appsmith/workers/Evaluation/evaluationUtils"; import { validate } from "workers/Evaluation/validations"; -import type { EvalProps } from "."; +import type { EvalPathsIdenticalToState, EvalProps } from "."; import type { ValidationResponse } from "constants/WidgetValidation"; const LARGE_COLLECTION_SIZE = 100; @@ -37,14 +37,21 @@ export function setToEvalPathsIdenticalToState({ evalPath, evalPathsIdenticalToState, evalProps, + fullPropertyPath, isParsedValueTheSame, - statePath, value, -}: any) { +}: { + evalPath: string; + evalPathsIdenticalToState: EvalPathsIdenticalToState; + evalProps: EvalProps; + isParsedValueTheSame: boolean; + fullPropertyPath: string; + value: unknown; +}) { const isLargeCollection = getIsLargeCollection(value); if (isParsedValueTheSame && isLargeCollection) { - evalPathsIdenticalToState[evalPath] = statePath; + evalPathsIdenticalToState[evalPath] = fullPropertyPath; } else { delete evalPathsIdenticalToState[evalPath]; @@ -53,7 +60,6 @@ export function setToEvalPathsIdenticalToState({ } export function validateAndParseWidgetProperty({ configTree, - currentTree, evalPathsIdenticalToState, evalPropertyValue, evalProps, @@ -63,12 +69,11 @@ export function validateAndParseWidgetProperty({ }: { fullPropertyPath: string; widget: WidgetEntity; - currentTree: DataTree; configTree: ConfigTree; evalPropertyValue: unknown; unEvalPropertyValue: string; evalProps: EvalProps; - evalPathsIdenticalToState: any; + evalPathsIdenticalToState: EvalPathsIdenticalToState; }): unknown { const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath); if (isPathDynamicTrigger(widget, propertyPath)) { @@ -112,7 +117,6 @@ export function validateAndParseWidgetProperty({ errors: evalErrors, evalProps, fullPropertyPath, - dataTree: currentTree, configTree, }); } @@ -128,7 +132,7 @@ export function validateAndParseWidgetProperty({ evalPathsIdenticalToState, evalProps, isParsedValueTheSame, - statePath: fullPropertyPath, + fullPropertyPath, value: evaluatedValue, }); @@ -162,74 +166,93 @@ export function validateActionProperty( } return validate(config, value, {}, ""); } - +/** + * Validates all the nodes of the tree to make sure all the values are as expected according to the validation config + * + * For example :- If Button.isDisabled is set to false in propertyPane then it would be passed as "false" in unEvalTree and validateTree method makes sure to convert it to boolean. + * @param tree + * @param option + * @param configTree + * @returns + */ export function getValidatedTree( - tree: DataTree, - option: { evalProps: EvalProps; evalPathsIdenticalToState: any }, + dataTree: DataTree, + option: { + evalProps: EvalProps; + evalPathsIdenticalToState: EvalPathsIdenticalToState; + pathsValidated: string[]; + }, configTree: ConfigTree, ) { - const { evalPathsIdenticalToState, evalProps } = option; - return Object.keys(tree).reduce((tree, entityKey: string) => { - const entity = tree[entityKey]; + const { evalPathsIdenticalToState, evalProps, pathsValidated } = option; + for (const [entityName, entity] of Object.entries(dataTree)) { if (!isWidget(entity)) { - return tree; + continue; } - const entityConfig = configTree[entityKey] as WidgetEntityConfig; + const entityConfig = configTree[entityName] as WidgetEntityConfig; - Object.entries(entityConfig.validationPaths).forEach( - ([property, validation]) => { - const value = get(entity, property); - // const value = get(parsedEntity, property); - // Pass it through parse - const { isValid, messages, parsed, transformed } = - validateWidgetProperty(validation, value, entity, property); - set(entity, property, parsed); - const evaluatedValue = isValid - ? parsed - : isUndefined(transformed) - ? value - : transformed; + const validationPathsMap = Object.entries(entityConfig.validationPaths); - const isParsedValueTheSame = parsed === evaluatedValue; - const fullPropertyPath = `${entityKey}.${property}`; - const evalPath = getEvalValuePath(fullPropertyPath, { - isPopulated: false, - fullPath: true, - }); + for (const [propertyPath, validationConfig] of validationPathsMap) { + const fullPropertyPath = `${entityName}.${propertyPath}`; - setToEvalPathsIdenticalToState({ - evalPath, - evalPathsIdenticalToState, - evalProps, - isParsedValueTheSame, - statePath: fullPropertyPath, - value: evaluatedValue, - }); + if (pathsValidated.includes(fullPropertyPath)) continue; - resetValidationErrorsForEntityProperty({ + const value = get(entity, propertyPath); + // Pass it through parse + const { isValid, messages, parsed, transformed } = validateWidgetProperty( + validationConfig, + value, + entity, + propertyPath, + ); + + set(entity, propertyPath, parsed); + + const evaluatedValue = isValid + ? parsed + : isUndefined(transformed) + ? value + : transformed; + + const isParsedValueTheSame = parsed === evaluatedValue; + + const evalPath = getEvalValuePath(fullPropertyPath, { + isPopulated: false, + fullPath: true, + }); + + setToEvalPathsIdenticalToState({ + evalPath, + evalPathsIdenticalToState, + evalProps, + isParsedValueTheSame, + fullPropertyPath, + value: evaluatedValue, + }); + + resetValidationErrorsForEntityProperty({ + evalProps, + fullPropertyPath, + }); + + if (!isValid) { + const evalErrors: EvaluationError[] = + messages?.map((message) => ({ + errorType: PropertyEvaluationErrorType.VALIDATION, + errorMessage: message, + severity: Severity.ERROR, + raw: value, + })) ?? []; + + addErrorToEntityProperty({ + errors: evalErrors, evalProps, fullPropertyPath, + configTree, }); - - if (!isValid) { - const evalErrors: EvaluationError[] = - messages?.map((message) => ({ - errorType: PropertyEvaluationErrorType.VALIDATION, - errorMessage: message, - severity: Severity.ERROR, - raw: value, - })) ?? []; - - addErrorToEntityProperty({ - errors: evalErrors, - evalProps, - fullPropertyPath, - dataTree: tree, - configTree, - }); - } - }, - ); - return { ...tree, [entityKey]: entity }; - }, tree); + } + } + } + return dataTree; } diff --git a/app/client/src/workers/common/DependencyMap/index.ts b/app/client/src/workers/common/DependencyMap/index.ts index 4ea60b8914..439bc1eb62 100644 --- a/app/client/src/workers/common/DependencyMap/index.ts +++ b/app/client/src/workers/common/DependencyMap/index.ts @@ -119,7 +119,6 @@ export const updateDependencyMap = ({ const dependenciesOfRemovedPaths: Array = []; const removedPaths: Array<{ entityId: string; fullpath: string }> = []; let didUpdateDependencyMap = false; - let didUpdateValidationDependencyMap = false; const { allKeys, dependencyMap, @@ -161,10 +160,10 @@ export const updateDependencyMap = ({ } const didUpdateDep = dependencyMap.addNodes(allAddedPaths, false); - const didUpdateValidationDep = - validationDependencyMap.addNodes(allAddedPaths); + validationDependencyMap.addNodes(allAddedPaths); + if (didUpdateDep) didUpdateDependencyMap = true; - if (didUpdateValidationDep) didUpdateValidationDependencyMap = true; + if (isWidgetActionOrJsObject(entity)) { if (!isDynamicLeaf(unEvalDataTree, fullPropertyPath, configTree)) { const entityDependencyMap = getEntityDependencies( @@ -200,7 +199,6 @@ export const updateDependencyMap = ({ path, validationDependencies[path], ); - didUpdateValidationDependencyMap = true; } } } else { @@ -229,7 +227,6 @@ export const updateDependencyMap = ({ path, validationDependencies[path], ); - didUpdateValidationDependencyMap = true; } } } @@ -255,10 +252,9 @@ export const updateDependencyMap = ({ } const didUpdateDeps = dependencyMap.removeNodes(allDeletedPaths); - const didUpdateValidationDeps = - validationDependencyMap.removeNodes(allDeletedPaths); + validationDependencyMap.removeNodes(allDeletedPaths); + if (didUpdateDeps) didUpdateDependencyMap = true; - if (didUpdateValidationDeps) didUpdateValidationDependencyMap = true; if (isWidgetActionOrJsObject(entity)) { const entityId = getEntityId(entity); @@ -314,10 +310,6 @@ export const updateDependencyMap = ({ translatedDiffs, ); } - if (didUpdateValidationDependencyMap) { - dataTreeEvalRef.sortedValidationDependencies = - dataTreeEvalRef.sortDependencies(validationDependencyMap); - } /** We need this in order clear out the paths that could have errors when a property is deleted */ if (removedPaths.length) { diff --git a/app/client/src/workers/common/DependencyMap/utils.ts b/app/client/src/workers/common/DependencyMap/utils.ts index 33335dfbd9..ae380d2825 100644 --- a/app/client/src/workers/common/DependencyMap/utils.ts +++ b/app/client/src/workers/common/DependencyMap/utils.ts @@ -1,5 +1,5 @@ import { find, toPath, union } from "lodash"; -import type { EvalError, DependencyMap } from "utils/DynamicBindingUtils"; +import type { EvalError } from "utils/DynamicBindingUtils"; import { EvalErrorTypes } from "utils/DynamicBindingUtils"; import { extractIdentifierInfoFromCode } from "@shared/ast"; import { @@ -12,7 +12,6 @@ import { import type { ConfigTree, DataTreeEntity, - WidgetEntity, WidgetEntityConfig, } from "entities/DataTree/dataTreeFactory"; import { @@ -111,30 +110,6 @@ export const extractInfoFromBindings = ( ); }; -export function listValidationDependencies( - entity: WidgetEntity, - entityName: string, - entityConfig: WidgetEntityConfig, -): DependencyMap { - const validationDependency: DependencyMap = {}; - if (isWidget(entity)) { - const { validationPaths } = entityConfig; - - Object.entries(validationPaths).forEach( - ([propertyPath, validationConfig]) => { - if (validationConfig.dependentPaths) { - const dependencyArray = validationConfig.dependentPaths.map( - (path) => `${entityName}.${path}`, - ); - validationDependency[`${entityName}.${propertyPath}`] = - dependencyArray; - } - }, - ); - } - return validationDependency; -} - /**This function returns a unique array containing a merge of both arrays * @param currentArr * @param updateArr diff --git a/app/client/src/workers/common/DependencyMap/utils/getValidationDependencies.ts b/app/client/src/workers/common/DependencyMap/utils/getValidationDependencies.ts index 690f5872d3..dcb7efe064 100644 --- a/app/client/src/workers/common/DependencyMap/utils/getValidationDependencies.ts +++ b/app/client/src/workers/common/DependencyMap/utils/getValidationDependencies.ts @@ -6,6 +6,8 @@ import type { } from "entities/DataTree/dataTreeFactory"; import type { DependencyMap } from "utils/DynamicBindingUtils"; +const DATA_DERIVED_PROPERTY_PLACEHOLDER = "*"; + export function getValidationDependencies( entity: DataTreeEntity, entityName: string, @@ -19,9 +21,29 @@ export function getValidationDependencies( Object.entries(validationPaths).forEach( ([propertyPath, validationConfig]) => { if (validationConfig.dependentPaths) { - const dependencyArray = validationConfig.dependentPaths.map( - (path) => `${entityName}.${path}`, - ); + const propertyPathsSplitArray = propertyPath.split("."); + const dependencyArray = validationConfig.dependentPaths.map((path) => { + const pathSplitArray = path.split("."); + /** + * Below logic add support for data derived paths in validation dependencies + * dependentPaths: ["primaryColumns.*.computedValue"] + * + * Here, items.*.value is a data derived path and we need to replace * with the actual value resulting in "primaryColumns.columnName.computedValue" as dependency. + */ + const index = pathSplitArray.indexOf( + DATA_DERIVED_PROPERTY_PLACEHOLDER, + ); + if (index > -1) { + // replace * in pathSplitArray with same position value in propertyPathsSplitArray + for (let i = 0; i < pathSplitArray.length; i++) { + if (pathSplitArray[i] === DATA_DERIVED_PROPERTY_PLACEHOLDER) { + pathSplitArray[i] = propertyPathsSplitArray[i]; + } + } + return `${entityName}.${pathSplitArray.join(".")}`; + } + return `${entityName}.${path}`; + }); validationDependency[`${entityName}.${propertyPath}`] = dependencyArray; } },