fix: Saving variables as string rather than values in backend (#12999)
* parsing of js object has changed * added test for parsing * parse object with literal * added variable to reactive paths and solved some PR comments * test fixed * small fix * removed restricting functions to be added if object body has errors Co-authored-by: Aishwarya UR <aishwarya@appsmith.com>
This commit is contained in:
parent
b4347b44e1
commit
7fa4df32a1
|
|
@ -158,11 +158,19 @@ describe("generateDataTreeJSAction", () => {
|
||||||
body: "SMART_SUBSTITUTE",
|
body: "SMART_SUBSTITUTE",
|
||||||
myFun2: "SMART_SUBSTITUTE",
|
myFun2: "SMART_SUBSTITUTE",
|
||||||
myFun1: "SMART_SUBSTITUTE",
|
myFun1: "SMART_SUBSTITUTE",
|
||||||
|
myVar1: "SMART_SUBSTITUTE",
|
||||||
|
myVar2: "SMART_SUBSTITUTE",
|
||||||
},
|
},
|
||||||
dynamicBindingPathList: [
|
dynamicBindingPathList: [
|
||||||
{
|
{
|
||||||
key: "body",
|
key: "body",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "myVar1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myVar2",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "myFun2",
|
key: "myFun2",
|
||||||
},
|
},
|
||||||
|
|
@ -186,6 +194,8 @@ describe("generateDataTreeJSAction", () => {
|
||||||
body: "SMART_SUBSTITUTE",
|
body: "SMART_SUBSTITUTE",
|
||||||
myFun1: "SMART_SUBSTITUTE",
|
myFun1: "SMART_SUBSTITUTE",
|
||||||
myFun2: "SMART_SUBSTITUTE",
|
myFun2: "SMART_SUBSTITUTE",
|
||||||
|
myVar1: "SMART_SUBSTITUTE",
|
||||||
|
myVar2: "SMART_SUBSTITUTE",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const result = generateDataTreeJSAction(jsCollection);
|
const result = generateDataTreeJSAction(jsCollection);
|
||||||
|
|
@ -347,11 +357,19 @@ describe("generateDataTreeJSAction", () => {
|
||||||
body: "SMART_SUBSTITUTE",
|
body: "SMART_SUBSTITUTE",
|
||||||
myFun2: "SMART_SUBSTITUTE",
|
myFun2: "SMART_SUBSTITUTE",
|
||||||
myFun1: "SMART_SUBSTITUTE",
|
myFun1: "SMART_SUBSTITUTE",
|
||||||
|
myVar1: "SMART_SUBSTITUTE",
|
||||||
|
myVar2: "SMART_SUBSTITUTE",
|
||||||
},
|
},
|
||||||
dynamicBindingPathList: [
|
dynamicBindingPathList: [
|
||||||
{
|
{
|
||||||
key: "body",
|
key: "body",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "myVar1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myVar2",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "myFun2",
|
key: "myFun2",
|
||||||
},
|
},
|
||||||
|
|
@ -375,6 +393,8 @@ describe("generateDataTreeJSAction", () => {
|
||||||
body: "SMART_SUBSTITUTE",
|
body: "SMART_SUBSTITUTE",
|
||||||
myFun1: "SMART_SUBSTITUTE",
|
myFun1: "SMART_SUBSTITUTE",
|
||||||
myFun2: "SMART_SUBSTITUTE",
|
myFun2: "SMART_SUBSTITUTE",
|
||||||
|
myVar1: "SMART_SUBSTITUTE",
|
||||||
|
myVar2: "SMART_SUBSTITUTE",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ export const generateDataTreeJSAction = (
|
||||||
const variable = variables[i];
|
const variable = variables[i];
|
||||||
variableList[variable.name] = variable.value;
|
variableList[variable.name] = variable.value;
|
||||||
listVariables.push(variable.name);
|
listVariables.push(variable.name);
|
||||||
|
dynamicBindingPathList.push({ key: variable.name });
|
||||||
|
bindingPaths[variable.name] = EvaluationSubstitutionType.SMART_SUBSTITUTE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const dependencyMap: DependencyMap = {};
|
const dependencyMap: DependencyMap = {};
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,21 @@ export const getDifferenceInJSCollection = (
|
||||||
} else {
|
} else {
|
||||||
changedVariables = jsAction.variables;
|
changedVariables = jsAction.variables;
|
||||||
}
|
}
|
||||||
|
//delete variable
|
||||||
|
if (varList && varList.length > 0 && parsedBody.variables) {
|
||||||
|
for (let i = 0; i < varList.length; i++) {
|
||||||
|
const preVar = varList[i];
|
||||||
|
const existed = parsedBody.variables.find(
|
||||||
|
(jsVar: Variable) => jsVar.name === preVar.name,
|
||||||
|
);
|
||||||
|
if (!existed) {
|
||||||
|
const newvarList = varList.filter(
|
||||||
|
(deletedVar) => deletedVar.name !== preVar.name,
|
||||||
|
);
|
||||||
|
changedVariables = changedVariables.concat(newvarList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
newActions: toBeAddedActions,
|
newActions: toBeAddedActions,
|
||||||
updateActions: toBeUpdatedActions,
|
updateActions: toBeUpdatedActions,
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,9 @@ import {
|
||||||
ActionValidationConfigMap,
|
ActionValidationConfigMap,
|
||||||
ValidationConfig,
|
ValidationConfig,
|
||||||
} from "constants/PropertyControlConstants";
|
} from "constants/PropertyControlConstants";
|
||||||
|
import { parseJSObjectWithAST } from "workers/ast";
|
||||||
import { klona } from "klona/full";
|
import { klona } from "klona/full";
|
||||||
import { EvalMetaUpdates } from "./types";
|
import { EvalMetaUpdates } from "./types";
|
||||||
|
|
||||||
export default class DataTreeEvaluator {
|
export default class DataTreeEvaluator {
|
||||||
dependencyMap: DependencyMap = {};
|
dependencyMap: DependencyMap = {};
|
||||||
sortedDependencies: Array<string> = [];
|
sortedDependencies: Array<string> = [];
|
||||||
|
|
@ -573,8 +573,13 @@ export default class DataTreeEvaluator {
|
||||||
Object.keys(entity.reactivePaths).forEach((propertyPath) => {
|
Object.keys(entity.reactivePaths).forEach((propertyPath) => {
|
||||||
const existingDeps =
|
const existingDeps =
|
||||||
dependencies[`${entityName}.${propertyPath}`] || [];
|
dependencies[`${entityName}.${propertyPath}`] || [];
|
||||||
const unevalPropValue = _.get(entity, propertyPath).toString();
|
const unevalPropValue = _.get(entity, propertyPath);
|
||||||
const { jsSnippets } = getDynamicBindings(unevalPropValue, entity);
|
const unevalPropValueString =
|
||||||
|
!!unevalPropValue && unevalPropValue.toString();
|
||||||
|
const { jsSnippets } = getDynamicBindings(
|
||||||
|
unevalPropValueString,
|
||||||
|
entity,
|
||||||
|
);
|
||||||
dependencies[`${entityName}.${propertyPath}`] = existingDeps.concat(
|
dependencies[`${entityName}.${propertyPath}`] = existingDeps.concat(
|
||||||
jsSnippets.filter((jsSnippet) => !!jsSnippet),
|
jsSnippets.filter((jsSnippet) => !!jsSnippet),
|
||||||
);
|
);
|
||||||
|
|
@ -730,6 +735,30 @@ export default class DataTreeEvaluator {
|
||||||
_.set(currentTree, fullPropertyPath, evalPropertyValue);
|
_.set(currentTree, fullPropertyPath, evalPropertyValue);
|
||||||
return currentTree;
|
return currentTree;
|
||||||
} else if (isJSAction(entity)) {
|
} else if (isJSAction(entity)) {
|
||||||
|
const variableList: Array<string> =
|
||||||
|
_.get(entity, "variables") || [];
|
||||||
|
if (variableList.indexOf(propertyPath) > -1) {
|
||||||
|
const currentEvaluatedValue = _.get(
|
||||||
|
currentTree,
|
||||||
|
getEvalValuePath(fullPropertyPath, {
|
||||||
|
isPopulated: true,
|
||||||
|
fullPath: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (!currentEvaluatedValue) {
|
||||||
|
_.set(
|
||||||
|
currentTree,
|
||||||
|
getEvalValuePath(fullPropertyPath, {
|
||||||
|
isPopulated: true,
|
||||||
|
fullPath: true,
|
||||||
|
}),
|
||||||
|
evalPropertyValue,
|
||||||
|
);
|
||||||
|
_.set(currentTree, fullPropertyPath, evalPropertyValue);
|
||||||
|
} else {
|
||||||
|
_.set(currentTree, fullPropertyPath, currentEvaluatedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
return currentTree;
|
return currentTree;
|
||||||
} else {
|
} else {
|
||||||
return _.set(currentTree, fullPropertyPath, evalPropertyValue);
|
return _.set(currentTree, fullPropertyPath, evalPropertyValue);
|
||||||
|
|
@ -1041,47 +1070,60 @@ export default class DataTreeEvaluator {
|
||||||
if (correctFormat) {
|
if (correctFormat) {
|
||||||
const body = entity.body.replace(/export default/g, "");
|
const body = entity.body.replace(/export default/g, "");
|
||||||
try {
|
try {
|
||||||
const { result } = evaluateSync(body, unEvalDataTree, {}, true);
|
|
||||||
delete this.resolvedFunctions[`${entityName}`];
|
delete this.resolvedFunctions[`${entityName}`];
|
||||||
delete this.currentJSCollectionState[`${entityName}`];
|
delete this.currentJSCollectionState[`${entityName}`];
|
||||||
if (result) {
|
const parsedObject = parseJSObjectWithAST(body);
|
||||||
const actions: ParsedJSSubAction[] = [];
|
const actions: any = [];
|
||||||
const variables: any = [];
|
const variables: any = [];
|
||||||
Object.keys(result).forEach((unEvalFunc) => {
|
if (!!parsedObject) {
|
||||||
const unEvalValue = result[unEvalFunc];
|
parsedObject.forEach((parsedElement: any) => {
|
||||||
if (typeof unEvalValue === "function") {
|
if (
|
||||||
const params = getParams(unEvalValue);
|
parsedElement.type === "ArrowFunctionExpression" ||
|
||||||
const functionString = unEvalValue.toString();
|
parsedElement.type === "FunctionExpression"
|
||||||
_.set(
|
) {
|
||||||
this.resolvedFunctions,
|
try {
|
||||||
`${entityName}.${unEvalFunc}`,
|
const { result } = evaluateSync(
|
||||||
unEvalValue,
|
parsedElement.value,
|
||||||
);
|
unEvalDataTree,
|
||||||
_.set(
|
{},
|
||||||
this.currentJSCollectionState,
|
true,
|
||||||
`${entityName}.${unEvalFunc}`,
|
);
|
||||||
functionString,
|
if (!!result) {
|
||||||
);
|
const params = getParams(result);
|
||||||
actions.push({
|
const functionString = parsedElement.value;
|
||||||
name: unEvalFunc,
|
_.set(
|
||||||
body: functionString,
|
this.resolvedFunctions,
|
||||||
arguments: params,
|
`${entityName}.${parsedElement.key}`,
|
||||||
parsedFunction: unEvalValue,
|
result,
|
||||||
isAsync: false,
|
);
|
||||||
});
|
_.set(
|
||||||
} else {
|
this.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({
|
variables.push({
|
||||||
name: unEvalFunc,
|
name: parsedElement.key,
|
||||||
value: result[unEvalFunc],
|
value: parsedElement.value,
|
||||||
});
|
});
|
||||||
_.set(
|
_.set(
|
||||||
this.currentJSCollectionState,
|
this.currentJSCollectionState,
|
||||||
`${entityName}.${unEvalFunc}`,
|
`${entityName}.${parsedElement.key}`,
|
||||||
unEvalValue,
|
parsedElement.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsedBody = {
|
const parsedBody = {
|
||||||
body: entity.body,
|
body: entity.body,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
|
|
@ -1097,16 +1139,8 @@ export default class DataTreeEvaluator {
|
||||||
id: entity.actionId,
|
id: entity.actionId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
const errors = {
|
//if we need to push error as popup in case
|
||||||
type: EvalErrorTypes.PARSE_JS_ERROR,
|
|
||||||
context: {
|
|
||||||
entity: entity,
|
|
||||||
propertyPath: entity.name + ".body",
|
|
||||||
},
|
|
||||||
message: (error as Error).message,
|
|
||||||
};
|
|
||||||
this.errors.push(errors);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const errors = {
|
const errors = {
|
||||||
|
|
@ -1503,7 +1537,7 @@ export default class DataTreeEvaluator {
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isAction(entity) && !isWidget(entity)) {
|
if (!isAction(entity) && !isWidget(entity) && !isJSAction(entity)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let entityDynamicBindingPaths: string[] = [];
|
let entityDynamicBindingPaths: string[] = [];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { extractIdentifiersFromCode } from "workers/ast";
|
import { extractIdentifiersFromCode, parseJSObjectWithAST } from "workers/ast";
|
||||||
|
|
||||||
describe("getAllIdentifiers", () => {
|
describe("getAllIdentifiers", () => {
|
||||||
it("works properly", () => {
|
it("works properly", () => {
|
||||||
|
|
@ -228,3 +228,85 @@ describe("getAllIdentifiers", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("parseJSObjectWithAST", () => {
|
||||||
|
it("parse js object", () => {
|
||||||
|
const body = `{
|
||||||
|
myVar1: [],
|
||||||
|
myVar2: {},
|
||||||
|
myFun1: () => {
|
||||||
|
//write code here
|
||||||
|
},
|
||||||
|
myFun2: async () => {
|
||||||
|
//use async-await or promises
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const parsedObject = [
|
||||||
|
{
|
||||||
|
key: "myVar1",
|
||||||
|
value: "[]",
|
||||||
|
type: "ArrayExpression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myVar2",
|
||||||
|
value: "{}",
|
||||||
|
type: "ObjectExpression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myFun1",
|
||||||
|
value: "() => {}",
|
||||||
|
type: "ArrowFunctionExpression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myFun2",
|
||||||
|
value: "async () => {}",
|
||||||
|
type: "ArrowFunctionExpression",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const resultParsedObject = parseJSObjectWithAST(body);
|
||||||
|
expect(resultParsedObject).toStrictEqual(parsedObject);
|
||||||
|
});
|
||||||
|
it("parse js object with literal", () => {
|
||||||
|
const body = `{
|
||||||
|
myVar1: [],
|
||||||
|
myVar2: {
|
||||||
|
"a": "app",
|
||||||
|
},
|
||||||
|
myFun1: () => {
|
||||||
|
//write code here
|
||||||
|
},
|
||||||
|
myFun2: async () => {
|
||||||
|
//use async-await or promises
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const parsedObject = [
|
||||||
|
{
|
||||||
|
key: "myVar1",
|
||||||
|
value: "[]",
|
||||||
|
type: "ArrayExpression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '"a"',
|
||||||
|
value: '"app"',
|
||||||
|
type: "Literal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myVar2",
|
||||||
|
value: '{\n "a": "app"\n}',
|
||||||
|
type: "ObjectExpression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myFun1",
|
||||||
|
value: "() => {}",
|
||||||
|
type: "ArrowFunctionExpression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "myFun2",
|
||||||
|
value: "async () => {}",
|
||||||
|
type: "ArrowFunctionExpression",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const resultParsedObject = parseJSObjectWithAST(body);
|
||||||
|
expect(resultParsedObject).toStrictEqual(parsedObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { parse, Node } from "acorn";
|
import { parse, Node } from "acorn";
|
||||||
import { ancestor } from "acorn-walk";
|
import { ancestor, simple } from "acorn-walk";
|
||||||
import { ECMA_VERSION, NodeTypes } from "constants/ast";
|
import { ECMA_VERSION, NodeTypes } from "constants/ast";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { sanitizeScript } from "./evaluate";
|
import { sanitizeScript } from "./evaluate";
|
||||||
|
import { generate } from "astring";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Valuable links:
|
* Valuable links:
|
||||||
|
|
@ -298,3 +299,26 @@ const getPropertyAccessor = (propertyNode: IdentifierNode | LiteralNode) => {
|
||||||
return `[${propertyNode.value}]`;
|
return `[${propertyNode.value}]`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const parseJSObjectWithAST = (jsObjectBody: string) => {
|
||||||
|
const createString = `var jsObj = ${jsObjectBody}`;
|
||||||
|
const ast = parse(createString, { ecmaVersion: ECMA_VERSION });
|
||||||
|
const rawKeyValues: any = [];
|
||||||
|
const parsedObject: any = [];
|
||||||
|
simple(ast, {
|
||||||
|
Property(node: any) {
|
||||||
|
rawKeyValues.push({
|
||||||
|
key: node.key,
|
||||||
|
value: node.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
rawKeyValues.forEach((rawKeyValue: any) => {
|
||||||
|
parsedObject.push({
|
||||||
|
key: generate(rawKeyValue.key),
|
||||||
|
value: generate(rawKeyValue.value),
|
||||||
|
type: rawKeyValue.value.type,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return parsedObject;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -822,6 +822,22 @@ export const updateJSCollectionInDataTree = (
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
varList.push(newVar.name);
|
varList.push(newVar.name);
|
||||||
|
const reactivePaths = jsCollection.reactivePaths;
|
||||||
|
reactivePaths[newVar.name] =
|
||||||
|
EvaluationSubstitutionType.SMART_SUBSTITUTE;
|
||||||
|
_.set(
|
||||||
|
modifiedDataTree,
|
||||||
|
`${jsCollection.name}.reactivePaths`,
|
||||||
|
reactivePaths,
|
||||||
|
);
|
||||||
|
const dynamicBindingPathList = jsCollection.dynamicBindingPathList;
|
||||||
|
dynamicBindingPathList.push({ key: newVar.name });
|
||||||
|
_.set(
|
||||||
|
modifiedDataTree,
|
||||||
|
`${jsCollection.name}.dynamicBindingPathList`,
|
||||||
|
dynamicBindingPathList,
|
||||||
|
);
|
||||||
|
|
||||||
_.set(modifiedDataTree, `${jsCollection.name}.variables`, varList);
|
_.set(modifiedDataTree, `${jsCollection.name}.variables`, varList);
|
||||||
_.set(
|
_.set(
|
||||||
modifiedDataTree,
|
modifiedDataTree,
|
||||||
|
|
@ -830,15 +846,33 @@ export const updateJSCollectionInDataTree = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let newVarList: Array<string> = [];
|
let newVarList: Array<string> = varList;
|
||||||
for (let i = 0; i < varList.length; i++) {
|
for (let i = 0; i < varList.length; i++) {
|
||||||
const varListItem = varList[i];
|
const varListItem = varList[i];
|
||||||
const existsInParsed = parsedBody.variables.find(
|
const existsInParsed = parsedBody.variables.find(
|
||||||
(item) => item.name === varListItem,
|
(item) => item.name === varListItem,
|
||||||
);
|
);
|
||||||
if (!existsInParsed) {
|
if (!existsInParsed) {
|
||||||
|
const reactivePaths = jsCollection.reactivePaths;
|
||||||
|
delete reactivePaths[varListItem];
|
||||||
|
_.set(
|
||||||
|
modifiedDataTree,
|
||||||
|
`${jsCollection.name}.reactivePaths`,
|
||||||
|
reactivePaths,
|
||||||
|
);
|
||||||
|
|
||||||
|
let dynamicBindingPathList = jsCollection.dynamicBindingPathList;
|
||||||
|
dynamicBindingPathList = dynamicBindingPathList.filter(
|
||||||
|
(path) => path["key"] !== varListItem,
|
||||||
|
);
|
||||||
|
_.set(
|
||||||
|
modifiedDataTree,
|
||||||
|
`${jsCollection.name}.dynamicBindingPathList`,
|
||||||
|
dynamicBindingPathList,
|
||||||
|
);
|
||||||
|
|
||||||
|
newVarList = newVarList.filter((item) => item !== varListItem);
|
||||||
delete modifiedDataTree[`${jsCollection.name}`][`${varListItem}`];
|
delete modifiedDataTree[`${jsCollection.name}`][`${varListItem}`];
|
||||||
newVarList = varList.filter((item) => item !== varListItem);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newVarList.length) {
|
if (newVarList.length) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user