fix: support dataDerivedPaths for revalidation (#27408)
This commit is contained in:
parent
920d454d2b
commit
ab6b3e4e4a
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ export type EvalProps = {
|
|||
[entityName: string]: DataTreeEvaluationProps;
|
||||
};
|
||||
|
||||
export type EvalPathsIdenticalToState = Record<string, string>;
|
||||
export default class DataTreeEvaluator {
|
||||
dependencyMap: DependencyMap = new DependencyMap();
|
||||
sortedDependencies: SortedDependencies = [];
|
||||
|
|
@ -158,7 +159,6 @@ export default class DataTreeEvaluator {
|
|||
*/
|
||||
validationDependencyMap: DependencyMap = new DependencyMap();
|
||||
validationDependencies: Record<string, string[]> = {};
|
||||
sortedValidationDependencies: SortedDependencies = [];
|
||||
inverseValidationDependencies: Record<string, string[]> = {};
|
||||
|
||||
/**
|
||||
|
|
@ -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<string, boolean> = {};
|
||||
|
||||
prevState = {};
|
||||
|
|
@ -188,7 +188,7 @@ export default class DataTreeEvaluator {
|
|||
this.widgetConfigMap = widgetConfigMap;
|
||||
}
|
||||
|
||||
getEvalPathsIdenticalToState(): Record<string, string> {
|
||||
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<DataTree, DataTree>[] =
|
||||
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<string>();
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@ export const updateDependencyMap = ({
|
|||
const dependenciesOfRemovedPaths: Array<string> = [];
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user