Merge pull request #32102 from appsmithorg/release
chore: Daily promotion part 2 for 27th March, 2024
This commit is contained in:
commit
b47c563b32
|
|
@ -2,6 +2,7 @@ import {
|
|||
agHelper,
|
||||
deployMode,
|
||||
dataSources,
|
||||
assertHelper,
|
||||
locators,
|
||||
draggableWidgets,
|
||||
} from "../../../support/Objects/ObjectsCore";
|
||||
|
|
@ -86,6 +87,7 @@ describe("PgAdmin Clone App", { tags: ["@tag.Datasource"] }, function () {
|
|||
cy.xpath(appPage.submitButton).click({ force: true });
|
||||
agHelper.AssertElementVisibility(appPage.addColumn);
|
||||
cy.xpath(appPage.submitButton).first().click({ force: true });
|
||||
assertHelper.AssertNetworkStatus("@postExecute");
|
||||
cy.xpath(appPage.closeButton).click({ force: true });
|
||||
cy.xpath(appPage.addNewtable).should("be.visible");
|
||||
// viewing the table's columns by clicking on view button
|
||||
|
|
|
|||
|
|
@ -196,9 +196,8 @@ function setPropertyPaneSectionState(propertySectionState) {
|
|||
)) {
|
||||
cy.get("body").then(($body) => {
|
||||
if (
|
||||
$body.find(
|
||||
`${propertySectionClass(sectionName)} .t--chevron-icon.rotate-180`,
|
||||
).length >
|
||||
$body.find(`${propertySectionClass(sectionName)} .t--chevron-icon`)
|
||||
.length >
|
||||
0 !==
|
||||
shouldSectionOpen
|
||||
) {
|
||||
|
|
@ -214,9 +213,8 @@ function verifyPropertyPaneSectionState(propertySectionState) {
|
|||
)) {
|
||||
cy.get("body").then(($body) => {
|
||||
const isSectionOpen =
|
||||
$body.find(
|
||||
`${propertySectionClass(sectionName)} .t--chevron-icon.rotate-180`,
|
||||
).length > 0;
|
||||
$body.find(`${propertySectionClass(sectionName)} .t--chevron-icon`)
|
||||
.length > 0;
|
||||
expect(isSectionOpen).to.equal(shouldSectionOpen);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,9 +180,8 @@ function setPropertyPaneSectionState(propertySectionState) {
|
|||
)) {
|
||||
cy.get("body").then(($body) => {
|
||||
if (
|
||||
$body.find(
|
||||
`${propertySectionClass(sectionName)} .t--chevron-icon.rotate-180`,
|
||||
).length >
|
||||
$body.find(`${propertySectionClass(sectionName)} .t--chevron-icon`)
|
||||
.length >
|
||||
0 !==
|
||||
shouldSectionOpen
|
||||
) {
|
||||
|
|
@ -198,9 +197,8 @@ function verifyPropertyPaneSectionState(propertySectionState) {
|
|||
)) {
|
||||
cy.get("body").then(($body) => {
|
||||
const isSectionOpen =
|
||||
$body.find(
|
||||
`${propertySectionClass(sectionName)} .t--chevron-icon.rotate-180`,
|
||||
).length > 0;
|
||||
$body.find(`${propertySectionClass(sectionName)} .t--chevron-icon`)
|
||||
.length > 0;
|
||||
expect(isSectionOpen).to.equal(shouldSectionOpen);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# To run only limited tests - give the spec names in below format:
|
||||
cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js
|
||||
|
||||
|
||||
# For running all specs - uncomment below:
|
||||
#cypress/e2e/**/**/*
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { intersection } from "lodash";
|
|||
import type { DependencyMap } from "utils/DynamicBindingUtils";
|
||||
import type { QueryActionConfig } from "entities/Action";
|
||||
import type { DatasourceConfiguration } from "entities/Datasource";
|
||||
import type { DiffWithReferenceState } from "workers/Evaluation/helpers";
|
||||
import type { DiffWithNewTreeState } from "workers/Evaluation/helpers";
|
||||
import {
|
||||
EVALUATE_REDUX_ACTIONS,
|
||||
EVAL_AND_LINT_REDUX_ACTIONS,
|
||||
|
|
@ -57,8 +57,8 @@ export function shouldLog(action: ReduxAction<unknown>) {
|
|||
}
|
||||
|
||||
export const setEvaluatedTree = (
|
||||
updates: DiffWithReferenceState[],
|
||||
): ReduxAction<{ updates: DiffWithReferenceState[] }> => {
|
||||
updates: DiffWithNewTreeState[],
|
||||
): ReduxAction<{ updates: DiffWithNewTreeState[] }> => {
|
||||
return {
|
||||
type: ReduxActionTypes.SET_EVALUATED_TREE,
|
||||
payload: { updates },
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import { makeEntityConfigsAsObjProperties } from "@appsmith/workers/Evaluation/dataTreeUtils";
|
||||
import { smallDataSet } from "workers/Evaluation/__tests__/generateOpimisedUpdates.test";
|
||||
import produce from "immer";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
const unevalTreeFromMainThread = {
|
||||
Api2: {
|
||||
|
|
@ -131,122 +128,22 @@ const unevalTreeFromMainThread = {
|
|||
|
||||
describe("7. Test util methods", () => {
|
||||
describe("makeDataTreeEntityConfigAsProperty", () => {
|
||||
it("should not introduce __evaluation__ property", () => {
|
||||
it("should not introduce __evaluation__ property to the widget state when the corresponding evalProps isn't there for a widget", () => {
|
||||
const dataTree = makeEntityConfigsAsObjProperties(
|
||||
unevalTreeFromMainThread as unknown as DataTree,
|
||||
);
|
||||
|
||||
expect(dataTree.Api2).not.toHaveProperty("__evaluation__");
|
||||
});
|
||||
describe("identicalEvalPathsPatches decompress updates", () => {
|
||||
it("should decompress identicalEvalPathsPatches updates into evalProps and not in state", () => {
|
||||
const state = {
|
||||
Table1: {
|
||||
filteredTableData: smallDataSet,
|
||||
selectedRows: [],
|
||||
pageSize: 0,
|
||||
},
|
||||
} as any;
|
||||
it("should introduce __evaluation__ property to the widget state when it has a corresponding evalProps", () => {
|
||||
const dataTree = makeEntityConfigsAsObjProperties(
|
||||
unevalTreeFromMainThread as unknown as DataTree,
|
||||
{
|
||||
evalProps: { Api2: { __evaluation__: { errors: { someVal: [] } } } },
|
||||
},
|
||||
);
|
||||
|
||||
const identicalEvalPathsPatches = {
|
||||
"Table1.__evaluation__.evaluatedValues.['filteredTableData']":
|
||||
"Table1.filteredTableData",
|
||||
};
|
||||
const evalProps = {
|
||||
Table1: {
|
||||
__evaluation__: {
|
||||
evaluatedValues: {
|
||||
someProp: "abc",
|
||||
},
|
||||
errors: {
|
||||
someProp1: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as any;
|
||||
const dataTree = makeEntityConfigsAsObjProperties(state as any, {
|
||||
sanitizeDataTree: true,
|
||||
evalProps,
|
||||
identicalEvalPathsPatches,
|
||||
});
|
||||
// only errors should be attached to dataTree evaluatedValues should be excluded
|
||||
const expectedState = produce(state, (draft: any) => {
|
||||
draft.Table1.__evaluation__ = { errors: { someProp1: "abc" } };
|
||||
});
|
||||
|
||||
expect(dataTree).toEqual(expectedState);
|
||||
//evalProps should have decompressed updates in it coming from identicalEvalPathsPatches
|
||||
const expectedEvalProps = produce(evalProps, (draft: any) => {
|
||||
draft.Table1.__evaluation__.evaluatedValues.filteredTableData =
|
||||
smallDataSet;
|
||||
});
|
||||
expect(evalProps).toEqual(expectedEvalProps);
|
||||
});
|
||||
|
||||
it("should not make any updates to evalProps when the identicalEvalPathsPatches is empty", () => {
|
||||
const state = {
|
||||
Table1: {
|
||||
filteredTableData: smallDataSet,
|
||||
selectedRows: [],
|
||||
pageSize: 0,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const identicalEvalPathsPatches = {};
|
||||
const initialEvalProps = {} as any;
|
||||
const evalProps = cloneDeep(initialEvalProps);
|
||||
const dataTree = makeEntityConfigsAsObjProperties(state, {
|
||||
sanitizeDataTree: true,
|
||||
evalProps,
|
||||
identicalEvalPathsPatches,
|
||||
});
|
||||
|
||||
expect(dataTree).toEqual(dataTree);
|
||||
//evalProps not be mutated with any updates
|
||||
expect(evalProps).toEqual(initialEvalProps);
|
||||
});
|
||||
|
||||
it("should ignore non relevant identicalEvalPathsPatches updates into evalProps and state", () => {
|
||||
const state = {
|
||||
Table1: {
|
||||
filteredTableData: smallDataSet,
|
||||
selectedRows: [],
|
||||
pageSize: 0,
|
||||
},
|
||||
} as any;
|
||||
//ignore non existent widget state
|
||||
const identicalEvalPathsPatches = {
|
||||
"SomeWidget.__evaluation__.evaluatedValues.['filteredTableData']":
|
||||
"SomeWidget.filteredTableData",
|
||||
};
|
||||
|
||||
const initialEvalProps = {
|
||||
Table1: {
|
||||
__evaluation__: {
|
||||
evaluatedValues: {
|
||||
someProp: "abc",
|
||||
},
|
||||
errors: {
|
||||
someProp1: "efg",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as any;
|
||||
const evalProps = cloneDeep(initialEvalProps);
|
||||
const dataTree = makeEntityConfigsAsObjProperties(state, {
|
||||
sanitizeDataTree: true,
|
||||
evalProps,
|
||||
identicalEvalPathsPatches,
|
||||
});
|
||||
const expectedState = produce(state, (draft: any) => {
|
||||
// evaluatedValues should not be attached to dataTree only errors should be attached
|
||||
draft.Table1.__evaluation__ = { errors: { someProp1: "efg" } };
|
||||
});
|
||||
|
||||
expect(dataTree).toEqual(expectedState);
|
||||
//evalProps not be mutated with any updates
|
||||
expect(evalProps).toEqual(initialEvalProps);
|
||||
});
|
||||
expect(dataTree.Api2).toHaveProperty("__evaluation__");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import { get, isObject, set } from "lodash";
|
||||
import { isObject, set } from "lodash";
|
||||
import { klona } from "klona/json";
|
||||
import type { EvalProps } from "workers/common/DataTreeEvaluator";
|
||||
|
||||
|
|
@ -12,14 +12,9 @@ export function makeEntityConfigsAsObjProperties(
|
|||
option = {} as {
|
||||
sanitizeDataTree?: boolean;
|
||||
evalProps?: EvalProps;
|
||||
identicalEvalPathsPatches?: Record<string, string>;
|
||||
},
|
||||
): DataTree {
|
||||
const {
|
||||
evalProps,
|
||||
identicalEvalPathsPatches,
|
||||
sanitizeDataTree = true,
|
||||
} = option;
|
||||
const { evalProps, sanitizeDataTree = true } = option;
|
||||
const newDataTree: DataTree = {};
|
||||
for (const entityName of Object.keys(dataTree)) {
|
||||
const entity = dataTree[entityName];
|
||||
|
|
@ -31,24 +26,6 @@ export function makeEntityConfigsAsObjProperties(
|
|||
|
||||
if (!evalProps) return dataTreeToReturn;
|
||||
|
||||
//clean up deletes widget states
|
||||
Object.entries(identicalEvalPathsPatches || {}).forEach(
|
||||
([evalPath, statePath]) => {
|
||||
const [entity] = statePath.split(".");
|
||||
if (!dataTreeToReturn[entity]) {
|
||||
delete identicalEvalPathsPatches?.[evalPath];
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// decompressIdenticalEvalPaths
|
||||
Object.entries(identicalEvalPathsPatches || {}).forEach(
|
||||
([evalPath, statePath]) => {
|
||||
const referencePathValue = get(dataTreeToReturn, statePath);
|
||||
set(evalProps, evalPath, referencePathValue);
|
||||
},
|
||||
);
|
||||
|
||||
for (const [entityName, entityEvalProps] of Object.entries(evalProps)) {
|
||||
if (!entityEvalProps.__evaluation__) continue;
|
||||
set(
|
||||
|
|
|
|||
|
|
@ -194,8 +194,8 @@ export const PropertySection = memo((props: PropertySectionProps) => {
|
|||
)}
|
||||
{props.collapsible && (
|
||||
<Icon
|
||||
className={`ml-auto t--chevron-icon ${isOpen ? "rotate-180" : ""}`}
|
||||
name="arrow-up-s-line"
|
||||
className={`ml-auto t--chevron-icon`}
|
||||
name={isOpen ? "expand-less" : "expand-more"}
|
||||
size="md"
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import type { Diff } from "deep-diff";
|
||||
import { applyChange } from "deep-diff";
|
||||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import { createImmerReducer } from "utils/ReducerUtils";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { get } from "lodash";
|
||||
import type { DiffWithReferenceState } from "workers/Evaluation/helpers";
|
||||
import type { DiffWithNewTreeState } from "workers/Evaluation/helpers";
|
||||
|
||||
export type EvaluatedTreeState = DataTree;
|
||||
|
||||
|
|
@ -17,7 +15,7 @@ const evaluatedTreeReducer = createImmerReducer(initialState, {
|
|||
state: EvaluatedTreeState,
|
||||
action: ReduxAction<{
|
||||
dataTree: DataTree;
|
||||
updates: DiffWithReferenceState[];
|
||||
updates: DiffWithNewTreeState[];
|
||||
removedPaths: [string];
|
||||
}>,
|
||||
) => {
|
||||
|
|
@ -26,23 +24,13 @@ const evaluatedTreeReducer = createImmerReducer(initialState, {
|
|||
return state;
|
||||
}
|
||||
for (const update of updates) {
|
||||
// Null check for typescript
|
||||
if (!Array.isArray(update.path) || update.path.length === 0) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
//these are the decompression updates, there are cases where identical values are present in the state
|
||||
//over here we have the path which has the identical value and apply as an update
|
||||
if (update.kind === "referenceState") {
|
||||
const { path, referencePath } = update;
|
||||
|
||||
const patch = {
|
||||
kind: "N",
|
||||
path,
|
||||
rhs: get(state, referencePath),
|
||||
} as Diff<DataTree, DataTree>;
|
||||
applyChange(state, undefined, patch);
|
||||
if (update.kind === "newTree") {
|
||||
return update.rhs;
|
||||
} else {
|
||||
if (!update.path || update.path.length === 0) {
|
||||
continue;
|
||||
}
|
||||
applyChange(state, undefined, update);
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import log from "loglevel";
|
||||
import type { DiffWithReferenceState } from "workers/Evaluation/helpers";
|
||||
import type { DiffWithNewTreeState } from "workers/Evaluation/helpers";
|
||||
|
||||
export const parseUpdatesAndDeleteUndefinedUpdates = (
|
||||
updates: string,
|
||||
): DiffWithReferenceState[] => {
|
||||
): DiffWithNewTreeState[] => {
|
||||
let parsedUpdates = [];
|
||||
try {
|
||||
//Parse updates from a string
|
||||
|
|
@ -12,6 +12,7 @@ export const parseUpdatesAndDeleteUndefinedUpdates = (
|
|||
log.error("Failed to parse updates", e, updates);
|
||||
return [];
|
||||
}
|
||||
|
||||
//delete all undefined properties from the state
|
||||
const { deleteUpdates, regularUpdates } = parsedUpdates.reduce(
|
||||
(acc: any, curr: any) => {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,7 +13,11 @@ import { MessageType, sendMessage } from "utils/MessageUtil";
|
|||
import { MAIN_THREAD_ACTION } from "@appsmith/workers/Evaluation/evalWorkerActions";
|
||||
import type { UpdateDataTreeMessageData } from "sagas/EvalWorkerActionSagas";
|
||||
import type { JSUpdate } from "utils/JSPaneUtils";
|
||||
import { generateOptimisedUpdatesAndSetPrevState } from "./helpers";
|
||||
import {
|
||||
generateOptimisedUpdatesAndSetPrevState,
|
||||
getNewDataTreeUpdates,
|
||||
uniqueOrderUpdatePaths,
|
||||
} from "./helpers";
|
||||
|
||||
export function evalTreeWithChanges(
|
||||
updatedValuePaths: string[][],
|
||||
|
|
@ -49,8 +53,6 @@ export function evalTreeWithChanges(
|
|||
|
||||
dataTree = makeEntityConfigsAsObjProperties(dataTreeEvaluator.evalTree, {
|
||||
evalProps: dataTreeEvaluator.evalProps,
|
||||
identicalEvalPathsPatches:
|
||||
dataTreeEvaluator.getEvalPathsIdenticalToState(),
|
||||
});
|
||||
|
||||
/** Make sure evalMetaUpdates is sanitized to prevent postMessage failure */
|
||||
|
|
@ -62,11 +64,26 @@ export function evalTreeWithChanges(
|
|||
unevalTree = dataTreeEvaluator.getOldUnevalTree();
|
||||
configTree = dataTreeEvaluator.oldConfigTree;
|
||||
}
|
||||
const allUnevalUpdates = unEvalUpdates.map(
|
||||
(update) => update.payload.propertyPath,
|
||||
);
|
||||
const completeEvalOrder = uniqueOrderUpdatePaths([
|
||||
...allUnevalUpdates,
|
||||
...evalOrder,
|
||||
]);
|
||||
|
||||
const setterAndLocalStorageUpdates = getNewDataTreeUpdates(
|
||||
uniqueOrderUpdatePaths(updatedValuePaths.map((val) => val.join("."))),
|
||||
dataTree,
|
||||
);
|
||||
|
||||
const updates = generateOptimisedUpdatesAndSetPrevState(
|
||||
dataTree,
|
||||
dataTreeEvaluator,
|
||||
completeEvalOrder,
|
||||
setterAndLocalStorageUpdates,
|
||||
);
|
||||
|
||||
const evalTreeResponse: EvalTreeResponseData = {
|
||||
updates,
|
||||
dependencies,
|
||||
|
|
|
|||
|
|
@ -87,8 +87,6 @@ function resetWidgetMetaProperty(
|
|||
const oldUnEvalTree = dataTreeEvaluator.getOldUnevalTree();
|
||||
const configTree = dataTreeEvaluator.getConfigTree();
|
||||
const evalProps = dataTreeEvaluator.getEvalProps();
|
||||
const evalPathsIdenticalToState =
|
||||
dataTreeEvaluator.getEvalPathsIdenticalToState();
|
||||
|
||||
const evaluatedEntity = evalTree[widget.widgetName];
|
||||
const evaluatedEntityConfig = configTree[
|
||||
|
|
@ -135,7 +133,6 @@ function resetWidgetMetaProperty(
|
|||
evalPropertyValue: finalValue,
|
||||
unEvalPropertyValue: expressionToEvaluate,
|
||||
evalProps,
|
||||
evalPathsIdenticalToState,
|
||||
});
|
||||
|
||||
evalMetaUpdates.push({
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import DataTreeEvaluator from "workers/common/DataTreeEvaluator";
|
|||
import type { EvalMetaUpdates } from "@appsmith/workers/common/DataTreeEvaluator/types";
|
||||
import { makeEntityConfigsAsObjProperties } from "@appsmith/workers/Evaluation/dataTreeUtils";
|
||||
import type { DataTreeDiff } from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||
import { serialiseToBigInt } from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||
import {
|
||||
CrashingError,
|
||||
getSafeToRenderDataTree,
|
||||
|
|
@ -22,7 +23,10 @@ import { clearAllIntervals } from "../fns/overrides/interval";
|
|||
import JSObjectCollection from "workers/Evaluation/JSObject/Collection";
|
||||
import { getJSVariableCreatedEvents } from "../JSObject/JSVariableEvents";
|
||||
import { errorModifier } from "../errorModifier";
|
||||
import { generateOptimisedUpdatesAndSetPrevState } from "../helpers";
|
||||
import {
|
||||
generateOptimisedUpdatesAndSetPrevState,
|
||||
uniqueOrderUpdatePaths,
|
||||
} from "../helpers";
|
||||
import DataStore from "../dataStore";
|
||||
import type { TransmissionErrorHandler } from "../fns/utils/Messenger";
|
||||
import { MessageType, sendMessage } from "utils/MessageUtil";
|
||||
|
|
@ -76,6 +80,7 @@ export function evalTree(request: EvalWorkerSyncRequest) {
|
|||
canvasWidgets = widgets;
|
||||
canvasWidgetsMeta = widgetsMeta;
|
||||
metaWidgetsCache = metaWidgets;
|
||||
let isNewTree = false;
|
||||
|
||||
try {
|
||||
if (!dataTreeEvaluator) {
|
||||
|
|
@ -107,10 +112,9 @@ export function evalTree(request: EvalWorkerSyncRequest) {
|
|||
|
||||
dataTree = makeEntityConfigsAsObjProperties(dataTreeResponse.evalTree, {
|
||||
evalProps: dataTreeEvaluator.evalProps,
|
||||
identicalEvalPathsPatches:
|
||||
dataTreeEvaluator?.getEvalPathsIdenticalToState(),
|
||||
});
|
||||
staleMetaIds = dataTreeResponse.staleMetaIds;
|
||||
isNewTree = true;
|
||||
} else if (dataTreeEvaluator.hasCyclicalDependency || forceEvaluation) {
|
||||
if (dataTreeEvaluator && !isEmpty(allActionValidationConfig)) {
|
||||
//allActionValidationConfigs may not be set in dataTreeEvaluator. Therefore, set it explicitly via setter method
|
||||
|
|
@ -150,8 +154,6 @@ export function evalTree(request: EvalWorkerSyncRequest) {
|
|||
|
||||
dataTree = makeEntityConfigsAsObjProperties(dataTreeResponse.evalTree, {
|
||||
evalProps: dataTreeEvaluator.evalProps,
|
||||
identicalEvalPathsPatches:
|
||||
dataTreeEvaluator?.getEvalPathsIdenticalToState(),
|
||||
});
|
||||
staleMetaIds = dataTreeResponse.staleMetaIds;
|
||||
} else {
|
||||
|
|
@ -193,8 +195,6 @@ export function evalTree(request: EvalWorkerSyncRequest) {
|
|||
|
||||
dataTree = makeEntityConfigsAsObjProperties(dataTreeEvaluator.evalTree, {
|
||||
evalProps: dataTreeEvaluator.evalProps,
|
||||
identicalEvalPathsPatches:
|
||||
dataTreeEvaluator?.getEvalPathsIdenticalToState(),
|
||||
});
|
||||
|
||||
evalMetaUpdates = JSON.parse(
|
||||
|
|
@ -229,21 +229,42 @@ export function evalTree(request: EvalWorkerSyncRequest) {
|
|||
makeEntityConfigsAsObjProperties(unevalTree, {
|
||||
sanitizeDataTree: false,
|
||||
evalProps: dataTreeEvaluator?.evalProps,
|
||||
identicalEvalPathsPatches:
|
||||
dataTreeEvaluator?.getEvalPathsIdenticalToState(),
|
||||
}),
|
||||
widgetTypeConfigMap,
|
||||
configTree,
|
||||
);
|
||||
unEvalUpdates = [];
|
||||
isNewTree = true;
|
||||
}
|
||||
|
||||
const jsVarsCreatedEvent = getJSVariableCreatedEvents(jsUpdates);
|
||||
|
||||
const updates = generateOptimisedUpdatesAndSetPrevState(
|
||||
dataTree,
|
||||
dataTreeEvaluator,
|
||||
);
|
||||
let updates;
|
||||
if (isNewTree) {
|
||||
try {
|
||||
//for new tree send the whole thing, don't diff at all
|
||||
updates = serialiseToBigInt([{ kind: "newTree", rhs: dataTree }]);
|
||||
dataTreeEvaluator?.setPrevState(dataTree);
|
||||
} catch (e) {
|
||||
updates = "[]";
|
||||
}
|
||||
isNewTree = false;
|
||||
} else {
|
||||
const allUnevalUpdates = unEvalUpdates.map(
|
||||
(update) => update.payload.propertyPath,
|
||||
);
|
||||
|
||||
const completeEvalOrder = uniqueOrderUpdatePaths([
|
||||
...allUnevalUpdates,
|
||||
...evalOrder,
|
||||
]);
|
||||
|
||||
updates = generateOptimisedUpdatesAndSetPrevState(
|
||||
dataTree,
|
||||
dataTreeEvaluator,
|
||||
completeEvalOrder,
|
||||
);
|
||||
}
|
||||
|
||||
const evalTreeResponse: EvalTreeResponseData = {
|
||||
updates,
|
||||
|
|
|
|||
|
|
@ -1,22 +1,33 @@
|
|||
import { serialiseToBigInt } from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||
import type { WidgetEntity } from "@appsmith//entities/DataTree/types";
|
||||
import type { Diff } from "deep-diff";
|
||||
import { diff } from "deep-diff";
|
||||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import equal from "fast-deep-equal";
|
||||
import { get, isNumber, isObject, set } from "lodash";
|
||||
import { get, isObject, set } from "lodash";
|
||||
import { isMoment } from "moment";
|
||||
import { EvalErrorTypes } from "utils/DynamicBindingUtils";
|
||||
|
||||
export const fn_keys: string = "__fn_keys__";
|
||||
|
||||
export interface DiffReferenceState {
|
||||
kind: "referenceState";
|
||||
path: any[];
|
||||
referencePath: string;
|
||||
export const uniqueOrderUpdatePaths = (updatePaths: string[]) =>
|
||||
Array.from(new Set(updatePaths)).sort((a, b) => b.length - a.length);
|
||||
|
||||
export const getNewDataTreeUpdates = (paths: string[], dataTree: object) =>
|
||||
paths.map((path) => {
|
||||
const segmentedPath = path.split(".");
|
||||
return {
|
||||
kind: "N",
|
||||
path: segmentedPath,
|
||||
rhs: get(dataTree, segmentedPath),
|
||||
};
|
||||
});
|
||||
|
||||
export interface DiffNewTreeState {
|
||||
kind: "newTree";
|
||||
rhs: any;
|
||||
}
|
||||
export type DiffWithReferenceState =
|
||||
| Diff<DataTree, DataTree>
|
||||
| DiffReferenceState;
|
||||
export type DiffWithNewTreeState = Diff<DataTree, DataTree> | DiffNewTreeState;
|
||||
// Finds the first index which is a duplicate value
|
||||
// Returns -1 if there are no duplicates
|
||||
// Returns the index of the first duplicate entry it finds
|
||||
|
|
@ -72,29 +83,6 @@ export const countOccurrences = (
|
|||
};
|
||||
|
||||
const LARGE_COLLECTION_SIZE = 100;
|
||||
// for object paths which have a "." in the object key like "a.['b.c']"
|
||||
const REGEX_NESTED_OBJECT_PATH = /(.+)\.\[\'(.*)\'\]/;
|
||||
|
||||
const generateWithKey = (basePath: any, key: any) => {
|
||||
const segmentedPath = [...basePath, key];
|
||||
|
||||
if (isNumber(key)) {
|
||||
return {
|
||||
path: basePath.join(".") + ".[" + key + "]",
|
||||
segmentedPath,
|
||||
};
|
||||
}
|
||||
if (key.includes(".")) {
|
||||
return {
|
||||
path: basePath.join(".") + ".['" + key + "']",
|
||||
segmentedPath,
|
||||
};
|
||||
}
|
||||
return {
|
||||
path: basePath.join(".") + "." + key,
|
||||
segmentedPath,
|
||||
};
|
||||
};
|
||||
|
||||
export const stringifyFnsInObject = (
|
||||
userObject: Record<string, unknown>,
|
||||
|
|
@ -170,68 +158,45 @@ const isLargeCollection = (val: any) => {
|
|||
return size > LARGE_COLLECTION_SIZE;
|
||||
};
|
||||
|
||||
const normaliseEvalPath = (identicalEvalPathsPatches: any) =>
|
||||
Object.keys(identicalEvalPathsPatches || {}).reduce(
|
||||
(acc: any, evalPath: string) => {
|
||||
//for object paths which have a "." in the object key like "a.['b.c']", we need to extract these
|
||||
// paths and break them to appropriate patch paths
|
||||
|
||||
const matches = evalPath.match(REGEX_NESTED_OBJECT_PATH);
|
||||
if (!matches || !matches.length) {
|
||||
//regular paths like "a.b.c"
|
||||
acc[evalPath] = identicalEvalPathsPatches[evalPath];
|
||||
return acc;
|
||||
}
|
||||
|
||||
const [, firstSeg, nestedPathSeg] = matches;
|
||||
// normalise non nested paths like "a.['b']"
|
||||
if (!nestedPathSeg.includes(".")) {
|
||||
const key = [firstSeg, nestedPathSeg].join(".");
|
||||
acc[key] = identicalEvalPathsPatches[evalPath];
|
||||
return acc;
|
||||
}
|
||||
// object paths which have a "." like "a.['b.c']"
|
||||
const key = [firstSeg, `['${nestedPathSeg}']`].join(".");
|
||||
acc[key] = identicalEvalPathsPatches[evalPath];
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
const getReducedDataTree = (
|
||||
dataTree: DataTree,
|
||||
constrainedDiffPaths: string[],
|
||||
): DataTree => {
|
||||
const withErrors = Object.keys(dataTree).reduce((acc: any, key: string) => {
|
||||
const widgetValue = dataTree[key] as WidgetEntity;
|
||||
acc[key] = {
|
||||
__evaluation__: {
|
||||
errors: widgetValue.__evaluation__?.errors,
|
||||
},
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return constrainedDiffPaths.reduce((acc: DataTree, key: string) => {
|
||||
set(acc, key, get(dataTree, key));
|
||||
return acc;
|
||||
}, withErrors);
|
||||
};
|
||||
const generateDiffUpdates = (
|
||||
oldDataTree: any,
|
||||
dataTree: any,
|
||||
ignoreLargeKeys: any,
|
||||
): DiffWithReferenceState[] => {
|
||||
const attachDirectly: DiffWithReferenceState[] = [];
|
||||
const ignoreLargeKeysHasBeenAttached = new Set();
|
||||
const attachLater: DiffWithReferenceState[] = [];
|
||||
oldDataTree: DataTree,
|
||||
dataTree: DataTree,
|
||||
constrainedDiffPaths: string[],
|
||||
): Diff<DataTree, DataTree>[] => {
|
||||
const attachDirectly: Diff<DataTree, DataTree>[] = [];
|
||||
const attachLater: Diff<DataTree, DataTree>[] = [];
|
||||
|
||||
// we are reducing the data tree to only the paths that are being diffed
|
||||
const oldData = getReducedDataTree(oldDataTree, constrainedDiffPaths);
|
||||
const newData = getReducedDataTree(dataTree, constrainedDiffPaths);
|
||||
const updates =
|
||||
diff(oldDataTree, dataTree, (path, key) => {
|
||||
diff(oldData, newData, (path, key) => {
|
||||
if (!path.length || key === "__evaluation__") return false;
|
||||
|
||||
const { path: setPath, segmentedPath } = generateWithKey(path, key);
|
||||
const segmentedPath = [...path, key];
|
||||
|
||||
// if ignore path is present...this segment of code generates the data compression patches
|
||||
if (!!ignoreLargeKeys[setPath]) {
|
||||
const originalStateVal = get(oldDataTree, segmentedPath);
|
||||
const correspondingStatePath = ignoreLargeKeys[setPath];
|
||||
const statePathValue = get(dataTree, correspondingStatePath);
|
||||
if (!equal(originalStateVal, statePathValue)) {
|
||||
//reference state patches are a patch that does not have a patch value but it provides a path which contains the same value
|
||||
//this is helpful in making the payload sent to the main thread small
|
||||
attachLater.push({
|
||||
kind: "referenceState",
|
||||
path: segmentedPath,
|
||||
referencePath: correspondingStatePath,
|
||||
});
|
||||
}
|
||||
ignoreLargeKeysHasBeenAttached.add(setPath);
|
||||
return true;
|
||||
}
|
||||
const rhs = get(dataTree, segmentedPath);
|
||||
const rhs = get(dataTree, segmentedPath) as DataTree;
|
||||
|
||||
const lhs = get(oldDataTree, segmentedPath);
|
||||
const lhs = get(oldDataTree, segmentedPath) as DataTree;
|
||||
|
||||
//when a moment value changes we do not want the inner moment object updates, we just want the ISO result of it
|
||||
// which we get during the serialisation process we perform at latter steps
|
||||
|
|
@ -250,7 +215,6 @@ const generateDiffUpdates = (
|
|||
if (lhs !== undefined) {
|
||||
attachDirectly.push({ kind: "D", lhs, path: segmentedPath });
|
||||
}
|
||||
// if the lhs is also undefined ignore diff on this node
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -280,20 +244,107 @@ const generateDiffUpdates = (
|
|||
return [...updates, ...largeDataSetUpdates];
|
||||
};
|
||||
|
||||
const correctUndefinedUpdatesToDeletesOrNew = (
|
||||
updates: Diff<DataTree, DataTree>[],
|
||||
) =>
|
||||
updates.reduce(
|
||||
(acc, update) => {
|
||||
const { kind, lhs, path, rhs } = update as any;
|
||||
if (kind === "E") {
|
||||
if (lhs === undefined && rhs !== undefined) {
|
||||
acc.push({ kind: "N", path, rhs });
|
||||
}
|
||||
if (lhs !== undefined && rhs === undefined) {
|
||||
acc.push({ path, lhs, kind: "D" });
|
||||
}
|
||||
if (lhs !== undefined && rhs !== undefined) {
|
||||
acc.push(update);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
acc.push(update);
|
||||
return acc;
|
||||
},
|
||||
[] as Diff<DataTree, DataTree>[],
|
||||
);
|
||||
|
||||
// whenever an element in a collection is set to undefined, we need to send the entire collection as an update
|
||||
const generateRootWidgetUpdates = (
|
||||
updates: Diff<DataTree, DataTree>[],
|
||||
newDataTree: DataTree,
|
||||
oldDataTree: DataTree,
|
||||
): Diff<DataTree, DataTree>[] =>
|
||||
updates
|
||||
.filter(
|
||||
(v) =>
|
||||
v.kind === "D" &&
|
||||
v.path &&
|
||||
typeof v.path[v.path.length - 1] === "number",
|
||||
)
|
||||
.map(
|
||||
({ path }: any) => {
|
||||
const pathCopy = [...path];
|
||||
pathCopy.pop();
|
||||
return {
|
||||
kind: "E",
|
||||
path: pathCopy,
|
||||
lhs: get(oldDataTree, pathCopy) as DataTree,
|
||||
rhs: get(newDataTree, pathCopy) as DataTree,
|
||||
}; //push the parent path
|
||||
},
|
||||
[] as Diff<DataTree, DataTree>[],
|
||||
);
|
||||
|
||||
// when a root collection is updated, we need to scrub out updates that are inside the root collection
|
||||
const getScrubbedOutUpdatesWhenRootCollectionIsUpdated = (
|
||||
updates: Diff<DataTree, DataTree>[],
|
||||
rootCollectionUpdates: Diff<DataTree, DataTree>[],
|
||||
) => {
|
||||
const rootCollectionPaths = rootCollectionUpdates
|
||||
.filter((update) => update?.path?.length)
|
||||
.map((update) => (update.path as string[]).join("."));
|
||||
return (
|
||||
updates
|
||||
.map((update: any) => ({ update, condensedPath: update.path.join(".") }))
|
||||
.filter(
|
||||
({ condensedPath }) =>
|
||||
!rootCollectionPaths.some((p) => condensedPath.startsWith(p)),
|
||||
)
|
||||
// remove the condensedPath from the update
|
||||
.map(({ update }) => update)
|
||||
);
|
||||
};
|
||||
|
||||
export const generateOptimisedUpdates = (
|
||||
oldDataTree: any,
|
||||
dataTree: any,
|
||||
identicalEvalPathsPatches?: Record<string, string>,
|
||||
): DiffWithReferenceState[] => {
|
||||
const ignoreLargeKeys = normaliseEvalPath(identicalEvalPathsPatches);
|
||||
const updates = generateDiffUpdates(oldDataTree, dataTree, ignoreLargeKeys);
|
||||
return updates;
|
||||
oldDataTree: DataTree,
|
||||
dataTree: DataTree,
|
||||
// these are the paths that the diff is limited to, this is a performance optimisation and through this we don't have to diff the entire data tree
|
||||
constrainedDiffPaths: string[],
|
||||
): Diff<DataTree, DataTree>[] => {
|
||||
const updates = generateDiffUpdates(
|
||||
oldDataTree,
|
||||
dataTree,
|
||||
constrainedDiffPaths,
|
||||
);
|
||||
const correctedUpdates = correctUndefinedUpdatesToDeletesOrNew(updates);
|
||||
|
||||
const rootCollectionUpdates = generateRootWidgetUpdates(
|
||||
correctedUpdates,
|
||||
dataTree,
|
||||
oldDataTree,
|
||||
);
|
||||
const scrubedOutUpdates = getScrubbedOutUpdatesWhenRootCollectionIsUpdated(
|
||||
correctedUpdates,
|
||||
rootCollectionUpdates,
|
||||
);
|
||||
return [...scrubedOutUpdates, ...rootCollectionUpdates];
|
||||
};
|
||||
|
||||
export const generateSerialisedUpdates = (
|
||||
prevState: any,
|
||||
currentState: any,
|
||||
identicalEvalPathsPatches: any,
|
||||
prevState: DataTree,
|
||||
currentState: DataTree,
|
||||
constrainedDiffPaths: string[],
|
||||
mergeAdditionalUpdates?: any,
|
||||
): {
|
||||
serialisedUpdates: string;
|
||||
error?: { type: string; message: string };
|
||||
|
|
@ -301,13 +352,14 @@ export const generateSerialisedUpdates = (
|
|||
const updates = generateOptimisedUpdates(
|
||||
prevState,
|
||||
currentState,
|
||||
identicalEvalPathsPatches,
|
||||
constrainedDiffPaths,
|
||||
);
|
||||
|
||||
//remove lhs from diff to reduce the size of diff upload,
|
||||
//it is not necessary to send lhs and we can make the payload to transfer to the main thread smaller for quicker transfer
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const removedLhs = updates.map(({ lhs, ...rest }: any) => rest);
|
||||
let removedLhs = updates.map(({ lhs, ...rest }: any) => rest);
|
||||
removedLhs = [...removedLhs, ...(mergeAdditionalUpdates || [])];
|
||||
|
||||
try {
|
||||
// serialise bigInt values and convert the updates to a string over here to minismise the cost of transfer
|
||||
|
|
@ -325,16 +377,16 @@ export const generateSerialisedUpdates = (
|
|||
};
|
||||
|
||||
export const generateOptimisedUpdatesAndSetPrevState = (
|
||||
dataTree: any,
|
||||
dataTree: DataTree,
|
||||
dataTreeEvaluator: any,
|
||||
constrainedDiffPaths: string[],
|
||||
mergeAdditionalUpdates?: any,
|
||||
) => {
|
||||
const identicalEvalPathsPatches =
|
||||
dataTreeEvaluator?.getEvalPathsIdenticalToState();
|
||||
|
||||
const { error, serialisedUpdates } = generateSerialisedUpdates(
|
||||
dataTreeEvaluator.getPrevState(),
|
||||
dataTree,
|
||||
identicalEvalPathsPatches,
|
||||
constrainedDiffPaths,
|
||||
mergeAdditionalUpdates,
|
||||
);
|
||||
|
||||
if (error) {
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ import {
|
|||
import { getFixedTimeDifference, replaceThisDotParams } from "./utils";
|
||||
import { isJSObjectFunction } from "workers/Evaluation/JSObject/utils";
|
||||
import {
|
||||
setToEvalPathsIdenticalToState,
|
||||
validateActionProperty,
|
||||
validateAndParseWidgetProperty,
|
||||
validateWidgetProperty,
|
||||
|
|
@ -134,7 +133,6 @@ export interface EvalProps {
|
|||
[entityName: string]: DataTreeEvaluationProps;
|
||||
}
|
||||
|
||||
export type EvalPathsIdenticalToState = Record<string, string>;
|
||||
export default class DataTreeEvaluator {
|
||||
dependencyMap: DependencyMap = new DependencyMap();
|
||||
sortedDependencies: SortedDependencies = [];
|
||||
|
|
@ -164,9 +162,6 @@ export default class DataTreeEvaluator {
|
|||
* Sanitized eval values and errors
|
||||
*/
|
||||
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: EvalPathsIdenticalToState = {};
|
||||
undefinedEvalValuesMap: Record<string, boolean> = {};
|
||||
|
||||
prevState = {};
|
||||
|
|
@ -187,9 +182,6 @@ export default class DataTreeEvaluator {
|
|||
this.widgetConfigMap = widgetConfigMap;
|
||||
}
|
||||
|
||||
getEvalPathsIdenticalToState(): EvalPathsIdenticalToState {
|
||||
return this.evalPathsIdenticalToState || {};
|
||||
}
|
||||
getEvalTree() {
|
||||
return this.evalTree;
|
||||
}
|
||||
|
|
@ -343,12 +335,11 @@ export default class DataTreeEvaluator {
|
|||
dataTree: DataTree,
|
||||
option: {
|
||||
evalProps: EvalProps;
|
||||
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||
},
|
||||
configTree: ConfigTree,
|
||||
) {
|
||||
const unParsedEvalTree = this.getUnParsedEvalTree();
|
||||
const { evalPathsIdenticalToState, evalProps } = option;
|
||||
const { evalProps } = option;
|
||||
for (const [entityName, entity] of Object.entries(dataTree)) {
|
||||
if (!isWidget(entity)) continue;
|
||||
const entityConfig = configTree[entityName] as WidgetEntityConfig;
|
||||
|
|
@ -363,33 +354,15 @@ export default class DataTreeEvaluator {
|
|||
get(entity, propertyPath),
|
||||
);
|
||||
// Pass it through parse
|
||||
const { isValid, messages, parsed, transformed } =
|
||||
validateWidgetProperty(validationConfig, value, entity, propertyPath);
|
||||
const { isValid, messages, parsed } = 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,
|
||||
|
|
@ -442,7 +415,6 @@ export default class DataTreeEvaluator {
|
|||
evaluatedTree,
|
||||
{
|
||||
evalProps: this.evalProps,
|
||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||
},
|
||||
this.oldConfigTree,
|
||||
);
|
||||
|
|
@ -1096,7 +1068,6 @@ export default class DataTreeEvaluator {
|
|||
evalPropertyValue,
|
||||
unEvalPropertyValue,
|
||||
evalProps: this.evalProps,
|
||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||
});
|
||||
|
||||
parsedValue = this.getParsedValueForWidgetProperty({
|
||||
|
|
@ -1155,16 +1126,6 @@ export default class DataTreeEvaluator {
|
|||
|
||||
if (!propertyPath) continue;
|
||||
|
||||
const evalPath = getEvalValuePath(fullPropertyPath);
|
||||
setToEvalPathsIdenticalToState({
|
||||
evalPath,
|
||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||
evalProps: this.evalProps,
|
||||
isParsedValueTheSame: true,
|
||||
fullPropertyPath,
|
||||
value: evalPropertyValue,
|
||||
});
|
||||
|
||||
/**
|
||||
* Perf optimization very specific to handling actions
|
||||
* Fields like Api1.data doesn't get evaluated since it is not in dynamicBindingPathList
|
||||
|
|
@ -1206,35 +1167,11 @@ export default class DataTreeEvaluator {
|
|||
? prevEvaluatedValue
|
||||
: evalPropertyValue;
|
||||
|
||||
const evalPath = getEvalValuePath(fullPropertyPath, {
|
||||
isPopulated: true,
|
||||
//what is the purpose of this argument
|
||||
fullPath: true,
|
||||
});
|
||||
|
||||
/** Variables defined in a JS object are not reactive.
|
||||
* Their evaluated values need to be reset only when the variable is modified by the user.
|
||||
* When uneval value of a js variable hasn't changed, it means that the previously evaluated values are in both trees already */
|
||||
if (skipVariableValueAssignment) {
|
||||
setToEvalPathsIdenticalToState({
|
||||
evalPath,
|
||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||
evalProps: this.evalProps,
|
||||
isParsedValueTheSame: true,
|
||||
fullPropertyPath,
|
||||
value: evalValue,
|
||||
});
|
||||
} else {
|
||||
if (!skipVariableValueAssignment) {
|
||||
const valueForSafeTree = klona(evalValue);
|
||||
setToEvalPathsIdenticalToState({
|
||||
evalPath,
|
||||
evalPathsIdenticalToState: this.evalPathsIdenticalToState,
|
||||
evalProps: this.evalProps,
|
||||
isParsedValueTheSame: true,
|
||||
fullPropertyPath,
|
||||
value: valueForSafeTree,
|
||||
});
|
||||
|
||||
set(contextTree, fullPropertyPath, evalValue);
|
||||
set(safeTree, fullPropertyPath, valueForSafeTree);
|
||||
JSObjectCollection.setVariableValue(evalValue, fullPropertyPath);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ import type {
|
|||
WidgetEntityConfig,
|
||||
} from "@appsmith/entities/DataTree/types";
|
||||
import type { ConfigTree } from "entities/DataTree/dataTreeTypes";
|
||||
import { isObject, isUndefined, set } from "lodash";
|
||||
import type { EvaluationError } from "utils/DynamicBindingUtils";
|
||||
import {
|
||||
getEvalValuePath,
|
||||
isPathDynamicTrigger,
|
||||
PropertyEvaluationErrorType,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
|
|
@ -18,47 +16,11 @@ import {
|
|||
resetValidationErrorsForEntityProperty,
|
||||
} from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||
import { validate } from "workers/Evaluation/validations";
|
||||
import type { EvalPathsIdenticalToState, EvalProps } from ".";
|
||||
import type { EvalProps } from ".";
|
||||
import type { ValidationResponse } from "constants/WidgetValidation";
|
||||
|
||||
const LARGE_COLLECTION_SIZE = 100;
|
||||
|
||||
const getIsLargeCollection = (val: any) => {
|
||||
if (!Array.isArray(val)) return false;
|
||||
const rowSize = !isObject(val[0]) ? 1 : Object.keys(val[0]).length;
|
||||
|
||||
const size = val.length * rowSize;
|
||||
|
||||
return size > LARGE_COLLECTION_SIZE;
|
||||
};
|
||||
export function setToEvalPathsIdenticalToState({
|
||||
evalPath,
|
||||
evalPathsIdenticalToState,
|
||||
evalProps,
|
||||
fullPropertyPath,
|
||||
isParsedValueTheSame,
|
||||
value,
|
||||
}: {
|
||||
evalPath: string;
|
||||
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||
evalProps: EvalProps;
|
||||
isParsedValueTheSame: boolean;
|
||||
fullPropertyPath: string;
|
||||
value: unknown;
|
||||
}) {
|
||||
const isLargeCollection = getIsLargeCollection(value);
|
||||
|
||||
if (isParsedValueTheSame && isLargeCollection) {
|
||||
evalPathsIdenticalToState[evalPath] = fullPropertyPath;
|
||||
} else {
|
||||
delete evalPathsIdenticalToState[evalPath];
|
||||
|
||||
set(evalProps, evalPath, value);
|
||||
}
|
||||
}
|
||||
export function validateAndParseWidgetProperty({
|
||||
configTree,
|
||||
evalPathsIdenticalToState,
|
||||
evalPropertyValue,
|
||||
evalProps,
|
||||
fullPropertyPath,
|
||||
|
|
@ -71,7 +33,6 @@ export function validateAndParseWidgetProperty({
|
|||
evalPropertyValue: unknown;
|
||||
unEvalPropertyValue: string;
|
||||
evalProps: EvalProps;
|
||||
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||
}): unknown {
|
||||
const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath);
|
||||
|
||||
|
|
@ -82,26 +43,20 @@ export function validateAndParseWidgetProperty({
|
|||
const widgetConfig = configTree[widget.widgetName] as WidgetEntityConfig;
|
||||
const validation = widgetConfig.validationPaths[propertyPath];
|
||||
|
||||
const { isValid, messages, parsed, transformed } = validateWidgetProperty(
|
||||
const { isValid, messages, parsed } = validateWidgetProperty(
|
||||
validation,
|
||||
evalPropertyValue,
|
||||
widget,
|
||||
propertyPath,
|
||||
);
|
||||
|
||||
let evaluatedValue;
|
||||
|
||||
// remove already present validation errors
|
||||
resetValidationErrorsForEntityProperty({
|
||||
evalProps,
|
||||
fullPropertyPath,
|
||||
});
|
||||
|
||||
if (isValid) {
|
||||
evaluatedValue = parsed;
|
||||
} else {
|
||||
evaluatedValue = isUndefined(transformed) ? evalPropertyValue : transformed;
|
||||
|
||||
if (!isValid) {
|
||||
const evalErrors: EvaluationError[] =
|
||||
messages?.map((message) => {
|
||||
return {
|
||||
|
|
@ -120,21 +75,6 @@ export function validateAndParseWidgetProperty({
|
|||
});
|
||||
}
|
||||
|
||||
const evalPath = getEvalValuePath(fullPropertyPath, {
|
||||
isPopulated: false,
|
||||
fullPath: true,
|
||||
});
|
||||
const isParsedValueTheSame = parsed === evaluatedValue;
|
||||
|
||||
setToEvalPathsIdenticalToState({
|
||||
evalPath,
|
||||
evalPathsIdenticalToState,
|
||||
evalProps,
|
||||
isParsedValueTheSame,
|
||||
fullPropertyPath,
|
||||
value: evaluatedValue,
|
||||
});
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user