fix: support dataDerivedPaths for revalidation (#27408)
This commit is contained in:
parent
920d454d2b
commit
ab6b3e4e4a
|
|
@ -252,10 +252,8 @@ describe("2. privateWidgets", () => {
|
||||||
Text3: true,
|
Text3: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const actualPrivateWidgetsList = getAllPrivateWidgetsInDataTree(
|
const actualPrivateWidgetsList =
|
||||||
testDataTree,
|
getAllPrivateWidgetsInDataTree(testConfigTree);
|
||||||
testConfigTree,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(expectedPrivateWidgetsList).toStrictEqual(actualPrivateWidgetsList);
|
expect(expectedPrivateWidgetsList).toStrictEqual(actualPrivateWidgetsList);
|
||||||
});
|
});
|
||||||
|
|
@ -833,7 +831,6 @@ describe("7. Test addErrorToEntityProperty method", () => {
|
||||||
} as EvaluationError;
|
} as EvaluationError;
|
||||||
addErrorToEntityProperty({
|
addErrorToEntityProperty({
|
||||||
errors: [error],
|
errors: [error],
|
||||||
dataTree: dataTreeEvaluator.evalTree,
|
|
||||||
evalProps: dataTreeEvaluator.evalProps,
|
evalProps: dataTreeEvaluator.evalProps,
|
||||||
fullPropertyPath: "Api1.data",
|
fullPropertyPath: "Api1.data",
|
||||||
configTree: dataTreeEvaluator.oldConfigTree,
|
configTree: dataTreeEvaluator.oldConfigTree,
|
||||||
|
|
|
||||||
|
|
@ -579,31 +579,25 @@ export function getSafeToRenderDataTree(
|
||||||
|
|
||||||
export const addErrorToEntityProperty = ({
|
export const addErrorToEntityProperty = ({
|
||||||
configTree,
|
configTree,
|
||||||
dataTree,
|
|
||||||
errors,
|
errors,
|
||||||
evalProps,
|
evalProps,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
}: {
|
}: {
|
||||||
errors: EvaluationError[];
|
errors: EvaluationError[];
|
||||||
dataTree: DataTree;
|
|
||||||
fullPropertyPath: string;
|
fullPropertyPath: string;
|
||||||
evalProps: EvalProps;
|
evalProps: EvalProps;
|
||||||
configTree: ConfigTree;
|
configTree: ConfigTree;
|
||||||
}) => {
|
}) => {
|
||||||
const { entityName, propertyPath } =
|
const { entityName, propertyPath } =
|
||||||
getEntityNameAndPropertyPath(fullPropertyPath);
|
getEntityNameAndPropertyPath(fullPropertyPath);
|
||||||
const isPrivateEntityPath = getAllPrivateWidgetsInDataTree(
|
const isPrivateEntityPath =
|
||||||
dataTree,
|
getAllPrivateWidgetsInDataTree(configTree)[entityName];
|
||||||
configTree,
|
|
||||||
)[entityName];
|
|
||||||
const logBlackList = get(configTree, `${entityName}.logBlackList`, {});
|
const logBlackList = get(configTree, `${entityName}.logBlackList`, {});
|
||||||
if (propertyPath && !(propertyPath in logBlackList) && !isPrivateEntityPath) {
|
if (propertyPath && !(propertyPath in logBlackList) && !isPrivateEntityPath) {
|
||||||
const errorPath = `${entityName}.${EVAL_ERROR_PATH}['${propertyPath}']`;
|
const errorPath = `${entityName}.${EVAL_ERROR_PATH}['${propertyPath}']`;
|
||||||
const existingErrors = get(evalProps, errorPath, []) as EvaluationError[];
|
const existingErrors = get(evalProps, errorPath, []) as EvaluationError[];
|
||||||
set(evalProps, errorPath, existingErrors.concat(errors));
|
set(evalProps, errorPath, existingErrors.concat(errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataTree;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetValidationErrorsForEntityProperty = ({
|
export const resetValidationErrorsForEntityProperty = ({
|
||||||
|
|
@ -712,15 +706,13 @@ export const isPrivateEntityPath = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllPrivateWidgetsInDataTree = (
|
export const getAllPrivateWidgetsInDataTree = (
|
||||||
dataTree: DataTree,
|
|
||||||
configTree: ConfigTree,
|
configTree: ConfigTree,
|
||||||
): PrivateWidgets => {
|
): PrivateWidgets => {
|
||||||
let privateWidgets: PrivateWidgets = {};
|
let privateWidgets: PrivateWidgets = {};
|
||||||
|
|
||||||
Object.keys(dataTree).forEach((entityName) => {
|
Object.keys(configTree).forEach((entityName) => {
|
||||||
const entity = dataTree[entityName];
|
|
||||||
const entityConfig = configTree[entityName] as WidgetEntityConfig;
|
const entityConfig = configTree[entityName] as WidgetEntityConfig;
|
||||||
if (isWidget(entity) && !_.isEmpty(entityConfig.privateWidgets)) {
|
if (isWidget(entityConfig) && !_.isEmpty(entityConfig.privateWidgets)) {
|
||||||
privateWidgets = { ...privateWidgets, ...entityConfig.privateWidgets };
|
privateWidgets = { ...privateWidgets, ...entityConfig.privateWidgets };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -732,7 +724,7 @@ export const getDataTreeWithoutPrivateWidgets = (
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
configTree: ConfigTree,
|
configTree: ConfigTree,
|
||||||
): DataTree => {
|
): DataTree => {
|
||||||
const privateWidgets = getAllPrivateWidgetsInDataTree(dataTree, configTree);
|
const privateWidgets = getAllPrivateWidgetsInDataTree(configTree);
|
||||||
const privateWidgetNames = Object.keys(privateWidgets);
|
const privateWidgetNames = Object.keys(privateWidgets);
|
||||||
const treeWithoutPrivateWidgets = _.omit(dataTree, privateWidgetNames);
|
const treeWithoutPrivateWidgets = _.omit(dataTree, privateWidgetNames);
|
||||||
return treeWithoutPrivateWidgets;
|
return treeWithoutPrivateWidgets;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ export type EvalProps = {
|
||||||
[entityName: string]: DataTreeEvaluationProps;
|
[entityName: string]: DataTreeEvaluationProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EvalPathsIdenticalToState = Record<string, string>;
|
||||||
export default class DataTreeEvaluator {
|
export default class DataTreeEvaluator {
|
||||||
dependencyMap: DependencyMap = new DependencyMap();
|
dependencyMap: DependencyMap = new DependencyMap();
|
||||||
sortedDependencies: SortedDependencies = [];
|
sortedDependencies: SortedDependencies = [];
|
||||||
|
|
@ -158,7 +159,6 @@ export default class DataTreeEvaluator {
|
||||||
*/
|
*/
|
||||||
validationDependencyMap: DependencyMap = new DependencyMap();
|
validationDependencyMap: DependencyMap = new DependencyMap();
|
||||||
validationDependencies: Record<string, string[]> = {};
|
validationDependencies: Record<string, string[]> = {};
|
||||||
sortedValidationDependencies: SortedDependencies = [];
|
|
||||||
inverseValidationDependencies: Record<string, string[]> = {};
|
inverseValidationDependencies: Record<string, string[]> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -167,7 +167,7 @@ export default class DataTreeEvaluator {
|
||||||
evalProps: EvalProps = {};
|
evalProps: EvalProps = {};
|
||||||
//when attaching values to __evaluations__ segment of the state there are cases where this value is identical to the widget property
|
//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
|
//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> = {};
|
undefinedEvalValuesMap: Record<string, boolean> = {};
|
||||||
|
|
||||||
prevState = {};
|
prevState = {};
|
||||||
|
|
@ -188,7 +188,7 @@ export default class DataTreeEvaluator {
|
||||||
this.widgetConfigMap = widgetConfigMap;
|
this.widgetConfigMap = widgetConfigMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEvalPathsIdenticalToState(): Record<string, string> {
|
getEvalPathsIdenticalToState(): EvalPathsIdenticalToState {
|
||||||
return this.evalPathsIdenticalToState || {};
|
return this.evalPathsIdenticalToState || {};
|
||||||
}
|
}
|
||||||
getEvalTree() {
|
getEvalTree() {
|
||||||
|
|
@ -283,9 +283,6 @@ export default class DataTreeEvaluator {
|
||||||
|
|
||||||
const sortDependenciesStartTime = performance.now();
|
const sortDependenciesStartTime = performance.now();
|
||||||
this.sortedDependencies = this.sortDependencies(this.dependencyMap);
|
this.sortedDependencies = this.sortDependencies(this.dependencyMap);
|
||||||
this.sortedValidationDependencies = this.sortDependencies(
|
|
||||||
this.validationDependencyMap,
|
|
||||||
);
|
|
||||||
const sortDependenciesEndTime = performance.now();
|
const sortDependenciesEndTime = performance.now();
|
||||||
|
|
||||||
const secondCloneStartTime = performance.now();
|
const secondCloneStartTime = performance.now();
|
||||||
|
|
@ -349,23 +346,24 @@ export default class DataTreeEvaluator {
|
||||||
undefined,
|
undefined,
|
||||||
this.oldConfigTree,
|
this.oldConfigTree,
|
||||||
);
|
);
|
||||||
|
|
||||||
const evaluationEndTime = performance.now();
|
const evaluationEndTime = performance.now();
|
||||||
const validationStartTime = performance.now();
|
const validationStartTime = performance.now();
|
||||||
// Validate Widgets
|
|
||||||
|
|
||||||
this.setEvalTree(
|
const validatedEvalTree = getValidatedTree(
|
||||||
getValidatedTree(
|
evaluatedTree,
|
||||||
evaluatedTree,
|
{
|
||||||
{
|
evalProps: this.evalProps,
|
||||||
evalProps: this.evalProps,
|
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
pathsValidated: evaluationOrder,
|
||||||
},
|
},
|
||||||
this.oldConfigTree,
|
this.oldConfigTree,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const validationEndTime = performance.now();
|
const validationEndTime = performance.now();
|
||||||
|
|
||||||
|
this.setEvalTree(validatedEvalTree);
|
||||||
|
|
||||||
const timeTakenForEvalAndValidateFirstTree = {
|
const timeTakenForEvalAndValidateFirstTree = {
|
||||||
evaluation: getFixedTimeDifference(
|
evaluation: getFixedTimeDifference(
|
||||||
evaluationEndTime,
|
evaluationEndTime,
|
||||||
|
|
@ -468,13 +466,13 @@ export default class DataTreeEvaluator {
|
||||||
configTree,
|
configTree,
|
||||||
);
|
);
|
||||||
|
|
||||||
const oldUnEvalTreeWithStrigifiedJSFunctions = Object.assign(
|
const oldUnEvalTreeWithStringifiedJSFunctions = Object.assign(
|
||||||
{},
|
{},
|
||||||
this.oldUnEvalTree,
|
this.oldUnEvalTree,
|
||||||
stringifiedOldUnEvalTreeJSCollections,
|
stringifiedOldUnEvalTreeJSCollections,
|
||||||
);
|
);
|
||||||
|
|
||||||
const localUnEvalTreeWithStrigifiedJSFunctions = Object.assign(
|
const localUnEvalTreeWithStringifiedJSFunctions = Object.assign(
|
||||||
{},
|
{},
|
||||||
localUnEvalTree,
|
localUnEvalTree,
|
||||||
stringifiedLocalUnEvalTreeJSCollection,
|
stringifiedLocalUnEvalTreeJSCollection,
|
||||||
|
|
@ -482,8 +480,8 @@ export default class DataTreeEvaluator {
|
||||||
|
|
||||||
const differences: Diff<DataTree, DataTree>[] =
|
const differences: Diff<DataTree, DataTree>[] =
|
||||||
diff(
|
diff(
|
||||||
oldUnEvalTreeWithStrigifiedJSFunctions,
|
oldUnEvalTreeWithStringifiedJSFunctions,
|
||||||
localUnEvalTreeWithStrigifiedJSFunctions,
|
localUnEvalTreeWithStringifiedJSFunctions,
|
||||||
) || [];
|
) || [];
|
||||||
// Since eval tree is listening to possible events that don't cause differences
|
// 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
|
// 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();
|
const updateDependencyStartTime = performance.now();
|
||||||
// TODO => Optimize using dataTree diff
|
// 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
|
// 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
|
// global dependency map if an existing dynamic binding has now become legal
|
||||||
const {
|
const {
|
||||||
|
|
@ -795,15 +793,14 @@ export default class DataTreeEvaluator {
|
||||||
this.setEvalTree(newEvalTree);
|
this.setEvalTree(newEvalTree);
|
||||||
|
|
||||||
const reValidateStartTime = performance.now();
|
const reValidateStartTime = performance.now();
|
||||||
const validationOrder = new Set([
|
|
||||||
|
const pathsChanged = new Set([
|
||||||
...evaluationOrder,
|
...evaluationOrder,
|
||||||
...nonDynamicFieldValidationOrder,
|
...nonDynamicFieldValidationOrder,
|
||||||
]);
|
]);
|
||||||
const reValidatedPaths = this.reValidateTree(
|
const pathsToValidate = this.getPathsToValidate([...pathsChanged]);
|
||||||
[...validationOrder],
|
this.validateEvalDependentPaths(pathsToValidate);
|
||||||
newEvalTree,
|
|
||||||
configTree,
|
|
||||||
);
|
|
||||||
const reValidateEndTime = performance.now();
|
const reValidateEndTime = performance.now();
|
||||||
|
|
||||||
const timeTakenForEvalAndValidateSubTree = {
|
const timeTakenForEvalAndValidateSubTree = {
|
||||||
|
|
@ -820,7 +817,7 @@ export default class DataTreeEvaluator {
|
||||||
return {
|
return {
|
||||||
evalMetaUpdates,
|
evalMetaUpdates,
|
||||||
staleMetaIds,
|
staleMetaIds,
|
||||||
reValidatedPaths,
|
reValidatedPaths: pathsToValidate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1039,7 +1036,6 @@ export default class DataTreeEvaluator {
|
||||||
const parsedValue = validateAndParseWidgetProperty({
|
const parsedValue = validateAndParseWidgetProperty({
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
widget: widgetEntity,
|
widget: widgetEntity,
|
||||||
currentTree,
|
|
||||||
configTree: oldConfigTree,
|
configTree: oldConfigTree,
|
||||||
evalPropertyValue,
|
evalPropertyValue,
|
||||||
unEvalPropertyValue,
|
unEvalPropertyValue,
|
||||||
|
|
@ -1055,10 +1051,16 @@ export default class DataTreeEvaluator {
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
parsedValue,
|
parsedValue,
|
||||||
propertyPath,
|
propertyPath,
|
||||||
evalPropertyValue,
|
|
||||||
isNewWidget,
|
isNewWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// setting evalPropertyValue in unParsedEvalTree
|
||||||
|
set(
|
||||||
|
this.getUnParsedEvalTree(),
|
||||||
|
fullPropertyPath,
|
||||||
|
evalPropertyValue,
|
||||||
|
);
|
||||||
|
|
||||||
staleMetaIds = staleMetaIds.concat(
|
staleMetaIds = staleMetaIds.concat(
|
||||||
getStaleMetaStateIds({
|
getStaleMetaStateIds({
|
||||||
entity: widgetEntity,
|
entity: widgetEntity,
|
||||||
|
|
@ -1103,7 +1105,7 @@ export default class DataTreeEvaluator {
|
||||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||||
evalProps: this.evalProps,
|
evalProps: this.evalProps,
|
||||||
isParsedValueTheSame: true,
|
isParsedValueTheSame: true,
|
||||||
statePath: fullPropertyPath,
|
fullPropertyPath,
|
||||||
value: evalPropertyValue,
|
value: evalPropertyValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1148,7 +1150,7 @@ export default class DataTreeEvaluator {
|
||||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||||
evalProps: this.evalProps,
|
evalProps: this.evalProps,
|
||||||
isParsedValueTheSame: true,
|
isParsedValueTheSame: true,
|
||||||
statePath: fullPropertyPath,
|
fullPropertyPath,
|
||||||
value: evalValue,
|
value: evalValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1313,7 +1315,6 @@ export default class DataTreeEvaluator {
|
||||||
),
|
),
|
||||||
evalProps: this.evalProps,
|
evalProps: this.evalProps,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
dataTree: data,
|
|
||||||
configTree,
|
configTree,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1355,7 +1356,6 @@ export default class DataTreeEvaluator {
|
||||||
],
|
],
|
||||||
evalProps: this.evalProps,
|
evalProps: this.evalProps,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
dataTree: data,
|
|
||||||
configTree,
|
configTree,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1439,7 +1439,6 @@ export default class DataTreeEvaluator {
|
||||||
currentTree,
|
currentTree,
|
||||||
entity,
|
entity,
|
||||||
evalMetaUpdates,
|
evalMetaUpdates,
|
||||||
evalPropertyValue,
|
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
isNewWidget,
|
isNewWidget,
|
||||||
parsedValue,
|
parsedValue,
|
||||||
|
|
@ -1453,7 +1452,6 @@ export default class DataTreeEvaluator {
|
||||||
isNewWidget: boolean;
|
isNewWidget: boolean;
|
||||||
parsedValue: unknown;
|
parsedValue: unknown;
|
||||||
propertyPath: string;
|
propertyPath: string;
|
||||||
evalPropertyValue: unknown;
|
|
||||||
}) {
|
}) {
|
||||||
const overwriteObj = overrideWidgetProperties({
|
const overwriteObj = overrideWidgetProperties({
|
||||||
entity,
|
entity,
|
||||||
|
|
@ -1471,69 +1469,51 @@ export default class DataTreeEvaluator {
|
||||||
}
|
}
|
||||||
// setting parseValue in dataTree
|
// setting parseValue in dataTree
|
||||||
set(currentTree, fullPropertyPath, parsedValue);
|
set(currentTree, fullPropertyPath, parsedValue);
|
||||||
// setting evalPropertyValue in unParsedEvalTree
|
|
||||||
set(this.getUnParsedEvalTree(), fullPropertyPath, evalPropertyValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reValidateWidgetDependentProperty({
|
validateEntityDependentProperty({
|
||||||
configTree,
|
|
||||||
currentTree,
|
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
widget,
|
|
||||||
}: {
|
}: {
|
||||||
fullPropertyPath: string;
|
fullPropertyPath: string;
|
||||||
widget: WidgetEntity;
|
}) {
|
||||||
currentTree: DataTree;
|
const evalTree = this.getEvalTree();
|
||||||
configTree: ConfigTree;
|
const configTree = this.getConfigTree();
|
||||||
}): string[] {
|
const { entityName } = getEntityNameAndPropertyPath(fullPropertyPath);
|
||||||
const reValidatedPaths: string[] = [];
|
const entity = evalTree[entityName];
|
||||||
if (this.inverseValidationDependencies[fullPropertyPath]) {
|
|
||||||
const pathsToRevalidate =
|
|
||||||
this.inverseValidationDependencies[fullPropertyPath];
|
|
||||||
|
|
||||||
reValidatedPaths.push(...pathsToRevalidate);
|
validateAndParseWidgetProperty({
|
||||||
|
fullPropertyPath,
|
||||||
pathsToRevalidate.forEach((fullPath) => {
|
widget: entity as WidgetEntity,
|
||||||
validateAndParseWidgetProperty({
|
configTree,
|
||||||
fullPropertyPath: fullPath,
|
// we supply non-transformed evaluated value
|
||||||
widget,
|
evalPropertyValue: get(this.getUnParsedEvalTree(), fullPropertyPath),
|
||||||
currentTree,
|
unEvalPropertyValue: get(
|
||||||
configTree,
|
this.oldUnEvalTree,
|
||||||
// we supply non-transformed evaluated value
|
fullPropertyPath,
|
||||||
evalPropertyValue: get(this.getUnParsedEvalTree(), fullPath),
|
) as unknown as string,
|
||||||
unEvalPropertyValue: get(
|
evalProps: this.evalProps,
|
||||||
this.oldUnEvalTree,
|
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||||
fullPath,
|
});
|
||||||
) as unknown as string,
|
|
||||||
evalProps: this.evalProps,
|
|
||||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return reValidatedPaths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reValidateTree(
|
getPathsToValidate(pathsEvaluated: string[]) {
|
||||||
validationOrder: string[],
|
const pathsToValidate = new Set<string>();
|
||||||
currentTree: DataTree,
|
pathsEvaluated.forEach((fullPropertyPath) => {
|
||||||
configTree: ConfigTree,
|
if (this.inverseValidationDependencies[fullPropertyPath]) {
|
||||||
) {
|
const pathsToRevalidate =
|
||||||
const reValidatedPaths: string[] = [];
|
this.inverseValidationDependencies[fullPropertyPath];
|
||||||
validationOrder.forEach((fullPropertyPath) => {
|
pathsToRevalidate.forEach((path) => pathsToValidate.add(path));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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
|
// validates the user input saved as action property based on a validationConfig
|
||||||
|
|
@ -1572,7 +1552,6 @@ export default class DataTreeEvaluator {
|
||||||
errors: evalErrors,
|
errors: evalErrors,
|
||||||
evalProps: this.evalProps,
|
evalProps: this.evalProps,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
dataTree: currentTree,
|
|
||||||
configTree,
|
configTree,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import type {
|
||||||
WidgetEntity,
|
WidgetEntity,
|
||||||
WidgetEntityConfig,
|
WidgetEntityConfig,
|
||||||
} from "entities/DataTree/dataTreeFactory";
|
} 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 type { EvaluationError } from "utils/DynamicBindingUtils";
|
||||||
import {
|
import {
|
||||||
getEvalValuePath,
|
getEvalValuePath,
|
||||||
|
|
@ -20,7 +20,7 @@ import {
|
||||||
resetValidationErrorsForEntityProperty,
|
resetValidationErrorsForEntityProperty,
|
||||||
} from "@appsmith/workers/Evaluation/evaluationUtils";
|
} from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||||
import { validate } from "workers/Evaluation/validations";
|
import { validate } from "workers/Evaluation/validations";
|
||||||
import type { EvalProps } from ".";
|
import type { EvalPathsIdenticalToState, EvalProps } from ".";
|
||||||
import type { ValidationResponse } from "constants/WidgetValidation";
|
import type { ValidationResponse } from "constants/WidgetValidation";
|
||||||
|
|
||||||
const LARGE_COLLECTION_SIZE = 100;
|
const LARGE_COLLECTION_SIZE = 100;
|
||||||
|
|
@ -37,14 +37,21 @@ export function setToEvalPathsIdenticalToState({
|
||||||
evalPath,
|
evalPath,
|
||||||
evalPathsIdenticalToState,
|
evalPathsIdenticalToState,
|
||||||
evalProps,
|
evalProps,
|
||||||
|
fullPropertyPath,
|
||||||
isParsedValueTheSame,
|
isParsedValueTheSame,
|
||||||
statePath,
|
|
||||||
value,
|
value,
|
||||||
}: any) {
|
}: {
|
||||||
|
evalPath: string;
|
||||||
|
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||||
|
evalProps: EvalProps;
|
||||||
|
isParsedValueTheSame: boolean;
|
||||||
|
fullPropertyPath: string;
|
||||||
|
value: unknown;
|
||||||
|
}) {
|
||||||
const isLargeCollection = getIsLargeCollection(value);
|
const isLargeCollection = getIsLargeCollection(value);
|
||||||
|
|
||||||
if (isParsedValueTheSame && isLargeCollection) {
|
if (isParsedValueTheSame && isLargeCollection) {
|
||||||
evalPathsIdenticalToState[evalPath] = statePath;
|
evalPathsIdenticalToState[evalPath] = fullPropertyPath;
|
||||||
} else {
|
} else {
|
||||||
delete evalPathsIdenticalToState[evalPath];
|
delete evalPathsIdenticalToState[evalPath];
|
||||||
|
|
||||||
|
|
@ -53,7 +60,6 @@ export function setToEvalPathsIdenticalToState({
|
||||||
}
|
}
|
||||||
export function validateAndParseWidgetProperty({
|
export function validateAndParseWidgetProperty({
|
||||||
configTree,
|
configTree,
|
||||||
currentTree,
|
|
||||||
evalPathsIdenticalToState,
|
evalPathsIdenticalToState,
|
||||||
evalPropertyValue,
|
evalPropertyValue,
|
||||||
evalProps,
|
evalProps,
|
||||||
|
|
@ -63,12 +69,11 @@ export function validateAndParseWidgetProperty({
|
||||||
}: {
|
}: {
|
||||||
fullPropertyPath: string;
|
fullPropertyPath: string;
|
||||||
widget: WidgetEntity;
|
widget: WidgetEntity;
|
||||||
currentTree: DataTree;
|
|
||||||
configTree: ConfigTree;
|
configTree: ConfigTree;
|
||||||
evalPropertyValue: unknown;
|
evalPropertyValue: unknown;
|
||||||
unEvalPropertyValue: string;
|
unEvalPropertyValue: string;
|
||||||
evalProps: EvalProps;
|
evalProps: EvalProps;
|
||||||
evalPathsIdenticalToState: any;
|
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||||
}): unknown {
|
}): unknown {
|
||||||
const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath);
|
const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath);
|
||||||
if (isPathDynamicTrigger(widget, propertyPath)) {
|
if (isPathDynamicTrigger(widget, propertyPath)) {
|
||||||
|
|
@ -112,7 +117,6 @@ export function validateAndParseWidgetProperty({
|
||||||
errors: evalErrors,
|
errors: evalErrors,
|
||||||
evalProps,
|
evalProps,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
dataTree: currentTree,
|
|
||||||
configTree,
|
configTree,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +132,7 @@ export function validateAndParseWidgetProperty({
|
||||||
evalPathsIdenticalToState,
|
evalPathsIdenticalToState,
|
||||||
evalProps,
|
evalProps,
|
||||||
isParsedValueTheSame,
|
isParsedValueTheSame,
|
||||||
statePath: fullPropertyPath,
|
fullPropertyPath,
|
||||||
value: evaluatedValue,
|
value: evaluatedValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -162,74 +166,93 @@ export function validateActionProperty(
|
||||||
}
|
}
|
||||||
return validate(config, value, {}, "");
|
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(
|
export function getValidatedTree(
|
||||||
tree: DataTree,
|
dataTree: DataTree,
|
||||||
option: { evalProps: EvalProps; evalPathsIdenticalToState: any },
|
option: {
|
||||||
|
evalProps: EvalProps;
|
||||||
|
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||||
|
pathsValidated: string[];
|
||||||
|
},
|
||||||
configTree: ConfigTree,
|
configTree: ConfigTree,
|
||||||
) {
|
) {
|
||||||
const { evalPathsIdenticalToState, evalProps } = option;
|
const { evalPathsIdenticalToState, evalProps, pathsValidated } = option;
|
||||||
return Object.keys(tree).reduce((tree, entityKey: string) => {
|
for (const [entityName, entity] of Object.entries(dataTree)) {
|
||||||
const entity = tree[entityKey];
|
|
||||||
if (!isWidget(entity)) {
|
if (!isWidget(entity)) {
|
||||||
return tree;
|
continue;
|
||||||
}
|
}
|
||||||
const entityConfig = configTree[entityKey] as WidgetEntityConfig;
|
const entityConfig = configTree[entityName] as WidgetEntityConfig;
|
||||||
|
|
||||||
Object.entries(entityConfig.validationPaths).forEach(
|
const validationPathsMap = Object.entries(entityConfig.validationPaths);
|
||||||
([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 isParsedValueTheSame = parsed === evaluatedValue;
|
for (const [propertyPath, validationConfig] of validationPathsMap) {
|
||||||
const fullPropertyPath = `${entityKey}.${property}`;
|
const fullPropertyPath = `${entityName}.${propertyPath}`;
|
||||||
const evalPath = getEvalValuePath(fullPropertyPath, {
|
|
||||||
isPopulated: false,
|
|
||||||
fullPath: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
setToEvalPathsIdenticalToState({
|
if (pathsValidated.includes(fullPropertyPath)) continue;
|
||||||
evalPath,
|
|
||||||
evalPathsIdenticalToState,
|
|
||||||
evalProps,
|
|
||||||
isParsedValueTheSame,
|
|
||||||
statePath: fullPropertyPath,
|
|
||||||
value: evaluatedValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
evalProps,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
|
configTree,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (!isValid) {
|
}
|
||||||
const evalErrors: EvaluationError[] =
|
}
|
||||||
messages?.map((message) => ({
|
return dataTree;
|
||||||
errorType: PropertyEvaluationErrorType.VALIDATION,
|
|
||||||
errorMessage: message,
|
|
||||||
severity: Severity.ERROR,
|
|
||||||
raw: value,
|
|
||||||
})) ?? [];
|
|
||||||
|
|
||||||
addErrorToEntityProperty({
|
|
||||||
errors: evalErrors,
|
|
||||||
evalProps,
|
|
||||||
fullPropertyPath,
|
|
||||||
dataTree: tree,
|
|
||||||
configTree,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return { ...tree, [entityKey]: entity };
|
|
||||||
}, tree);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,6 @@ export const updateDependencyMap = ({
|
||||||
const dependenciesOfRemovedPaths: Array<string> = [];
|
const dependenciesOfRemovedPaths: Array<string> = [];
|
||||||
const removedPaths: Array<{ entityId: string; fullpath: string }> = [];
|
const removedPaths: Array<{ entityId: string; fullpath: string }> = [];
|
||||||
let didUpdateDependencyMap = false;
|
let didUpdateDependencyMap = false;
|
||||||
let didUpdateValidationDependencyMap = false;
|
|
||||||
const {
|
const {
|
||||||
allKeys,
|
allKeys,
|
||||||
dependencyMap,
|
dependencyMap,
|
||||||
|
|
@ -161,10 +160,10 @@ export const updateDependencyMap = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const didUpdateDep = dependencyMap.addNodes(allAddedPaths, false);
|
const didUpdateDep = dependencyMap.addNodes(allAddedPaths, false);
|
||||||
const didUpdateValidationDep =
|
validationDependencyMap.addNodes(allAddedPaths);
|
||||||
validationDependencyMap.addNodes(allAddedPaths);
|
|
||||||
if (didUpdateDep) didUpdateDependencyMap = true;
|
if (didUpdateDep) didUpdateDependencyMap = true;
|
||||||
if (didUpdateValidationDep) didUpdateValidationDependencyMap = true;
|
|
||||||
if (isWidgetActionOrJsObject(entity)) {
|
if (isWidgetActionOrJsObject(entity)) {
|
||||||
if (!isDynamicLeaf(unEvalDataTree, fullPropertyPath, configTree)) {
|
if (!isDynamicLeaf(unEvalDataTree, fullPropertyPath, configTree)) {
|
||||||
const entityDependencyMap = getEntityDependencies(
|
const entityDependencyMap = getEntityDependencies(
|
||||||
|
|
@ -200,7 +199,6 @@ export const updateDependencyMap = ({
|
||||||
path,
|
path,
|
||||||
validationDependencies[path],
|
validationDependencies[path],
|
||||||
);
|
);
|
||||||
didUpdateValidationDependencyMap = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -229,7 +227,6 @@ export const updateDependencyMap = ({
|
||||||
path,
|
path,
|
||||||
validationDependencies[path],
|
validationDependencies[path],
|
||||||
);
|
);
|
||||||
didUpdateValidationDependencyMap = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,10 +252,9 @@ export const updateDependencyMap = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const didUpdateDeps = dependencyMap.removeNodes(allDeletedPaths);
|
const didUpdateDeps = dependencyMap.removeNodes(allDeletedPaths);
|
||||||
const didUpdateValidationDeps =
|
validationDependencyMap.removeNodes(allDeletedPaths);
|
||||||
validationDependencyMap.removeNodes(allDeletedPaths);
|
|
||||||
if (didUpdateDeps) didUpdateDependencyMap = true;
|
if (didUpdateDeps) didUpdateDependencyMap = true;
|
||||||
if (didUpdateValidationDeps) didUpdateValidationDependencyMap = true;
|
|
||||||
|
|
||||||
if (isWidgetActionOrJsObject(entity)) {
|
if (isWidgetActionOrJsObject(entity)) {
|
||||||
const entityId = getEntityId(entity);
|
const entityId = getEntityId(entity);
|
||||||
|
|
@ -314,10 +310,6 @@ export const updateDependencyMap = ({
|
||||||
translatedDiffs,
|
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 */
|
/** We need this in order clear out the paths that could have errors when a property is deleted */
|
||||||
if (removedPaths.length) {
|
if (removedPaths.length) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { find, toPath, union } from "lodash";
|
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 { EvalErrorTypes } from "utils/DynamicBindingUtils";
|
||||||
import { extractIdentifierInfoFromCode } from "@shared/ast";
|
import { extractIdentifierInfoFromCode } from "@shared/ast";
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,7 +12,6 @@ import {
|
||||||
import type {
|
import type {
|
||||||
ConfigTree,
|
ConfigTree,
|
||||||
DataTreeEntity,
|
DataTreeEntity,
|
||||||
WidgetEntity,
|
|
||||||
WidgetEntityConfig,
|
WidgetEntityConfig,
|
||||||
} from "entities/DataTree/dataTreeFactory";
|
} from "entities/DataTree/dataTreeFactory";
|
||||||
import {
|
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
|
/**This function returns a unique array containing a merge of both arrays
|
||||||
* @param currentArr
|
* @param currentArr
|
||||||
* @param updateArr
|
* @param updateArr
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import type {
|
||||||
} from "entities/DataTree/dataTreeFactory";
|
} from "entities/DataTree/dataTreeFactory";
|
||||||
import type { DependencyMap } from "utils/DynamicBindingUtils";
|
import type { DependencyMap } from "utils/DynamicBindingUtils";
|
||||||
|
|
||||||
|
const DATA_DERIVED_PROPERTY_PLACEHOLDER = "*";
|
||||||
|
|
||||||
export function getValidationDependencies(
|
export function getValidationDependencies(
|
||||||
entity: DataTreeEntity,
|
entity: DataTreeEntity,
|
||||||
entityName: string,
|
entityName: string,
|
||||||
|
|
@ -19,9 +21,29 @@ export function getValidationDependencies(
|
||||||
Object.entries(validationPaths).forEach(
|
Object.entries(validationPaths).forEach(
|
||||||
([propertyPath, validationConfig]) => {
|
([propertyPath, validationConfig]) => {
|
||||||
if (validationConfig.dependentPaths) {
|
if (validationConfig.dependentPaths) {
|
||||||
const dependencyArray = validationConfig.dependentPaths.map(
|
const propertyPathsSplitArray = propertyPath.split(".");
|
||||||
(path) => `${entityName}.${path}`,
|
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;
|
validationDependency[`${entityName}.${propertyPath}`] = dependencyArray;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user