fix: Parse js actions for view mode changes (#18357)

* added new function inside parse js actions for view moe

* fixing test cases

* added evaluateSync with isTriggered false

* Add types
Add switch case for diff.event
Add function get back app mode

* Change foreach loops to for of

* Dont return jsUpdates from view mode save resolved since no operation is happening
Change viewModeSaveResolvedFunctionsAndJSUpdates -> viewModeSaveResolvedFunctions to reflect
what it's functionality is

* Refactor JSObject code

* Refactor code for JSobject
* remove any
* export parsed object's type from ast
* create function for deleteResolvedFunctionsAndCurrentJSCollectionState

* Code review changes

* Fix tests

* Change returns in for of loops to continue
Remove try for evaluate sync
Js updates return

* Fix bug

* Fix bug

Co-authored-by: Rimil Dey <rimil@appsmith.com>
Co-authored-by: Aishwarya UR <aishwarya@appsmith.com>
This commit is contained in:
Apeksha Bhosale 2022-12-12 19:45:40 +05:30 committed by GitHub
parent 77a225f4dd
commit efa8b68a47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 545 additions and 179 deletions

View File

@ -139,7 +139,6 @@ describe("generateDataTreeJSAction", () => {
ENTITY_TYPE: "JSACTION",
body:
"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t},\n\tmyFun2: async () => {\n\t\t//use async-await or promises\n\t}\n}",
myFun2: {
data: {
users: [{ id: 1, name: "John" }],
@ -159,11 +158,13 @@ describe("generateDataTreeJSAction", () => {
arguments: [],
isAsync: true,
confirmBeforeExecute: false,
body: "async () => {\n\t\t//use async-await or promises\n\t}",
},
myFun1: {
arguments: [],
isAsync: false,
confirmBeforeExecute: false,
body: "() => {\n\t\t//write code here\n\t}",
},
},
bindingPaths: {
@ -350,11 +351,13 @@ describe("generateDataTreeJSAction", () => {
arguments: [],
isAsync: true,
confirmBeforeExecute: false,
body: "async () => {\n\t\t//use async-await or promises\n\t}",
},
myFun1: {
arguments: [],
isAsync: false,
confirmBeforeExecute: false,
body: "() => {\n\t\t//write code here\n\t}",
},
},
bindingPaths: {

View File

@ -44,6 +44,7 @@ export const generateDataTreeJSAction = (
arguments: action.actionConfiguration.jsArguments,
isAsync: action.actionConfiguration.isAsync,
confirmBeforeExecute: !!action.confirmBeforeExecute,
body: action.actionConfiguration.body,
};
bindingPaths[action.name] = EvaluationSubstitutionType.SMART_SUBSTITUTE;
dynamicBindingPathList.push({ key: action.name });

View File

@ -66,6 +66,7 @@ export interface MetaArgs {
arguments: Variable[];
isAsync: boolean;
confirmBeforeExecute: boolean;
body: string;
}
export interface JSActionEntityConfig {

View File

@ -2,7 +2,7 @@ import { DataTree, DataTreeJSAction } from "entities/DataTree/dataTreeFactory";
import { isEmpty, set } from "lodash";
import { EvalErrorTypes } from "utils/DynamicBindingUtils";
import { JSUpdate, ParsedJSSubAction } from "utils/JSPaneUtils";
import { isTypeOfFunction, parseJSObjectWithAST } from "@shared/ast";
import { NodeTypes, parseJSObjectWithAST, JsObjectProperty } from "@shared/ast";
import DataTreeEvaluator from "workers/common/DataTreeEvaluator";
import evaluateSync, { isFunctionAsync } from "workers/Evaluation/evaluate";
import {
@ -16,6 +16,19 @@ import {
updateJSCollectionInUnEvalTree,
} from "workers/Evaluation/JSObject/utils";
type Actions = {
name: string;
body: string;
arguments: Array<{ key: string; value: unknown }>;
parsedFunction: any;
isAsync: boolean;
};
type Variables = {
name: string;
value: string;
};
/**
* Here we update our unEvalTree according to the change in JSObject's body
*
@ -33,7 +46,7 @@ export const getUpdatedLocalUnEvalTreeAfterJSUpdates = (
const parsedBody = jsUpdates[jsEntity].parsedBody;
if (isJSAction(entity)) {
if (!!parsedBody) {
//add/delete/update functions from dataTree
// add/delete/update functions from dataTree
localUnEvalTree = updateJSCollectionInUnEvalTree(
parsedBody,
entity,
@ -54,6 +67,110 @@ export const getUpdatedLocalUnEvalTreeAfterJSUpdates = (
const regex = new RegExp(/^export default[\s]*?({[\s\S]*?})/);
function deleteResolvedFunctionsAndCurrentJSCollectionState(
dataTreeEvalRef: DataTreeEvaluator,
entityName: string,
) {
delete dataTreeEvalRef.resolvedFunctions[entityName];
delete dataTreeEvalRef.currentJSCollectionState[entityName];
}
function parseFunction(
parsedElement: JsObjectProperty,
unEvalDataTree: DataTree,
dataTreeEvalRef: DataTreeEvaluator,
entityName: string,
actions: Actions[],
) {
const { result } = evaluateSync(
parsedElement.value,
unEvalDataTree,
{},
true,
undefined,
undefined,
true,
);
if (!!result) {
let params: Array<{ key: string; value: unknown }> = [];
if (parsedElement.arguments) {
params = parsedElement.arguments.map(({ defaultValue, paramName }) => ({
key: paramName,
value: defaultValue,
}));
}
const functionString: string = parsedElement.value;
set(
dataTreeEvalRef.resolvedFunctions,
`${entityName}.${parsedElement.key}`,
result,
);
set(
dataTreeEvalRef.currentJSCollectionState,
`${entityName}.${parsedElement.key}`,
functionString,
);
actions.push({
name: parsedElement.key,
body: functionString,
arguments: params,
parsedFunction: result,
isAsync: false,
});
}
}
function parseVariables(
variables: Variables[],
parsedElement: JsObjectProperty,
dataTreeEvalRef: DataTreeEvaluator,
entityName: string,
) {
variables.push({
name: parsedElement.key,
value: parsedElement.value,
});
set(
dataTreeEvalRef.currentJSCollectionState,
`${entityName}.${parsedElement.key}`,
parsedElement.value,
);
}
function getParsedBody(
parsedObject: Array<JsObjectProperty>,
unEvalDataTree: DataTree,
dataTreeEvalRef: DataTreeEvaluator,
entityName: string,
) {
const actions: Actions[] = [];
const variables: Variables[] = [];
for (const parsedElement of parsedObject) {
switch (parsedElement.type) {
case "literal":
continue;
case NodeTypes.ArrowFunctionExpression:
case NodeTypes.FunctionExpression:
parseFunction(
parsedElement,
unEvalDataTree,
dataTreeEvalRef,
entityName,
actions,
);
break;
default:
parseVariables(variables, parsedElement, dataTreeEvalRef, entityName);
}
}
return {
actions,
variables,
};
}
/**
* Here we parse the JSObject and then determine
* 1. it's nature : async or sync
@ -62,24 +179,26 @@ const regex = new RegExp(/^export default[\s]*?({[\s\S]*?})/);
*
* @param dataTreeEvalRef
* @param entity
* @param jsUpdates
* @param unEvalDataTree
* @param entityName
* @param jsUpdates
* @returns
*/
export function saveResolvedFunctionsAndJSUpdates(
dataTreeEvalRef: DataTreeEvaluator,
entity: DataTreeJSAction,
jsUpdates: Record<string, JSUpdate>,
unEvalDataTree: DataTree,
entityName: string,
jsUpdates: Record<string, JSUpdate>,
) {
const correctFormat = regex.test(entity.body);
if (correctFormat) {
const body = entity.body.replace(/export default/g, "");
try {
delete dataTreeEvalRef.resolvedFunctions[`${entityName}`];
delete dataTreeEvalRef.currentJSCollectionState[`${entityName}`];
deleteResolvedFunctionsAndCurrentJSCollectionState(
dataTreeEvalRef,
entityName,
);
const parseStartTime = performance.now();
const parsedObject = parseJSObjectWithAST(body);
const parseEndTime = performance.now();
@ -88,82 +207,23 @@ export function saveResolvedFunctionsAndJSUpdates(
JSObjectName: entityName,
JSObjectASTParseTime,
});
const actions: any = [];
const variables: any = [];
if (!!parsedObject) {
parsedObject.forEach((parsedElement) => {
if (isTypeOfFunction(parsedElement.type)) {
try {
const { result } = evaluateSync(
parsedElement.value,
unEvalDataTree,
{},
false,
undefined,
undefined,
true,
);
if (!!result) {
let params: Array<{ key: string; value: unknown }> = [];
if (parsedElement.arguments) {
params = parsedElement.arguments.map(
({ defaultValue, paramName }) => ({
key: paramName,
value: defaultValue,
}),
);
}
const functionString = parsedElement.value;
set(
dataTreeEvalRef.resolvedFunctions,
`${entityName}.${parsedElement.key}`,
result,
);
set(
dataTreeEvalRef.currentJSCollectionState,
`${entityName}.${parsedElement.key}`,
functionString,
);
actions.push({
name: parsedElement.key,
body: functionString,
arguments: params,
parsedFunction: result,
isAsync: false,
});
}
} catch {
// in case we need to handle error state
}
} else if (parsedElement.type !== "literal") {
variables.push({
name: parsedElement.key,
value: parsedElement.value,
});
set(
dataTreeEvalRef.currentJSCollectionState,
`${entityName}.${parsedElement.key}`,
parsedElement.value,
);
const parsedBody = !!parsedObject
? {
body: entity.body,
...getParsedBody(
parsedObject,
unEvalDataTree,
dataTreeEvalRef,
entityName,
),
}
});
const parsedBody = {
body: entity.body,
actions: actions,
variables,
};
set(jsUpdates, `${entityName}`, {
parsedBody,
id: entity.actionId,
});
} else {
set(jsUpdates, `${entityName}`, {
parsedBody: undefined,
id: entity.actionId,
});
}
: undefined;
set(jsUpdates, `${entityName}`, {
parsedBody,
id: entity.actionId,
});
} catch (e) {
//if we need to push error as popup in case
}
@ -181,74 +241,152 @@ export function saveResolvedFunctionsAndJSUpdates(
return jsUpdates;
}
export function viewModeSaveResolvedFunctions(
dataTreeEvalRef: DataTreeEvaluator,
entity: DataTreeJSAction,
unEvalDataTree: DataTree,
entityName: string,
) {
try {
deleteResolvedFunctionsAndCurrentJSCollectionState(
dataTreeEvalRef,
entityName,
);
const jsActions = entity.meta;
const jsActionList = Object.keys(jsActions);
for (const jsAction of jsActionList) {
const { result } = evaluateSync(
jsActions[jsAction].body,
unEvalDataTree,
{},
false,
undefined,
undefined,
true,
);
if (!!result) {
const functionString = jsActions[jsAction].body;
set(
dataTreeEvalRef.resolvedFunctions,
`${entityName}.${jsAction}`,
result,
);
set(
dataTreeEvalRef.currentJSCollectionState,
`${entityName}.${jsAction}`,
functionString,
);
}
}
} catch (e) {
//if we need to push error as popup in case
}
}
export function parseJSActionsWithDifferences(
dataTreeEvalRef: DataTreeEvaluator,
unEvalDataTree: DataTree,
differences: DataTreeDiff[],
) {
let jsUpdates: Record<string, JSUpdate> = {};
for (const diff of differences) {
const payLoadPropertyPath = diff.payload.propertyPath;
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
payLoadPropertyPath,
);
const entity = unEvalDataTree[entityName];
if (!isJSAction(entity)) {
continue;
}
switch (diff.event) {
case DataTreeDiffEvent.DELETE:
// when JSObject is deleted, we remove it from currentJSCollectionState & resolvedFunctions
deleteResolvedFunctionsAndCurrentJSCollectionState(
dataTreeEvalRef,
payLoadPropertyPath,
);
break;
case DataTreeDiffEvent.EDIT:
if (propertyPath === "body") {
jsUpdates = saveResolvedFunctionsAndJSUpdates(
dataTreeEvalRef,
entity,
unEvalDataTree,
entityName,
jsUpdates,
);
}
break;
case DataTreeDiffEvent.NEW:
if (propertyPath === "") {
jsUpdates = saveResolvedFunctionsAndJSUpdates(
dataTreeEvalRef,
entity,
unEvalDataTree,
entityName,
jsUpdates,
);
}
break;
}
}
return parseJSUpdates(jsUpdates, unEvalDataTree, dataTreeEvalRef);
}
export function parseJSActionsForViewMode(
dataTreeEvalRef: DataTreeEvaluator,
unEvalDataTree: DataTree,
) {
const unEvalDataTreeKeys = Object.keys(unEvalDataTree);
for (const entityName of unEvalDataTreeKeys) {
const entity = unEvalDataTree[entityName];
if (!isJSAction(entity)) {
continue;
}
viewModeSaveResolvedFunctions(
dataTreeEvalRef,
entity,
unEvalDataTree,
entityName,
);
}
}
export function parseJSActions(
dataTreeEvalRef: DataTreeEvaluator,
unEvalDataTree: DataTree,
differences?: DataTreeDiff[],
oldUnEvalTree?: DataTree,
) {
let jsUpdates: Record<string, JSUpdate> = {};
if (!!differences && !!oldUnEvalTree) {
differences.forEach((diff) => {
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
diff.payload.propertyPath,
);
const entity = unEvalDataTree[entityName];
if (!isJSAction(entity)) {
return false;
}
if (diff.event === DataTreeDiffEvent.DELETE) {
// when JSObject is deleted, we remove it from currentJSCollectionState & resolvedFunctions
if (
dataTreeEvalRef.currentJSCollectionState &&
dataTreeEvalRef.currentJSCollectionState[diff.payload.propertyPath]
) {
delete dataTreeEvalRef.currentJSCollectionState[
diff.payload.propertyPath
];
}
if (
dataTreeEvalRef.resolvedFunctions &&
dataTreeEvalRef.resolvedFunctions[diff.payload.propertyPath]
) {
delete dataTreeEvalRef.resolvedFunctions[diff.payload.propertyPath];
}
}
if (
(diff.event === DataTreeDiffEvent.EDIT && propertyPath === "body") ||
(diff.event === DataTreeDiffEvent.NEW && propertyPath === "")
) {
jsUpdates = saveResolvedFunctionsAndJSUpdates(
dataTreeEvalRef,
entity,
jsUpdates,
unEvalDataTree,
entityName,
);
}
});
} else {
Object.keys(unEvalDataTree).forEach((entityName) => {
const entity = unEvalDataTree[entityName];
if (!isJSAction(entity)) {
return;
}
jsUpdates = saveResolvedFunctionsAndJSUpdates(
dataTreeEvalRef,
entity,
jsUpdates,
unEvalDataTree,
entityName,
);
});
const unEvalDataTreeKeys = Object.keys(unEvalDataTree);
for (const entityName of unEvalDataTreeKeys) {
const entity = unEvalDataTree[entityName];
if (!isJSAction(entity)) {
continue;
}
jsUpdates = saveResolvedFunctionsAndJSUpdates(
dataTreeEvalRef,
entity,
unEvalDataTree,
entityName,
jsUpdates,
);
}
return parseJSUpdates(jsUpdates, unEvalDataTree, dataTreeEvalRef);
}
Object.keys(jsUpdates).forEach((entityName) => {
export function parseJSUpdates(
jsUpdates: Record<string, JSUpdate>,
unEvalDataTree: DataTree,
dataTreeEvalRef: DataTreeEvaluator,
) {
const jsUpdateKeys = Object.keys(jsUpdates);
for (const entityName of jsUpdateKeys) {
const parsedBody = jsUpdates[entityName].parsedBody;
if (!parsedBody) return;
if (!parsedBody) continue;
parsedBody.actions = parsedBody.actions.map((action) => {
return {
...action,
@ -262,17 +400,18 @@ export function parseJSActions(
parsedFunction: undefined,
} as ParsedJSSubAction;
});
});
return { jsUpdates };
}
return jsUpdates;
}
export function getJSEntities(dataTree: DataTree) {
const jsCollections: Record<string, DataTreeJSAction> = {};
Object.keys(dataTree).forEach((key: string) => {
const dataTreeKeys = Object.keys(dataTree);
for (const key of dataTreeKeys) {
const entity = dataTree[key];
if (isJSAction(entity)) {
jsCollections[entity.name] = entity;
}
});
}
return jsCollections;
}

View File

@ -1,11 +1,13 @@
import {
DataTree,
DataTreeAppsmith,
DataTreeJSAction,
EvaluationSubstitutionType,
} from "entities/DataTree/dataTreeFactory";
import { ParsedBody, ParsedJSSubAction } from "utils/JSPaneUtils";
import { unset, set, get } from "lodash";
import { isJSAction } from "workers/Evaluation/evaluationUtils";
import { APP_MODE } from "../../../entities/App";
/**
* here we add/remove the properties (variables and actions) which got added/removed from the JSObject parsedBody.
@ -72,6 +74,7 @@ export const updateJSCollectionInUnEvalTree = (
arguments: action.arguments,
isAsync: false,
confirmBeforeExecute: false,
body: action.body,
};
const data = get(
@ -238,3 +241,8 @@ export function isJSObjectFunction(
}
return false;
}
export function getAppMode(dataTree: DataTree) {
const appsmithObj = dataTree.appsmith as DataTreeAppsmith;
return appsmithObj.mode as APP_MODE;
}

View File

@ -346,6 +346,14 @@ describe("DataTreeEvaluator", () => {
{},
);
const unEvalTree: UnEvalTree = {
appsmith: {
mode: "EDIT",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
ENTITY_TYPE: ENTITY_TYPE.APPSMITH,
store: {},
theme: {},
},
Text1: generateDataTreeWidget(
{
...BASE_WIDGET,

View File

@ -28,6 +28,7 @@ import {
convertPathToString,
CrashingError,
DataTreeDiff,
getAllPaths,
getEntityNameAndPropertyPath,
getImmediateParentsOfPropertyPaths,
isAction,
@ -38,7 +39,6 @@ import {
translateDiffEventToDataTreeDiffEvent,
trimDependantChangePaths,
overrideWidgetProperties,
getAllPaths,
isValidEntity,
} from "workers/Evaluation/evaluationUtils";
import {
@ -63,14 +63,14 @@ import {
import { DATA_BIND_REGEX } from "constants/BindingsConstants";
import evaluateSync, {
EvalResult,
EvaluateContext,
evaluateAsync,
EvaluateContext,
} from "workers/Evaluation/evaluate";
import { substituteDynamicBindingWithValues } from "workers/Evaluation/evaluationSubstitution";
import {
ENTITY_TYPE as CONSOLE_ENTITY_TYPE,
Severity,
SourceEntity,
ENTITY_TYPE as CONSOLE_ENTITY_TYPE,
UserLogObject,
} from "entities/AppsmithConsole";
import { error as logError } from "loglevel";
@ -83,21 +83,27 @@ import {
import { klona } from "klona/full";
import { EvalMetaUpdates } from "./types";
import {
updateDependencyMap,
createDependencyMap,
updateDependencyMap,
} from "workers/common/DependencyMap";
import {
getJSEntities,
getUpdatedLocalUnEvalTreeAfterJSUpdates,
parseJSActionsForViewMode,
parseJSActions,
parseJSActionsWithDifferences,
} from "workers/Evaluation/JSObject";
import { getFixedTimeDifference } from "./utils";
import { isJSObjectFunction } from "workers/Evaluation/JSObject/utils";
import {
getAppMode,
isJSObjectFunction,
} from "workers/Evaluation/JSObject/utils";
import {
getValidatedTree,
validateActionProperty,
validateAndParseWidgetProperty,
} from "./validationUtils";
import { APP_MODE } from "../../../entities/App";
type SortedDependencies = Array<string>;
@ -142,6 +148,10 @@ export default class DataTreeEvaluator {
sortedValidationDependencies: SortedDependencies = [];
inverseValidationDependencyMap: DependencyMap = {};
public hasCyclicalDependency = false;
parseJsActionsConfig = {
[APP_MODE.EDIT]: parseJSActions,
[APP_MODE.PUBLISHED]: parseJSActionsForViewMode,
};
constructor(
widgetConfigMap: WidgetTypeConfigMap,
allActionValidationConfig?: {
@ -190,8 +200,9 @@ export default class DataTreeEvaluator {
//save current state of js collection action and variables to be added to uneval tree
//save functions in resolveFunctions (as functions) to be executed as functions are not allowed in evalTree
//and functions are saved in dataTree as strings
const parsedCollections = parseJSActions(this, localUnEvalTree);
jsUpdates = parsedCollections.jsUpdates;
const currentAppMode: APP_MODE = getAppMode(localUnEvalTree);
jsUpdates =
this.parseJsActionsConfig[currentAppMode](this, localUnEvalTree) || {};
localUnEvalTree = getUpdatedLocalUnEvalTreeAfterJSUpdates(
jsUpdates,
localUnEvalTree,
@ -363,18 +374,18 @@ export default class DataTreeEvaluator {
translateDiffEventToDataTreeDiffEvent(diff, localUnEvalTree),
),
);
//save parsed functions in resolveJSFunctions, update current state of js collection
const parsedCollections = parseJSActions(
this,
localUnEvalTree,
jsTranslatedDiffs,
this.oldUnEvalTree,
);
jsUpdates = parsedCollections.jsUpdates;
//update local data tree if js body has updated (remove/update/add js functions or variables)
// save parsed functions in resolveJSFunctions, update current state of js collection
jsUpdates =
(!!jsTranslatedDiffs && !!this.oldUnEvalTree
? parseJSActionsWithDifferences(
this,
localUnEvalTree,
jsTranslatedDiffs,
)
: parseJSActions(this, localUnEvalTree)) || {};
// update local data tree if js body has updated (remove/update/add js functions or variables)
localUnEvalTree = getUpdatedLocalUnEvalTreeAfterJSUpdates(
jsUpdates,
jsUpdates || {},
localUnEvalTree,
);

View File

@ -5,6 +5,7 @@ import {
DataTreeAction,
DataTreeWidget,
ENTITY_TYPE,
DataTreeAppsmith,
} from "entities/DataTree/dataTreeFactory";
export const arrayAccessorCyclicDependency: Record<string, DataTree> = {
@ -210,6 +211,71 @@ export const arrayAccessorCyclicDependency: Record<string, DataTree> = {
privateWidgets: {},
meta: {},
} as unknown) as DataTreeWidget,
appsmith: ({
user: {
email: "anand@appsmith.com",
workspaceIds: [
"61431979a67ce2289d3c7c6d",
"61431a95a67ce2289d3c7c74",
"5f7add8687af934ed846dd6a",
"5f9fd13993794869fdbb8dcb",
"618b5af5da7cd651ee273112",
"604ef1c5c046f668d7bcc051",
"61b3389cd3e4214454c26bd1",
"61b3389cd3e4214454c26bd2",
"620a0d896b4b1e154a3c057a",
"620b37296b4b1e154a3c1fd7",
"60c1a5273535574772b6377b",
"6066e71a034ece74b1481ad2",
"623b36e34d9aea1b062b15b3",
"623b37de4d9aea1b062b170f",
"624fe51b457aa64da9e02ed3",
"6176537b515e45415cc7fd15",
"6206486d6b4b1e154a3be208",
],
username: "anand@appsmith.com",
name: "Anand Srinivasan",
enableTelemetry: true,
idToken: {
sub: "109879730040206968321",
email_verified: true,
name: "Anand Srinivasan",
given_name: "Anand",
locale: "en",
hd: "appsmith.com",
family_name: "Srinivasan",
picture:
"https://lh3.googleusercontent.com/a-/AOh14Gi4HfYY0sKhaG93YAHB_E5-dL4BkFxdf8ZfQ2w7=s96-c",
email: "anand@appsmith.com",
},
accountNonExpired: true,
accountNonLocked: true,
credentialsNonExpired: true,
emptyInstance: false,
isAnonymous: false,
isEnabled: true,
isSuperUser: false,
isConfigurable: true,
},
URL: {
fullPath:
"https://app.appsmith.com/app/untitled-application-25/page1-6272179d8a368d6f1efcd0d2/edit",
host: "app.appsmith.com",
hostname: "app.appsmith.com",
queryParams: {},
protocol: "https:",
pathname:
"/app/untitled-application-25/page1-6272179d8a368d6f1efcd0d2/edit",
port: "",
hash: "",
},
store: {},
geolocation: {
canBeRequested: true,
},
mode: "EDIT",
ENTITY_TYPE: ENTITY_TYPE.APPSMITH,
} as unknown) as DataTreeAppsmith,
},
apiSuccessUnEvalTree: {
// success: response -> [{...}, {...}, {...}]

View File

@ -5,6 +5,7 @@ import {
DataTreeAction,
DataTreeWidget,
ENTITY_TYPE,
DataTreeAppsmith,
} from "entities/DataTree/dataTreeFactory";
export const nestedArrayAccessorCyclicDependency: Record<string, DataTree> = {
@ -210,6 +211,53 @@ export const nestedArrayAccessorCyclicDependency: Record<string, DataTree> = {
privateWidgets: {},
meta: {},
} as unknown) as DataTreeWidget,
appsmith: ({
user: {
email: "rathod@appsmith.com",
workspaceIds: [
"6218a61972ccd9145ec78c57",
"621913df0276eb01d22fec44",
"60caf8edb1e47a1315f0c48f",
"609114fe05c4d35a9f6cbbf2",
],
username: "rathod@appsmith.com",
name: "Rishabh",
commentOnboardingState: "RESOLVED",
role: "engineer",
useCase: "personal project",
enableTelemetry: false,
emptyInstance: false,
accountNonExpired: true,
accountNonLocked: true,
credentialsNonExpired: true,
isAnonymous: false,
isEnabled: true,
isSuperUser: false,
isConfigurable: true,
},
URL: {
fullPath:
"https://dev.appsmith.com/applications/6200d1a2b5bfc0392b959cab/pages/6220c268c48234070f8ac65a/edit?a=b",
host: "dev.appsmith.com",
hostname: "dev.appsmith.com",
queryParams: {
a: "b",
},
protocol: "https:",
pathname:
"/applications/6200d1a2b5bfc0392b959cab/pages/6220c268c48234070f8ac65a/edit",
port: "",
hash: "",
},
store: {
textColor: "#DF7E65",
},
geolocation: {
canBeRequested: true,
},
mode: "EDIT",
ENTITY_TYPE: ENTITY_TYPE.APPSMITH,
} as unknown) as DataTreeAppsmith,
},
apiSuccessUnEvalTree: {
// success: response -> [ [{...}, {...}, {...}], [{...}, {...}, {...}], [{...}, {...}, {...}] ]

View File

@ -388,6 +388,7 @@ export const asyncTagUnevalTree: DataTree = {
arguments: [],
isAsync: false,
confirmBeforeExecute: false,
body: "myFun1: () => {\n\t\treturn JSObject2.callApi();\n\t}",
},
},
bindingPaths: {
@ -426,6 +427,7 @@ export const asyncTagUnevalTree: DataTree = {
arguments: [],
isAsync: false,
confirmBeforeExecute: false,
body: "callApi: () => {\n\t\treturn Api1.run()\n\t}",
},
},
bindingPaths: {
@ -1419,3 +1421,71 @@ export const unEvalTreeWidgetSelectWidget = {
isDirty: false,
},
};
export const emptyTreeWithAppsmithObject = ({
appsmith: ({
user: {
email: "anand@appsmith.com",
workspaceIds: [
"61431979a67ce2289d3c7c6d",
"61431a95a67ce2289d3c7c74",
"5f7add8687af934ed846dd6a",
"5f9fd13993794869fdbb8dcb",
"618b5af5da7cd651ee273112",
"604ef1c5c046f668d7bcc051",
"61b3389cd3e4214454c26bd1",
"61b3389cd3e4214454c26bd2",
"620a0d896b4b1e154a3c057a",
"620b37296b4b1e154a3c1fd7",
"60c1a5273535574772b6377b",
"6066e71a034ece74b1481ad2",
"623b36e34d9aea1b062b15b3",
"623b37de4d9aea1b062b170f",
"624fe51b457aa64da9e02ed3",
"6176537b515e45415cc7fd15",
"6206486d6b4b1e154a3be208",
],
username: "anand@appsmith.com",
name: "Anand Srinivasan",
enableTelemetry: true,
idToken: {
sub: "109879730040206968321",
email_verified: true,
name: "Anand Srinivasan",
given_name: "Anand",
locale: "en",
hd: "appsmith.com",
family_name: "Srinivasan",
picture:
"https://lh3.googleusercontent.com/a-/AOh14Gi4HfYY0sKhaG93YAHB_E5-dL4BkFxdf8ZfQ2w7=s96-c",
email: "anand@appsmith.com",
},
accountNonExpired: true,
accountNonLocked: true,
credentialsNonExpired: true,
emptyInstance: false,
isAnonymous: false,
isEnabled: true,
isSuperUser: false,
isConfigurable: true,
},
URL: {
fullPath:
"https://app.appsmith.com/app/untitled-application-25/page1-6272179d8a368d6f1efcd0d2/edit",
host: "app.appsmith.com",
hostname: "app.appsmith.com",
queryParams: {},
protocol: "https:",
pathname:
"/app/untitled-application-25/page1-6272179d8a368d6f1efcd0d2/edit",
port: "",
hash: "",
},
store: {},
geolocation: {
canBeRequested: true,
},
mode: "EDIT",
ENTITY_TYPE: ENTITY_TYPE.APPSMITH,
} as unknown) as DataTreeAppsmith,
} as unknown) as DataTree;

View File

@ -1,17 +1,22 @@
import DataTreeEvaluator from ".";
import {
asyncTagUnevalTree,
emptyTreeWithAppsmithObject,
lintingUnEvalTree,
unEvalTree,
} from "./mockData/mockUnEvalTree";
import { DataTree } from "entities/DataTree/dataTreeFactory";
import { DataTreeDiff } from "workers/Evaluation/evaluationUtils";
import {
DataTreeDiff,
DataTreeDiffEvent,
} from "workers/Evaluation/evaluationUtils";
import { ALL_WIDGETS_AND_CONFIG } from "utils/WidgetRegistry";
import { arrayAccessorCyclicDependency } from "./mockData/ArrayAccessorTree";
import { nestedArrayAccessorCyclicDependency } from "./mockData/NestedArrayAccessorTree";
import { updateDependencyMap } from "workers/common/DependencyMap";
import { parseJSActions } from "workers/Evaluation/JSObject";
import { WidgetConfiguration } from "widgets/constants";
import { JSUpdate, ParsedBody } from "../../../utils/JSPaneUtils";
const widgetConfigMap: Record<
string,
@ -21,6 +26,7 @@ const widgetConfigMap: Record<
metaProperties: WidgetConfiguration["properties"]["meta"];
}
> = {};
ALL_WIDGETS_AND_CONFIG.map(([, config]) => {
if (config.type && config.properties) {
widgetConfigMap[config.type] = {
@ -209,17 +215,21 @@ describe("DataTreeEvaluator", () => {
describe("parseJsActions", () => {
beforeEach(() => {
dataTreeEvaluator.setupFirstTree(({} as unknown) as DataTree);
dataTreeEvaluator.setupFirstTree(
(emptyTreeWithAppsmithObject as unknown) as DataTree,
);
dataTreeEvaluator.evalAndValidateFirstTree();
});
it("set's isAsync tag for cross JsObject references", () => {
const result = parseJSActions(dataTreeEvaluator, asyncTagUnevalTree);
expect(
result.jsUpdates["JSObject1"]?.parsedBody?.actions[0].isAsync,
).toBe(true);
expect(
result.jsUpdates["JSObject2"]?.parsedBody?.actions[0].isAsync,
).toBe(true);
const jsUpdatesForJsObject1 = (result as Record<string, JSUpdate>)[
"JSObject1"
].parsedBody as ParsedBody;
const jsUpdatesForJsObject2 = (result as Record<string, JSUpdate>)[
"JSObject2"
].parsedBody as ParsedBody;
expect(jsUpdatesForJsObject1.actions[0].isAsync).toBe(true);
expect(jsUpdatesForJsObject2.actions[0].isAsync).toBe(true);
});
});

View File

@ -21,14 +21,15 @@ import {
import { ECMA_VERSION, SourceType, NodeTypes } from "./src/constants";
// JSObjects
import { parseJSObjectWithAST } from "./src/jsObject";
import { parseJSObjectWithAST, JsObjectProperty } from "./src/jsObject";
// types or intefaces should be exported with type keyword, while enums can be exported like normal functions
// types or interfaces should be exported with type keyword, while enums can be exported like normal functions
export type {
ObjectExpression,
PropertyNode,
MemberExpressionData,
IdentifierInfo,
JsObjectProperty,
};
export {

View File

@ -11,7 +11,7 @@ import {
functionParam,
} from "../index";
type JsObjectProperty = {
export type JsObjectProperty = {
key: string;
value: string;
type: string;