fix: update rts logic to use updated shared AST logic (#16849)
* update rts logic to use updated shared AST logic * Make changes to naming conventions * Add test cases for RTS and rename ast functions * Add running jest test to RTS workflow * Install dependencies and then trigger jest tests in workflow * Close server connection after test ends * Remove logs * Improve jest test descriptions
This commit is contained in:
parent
9d32752abb
commit
610509506e
10
.github/workflows/rts-build.yml
vendored
10
.github/workflows/rts-build.yml
vendored
|
|
@ -119,6 +119,16 @@ jobs:
|
|||
echo ::set-output name=version::$next_version-SNAPSHOT
|
||||
echo ::set-output name=tag::$(echo ${GITHUB_REF:11})
|
||||
|
||||
# Install all the dependencies
|
||||
- name: Install dependencies
|
||||
if: steps.run_result.outputs.run_result != 'success'
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
# Run the Jest tests only if the workflow has been invoked in a PR
|
||||
- name: Run the jest tests
|
||||
if: steps.run_result.outputs.run_result != 'success'
|
||||
run: yarn run test:unit
|
||||
|
||||
- name: Build
|
||||
if: steps.run_result.outputs.run_result != 'success'
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getDynamicBindings,
|
||||
extraLibrariesNames,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
import { extractInfoFromCode } from "@shared/ast";
|
||||
import { extractIdentifierInfoFromCode } from "@shared/ast";
|
||||
import { convertPathToString, isWidget } from "../evaluationUtils";
|
||||
import { DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
||||
import {
|
||||
|
|
@ -31,7 +31,7 @@ export const extractInfoFromBinding = (
|
|||
script: string,
|
||||
allPaths: Record<string, true>,
|
||||
): { validReferences: string[]; invalidReferences: string[] } => {
|
||||
const { references } = extractInfoFromCode(
|
||||
const { references } = extractIdentifierInfoFromCode(
|
||||
script,
|
||||
self.evaluationVersion,
|
||||
invalidEntityIdentifiers,
|
||||
|
|
|
|||
23
app/rts/jest.config.js
Normal file
23
app/rts/jest.config.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
module.exports = {
|
||||
roots: ["<rootDir>/src"],
|
||||
transform: {
|
||||
"^.+\\.(png|js|ts|tsx)$": "ts-jest",
|
||||
},
|
||||
testTimeout: 9000,
|
||||
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(tsx|ts|js)?$",
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"],
|
||||
moduleDirectories: ["node_modules", "src", "test"],
|
||||
moduleNameMapper: {
|
||||
"@constants/(.*)": ["<rootDir>/src/constants/$1"],
|
||||
"@services/(.*)": ["<rootDir>/src/services/$1"],
|
||||
"@middlewares/(.*)": ["<rootDir>/src/middlewares/$1"],
|
||||
"@controllers/(.*)": ["<rootDir>/src/controllers/$1"],
|
||||
"@rules/(.*)": ["<rootDir>/src/middlewares/rules/$1"],
|
||||
"@utils/(.*)": ["<rootDir>/src/utils/$1"],
|
||||
},
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
isolatedModules: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -12,17 +12,22 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/jest": "^29.0.3",
|
||||
"@types/mongodb": "^3.6.10",
|
||||
"axios": "^0.21.2",
|
||||
"express": "^4.17.1",
|
||||
"jest": "^29.0.3",
|
||||
"loglevel": "^1.7.1",
|
||||
"mongodb": "^3.6.4",
|
||||
"socket.io": "^4.5.1",
|
||||
"socket.io-adapter": "^2.3.2",
|
||||
"source-map-support": "^0.5.19",
|
||||
"ts-jest": "^29.0.2",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test:unit": "export APPSMITH_API_BASE_URL=http APPSMITH_MONGODB_URI=mongodb && $(npm bin)/jest -b --colors --no-cache --silent --coverage --collectCoverage=true --coverageDirectory='../../' --coverageReporters='json-summary'",
|
||||
"test:jest": "export APPSMITH_API_BASE_URL=http APPSMITH_MONGODB_URI=mongodb && $(npm bin)/jest --watch ",
|
||||
"preinstall": "CURRENT_SCOPE=rts node ../shared/build-shared-dep.js",
|
||||
"build": "./build.sh",
|
||||
"postinstall": "CURRENT_SCOPE=rts node ../shared/install-dependencies.js",
|
||||
|
|
@ -31,6 +36,7 @@
|
|||
"dependencies": {
|
||||
"express-validator": "^6.14.2",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"supertest": "^6.2.4",
|
||||
"tsc-alias": "^1.7.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ export default class AstController extends BaseController {
|
|||
super();
|
||||
}
|
||||
|
||||
async getDependentIdentifiers(req: Request, res: Response) {
|
||||
async getIdentifierDataFromScript(req: Request, res: Response) {
|
||||
try {
|
||||
// By default the application eval version is set to be 2
|
||||
const { script, evalVersion = 2 }: ScriptToIdentifiersType = req.body;
|
||||
const data = await AstService.getIdentifiersFromScript(
|
||||
const data = await AstService.extractIdentifierDataFromScript(
|
||||
script,
|
||||
evalVersion
|
||||
);
|
||||
|
|
@ -37,7 +37,7 @@ export default class AstController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
async getMultipleDependentIdentifiers(req: Request, res: Response) {
|
||||
async getIdentifierDataFromMultipleScripts(req: Request, res: Response) {
|
||||
try {
|
||||
// By default the application eval version is set to be 2
|
||||
const { scripts, evalVersion = 2 }: MultipleScriptToIdentifiersType =
|
||||
|
|
@ -46,7 +46,10 @@ export default class AstController extends BaseController {
|
|||
Promise.all(
|
||||
scripts.map(
|
||||
async (script) =>
|
||||
await AstService.getIdentifiersFromScript(script, evalVersion)
|
||||
await AstService.extractIdentifierDataFromScript(
|
||||
script,
|
||||
evalVersion
|
||||
)
|
||||
)
|
||||
).then((data) => {
|
||||
return super.sendResponse(res, data);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Response } from "express";
|
||||
import { ValidationError } from "express-validator";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { IdentifierInfo } from "@shared/ast";
|
||||
|
||||
type ErrorData = {
|
||||
error: string | string[];
|
||||
|
|
@ -16,7 +17,7 @@ type ErrorBag = {
|
|||
type ResponseData = {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
data: unknown; //setting unknown for now, to be modified later.
|
||||
data: IdentifierInfo;
|
||||
};
|
||||
|
||||
export default class BaseController {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,17 @@ const astController = new AstController();
|
|||
const validator = new Validator();
|
||||
|
||||
router.post(
|
||||
"/single-script-identifiers",
|
||||
"/single-script-data",
|
||||
AstRules.getScriptValidator(),
|
||||
validator.validateRequest,
|
||||
astController.getDependentIdentifiers
|
||||
astController.getIdentifierDataFromScript
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/multiple-script-identifiers",
|
||||
"/multiple-script-data",
|
||||
AstRules.getMultipleScriptValidator(),
|
||||
validator.validateRequest,
|
||||
astController.getMultipleDependentIdentifiers
|
||||
astController.getIdentifierDataFromMultipleScripts
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { initializeSockets } from "./sockets";
|
|||
import ast_routes from "./routes/ast_routes";
|
||||
|
||||
const RTS_BASE_PATH = "/rts";
|
||||
const RTS_BASE_API_PATH = "/rts-api/v1";
|
||||
export const RTS_BASE_API_PATH = "/rts-api/v1";
|
||||
|
||||
// Setting the logLevel for all log messages
|
||||
const logLevel: LogLevelDesc = (process.env.APPSMITH_LOG_LEVEL ||
|
||||
|
|
@ -36,32 +36,30 @@ if (API_BASE_URL == null || API_BASE_URL === "") {
|
|||
|
||||
const PORT = process.env.PORT || 8091;
|
||||
|
||||
main();
|
||||
//Disable x-powered-by header to prevent information disclosure
|
||||
const app = express();
|
||||
app.disable("x-powered-by");
|
||||
const server = new http.Server(app);
|
||||
const io = new Server(server, {
|
||||
path: RTS_BASE_PATH,
|
||||
});
|
||||
|
||||
function main() {
|
||||
const app = express();
|
||||
//Disable x-powered-by header to prevent information disclosure
|
||||
app.disable("x-powered-by");
|
||||
const server = new http.Server(app);
|
||||
const io = new Server(server, {
|
||||
path: RTS_BASE_PATH,
|
||||
});
|
||||
// Initializing Sockets
|
||||
initializeSockets(io);
|
||||
|
||||
// Initializing Sockets
|
||||
initializeSockets(io);
|
||||
// parse incoming json requests
|
||||
app.use(express.json({ limit: "5mb" }));
|
||||
// Initializing Routes
|
||||
app.use(express.static(path.join(__dirname, "static")));
|
||||
app.get("/", (_, res) => {
|
||||
res.redirect("/index.html");
|
||||
});
|
||||
|
||||
// parse incoming json requests
|
||||
app.use(express.json({ limit: "5mb" }));
|
||||
// Initializing Routes
|
||||
app.use(express.static(path.join(__dirname, "static")));
|
||||
app.get("/", (_, res) => {
|
||||
res.redirect("/index.html");
|
||||
});
|
||||
app.use(`${RTS_BASE_API_PATH}/ast`, ast_routes);
|
||||
|
||||
app.use(`${RTS_BASE_API_PATH}/ast`, ast_routes);
|
||||
// Run the server
|
||||
server.listen(PORT, () => {
|
||||
log.info(`RTS version ${buildVersion} running at http://localhost:${PORT}`);
|
||||
});
|
||||
|
||||
// Run the server
|
||||
server.listen(PORT, () => {
|
||||
log.info(`RTS version ${buildVersion} running at http://localhost:${PORT}`);
|
||||
});
|
||||
}
|
||||
export default server;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
import { extractInfoFromCode } from "@shared/ast";
|
||||
import { extractIdentifierInfoFromCode } from "@shared/ast";
|
||||
|
||||
export default class AstService {
|
||||
static async getIdentifiersFromScript(
|
||||
static async extractIdentifierDataFromScript(
|
||||
script,
|
||||
evalVersion
|
||||
evalVersion,
|
||||
invalidIdentifiers = {}
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const extractions = extractInfoFromCode(
|
||||
const identifierInfo = extractIdentifierInfoFromCode(
|
||||
script,
|
||||
evalVersion
|
||||
evalVersion,
|
||||
invalidIdentifiers
|
||||
);
|
||||
|
||||
resolve(extractions);
|
||||
resolve(identifierInfo);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
|
|
|||
67
app/rts/src/test/server.test.ts
Normal file
67
app/rts/src/test/server.test.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import app, { RTS_BASE_API_PATH } from "../server";
|
||||
import supertest from "supertest";
|
||||
|
||||
const singleScript = {
|
||||
script:
|
||||
"(function abc() { let Api2 = { }; return Api2.data ? str.data + Api1.data : [] })()",
|
||||
};
|
||||
|
||||
const multipleScripts = {
|
||||
scripts: [
|
||||
"(function abc() { return Api1.data })() ",
|
||||
"(function abc() { let str = ''; return str ? Api1.data : [] })()",
|
||||
],
|
||||
};
|
||||
|
||||
afterAll((done) => {
|
||||
app.close();
|
||||
done();
|
||||
});
|
||||
|
||||
describe("AST tests", () => {
|
||||
it("Checks to see if single script is parsed correctly using the API", async () => {
|
||||
const expectedResponse = {
|
||||
references: ["str.data", "Api1.data"],
|
||||
functionalParams: [],
|
||||
variables: ["Api2"],
|
||||
};
|
||||
|
||||
await supertest(app)
|
||||
.post(`${RTS_BASE_API_PATH}/ast/single-script-data`, {
|
||||
JSON: true,
|
||||
})
|
||||
.send(singleScript)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.success).toEqual(true);
|
||||
expect(response.body.data).toEqual(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
it("Checks to see if multiple scripts are parsed correctly using the API", async () => {
|
||||
const expectedResponse = [
|
||||
{
|
||||
references: ["Api1.data"],
|
||||
functionalParams: [],
|
||||
variables: [],
|
||||
},
|
||||
{
|
||||
references: ["Api1.data"],
|
||||
functionalParams: [],
|
||||
variables: ["str"],
|
||||
},
|
||||
];
|
||||
|
||||
await supertest(app)
|
||||
.post(`${RTS_BASE_API_PATH}/ast/multiple-script-data`, {
|
||||
JSON: true,
|
||||
})
|
||||
.send(multipleScripts)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.success).toEqual(true);
|
||||
expect(response.body.data.length).toBeGreaterThan(1);
|
||||
expect(response.body.data).toEqual(expectedResponse);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
"@middlewares/*": ["./src/middlewares/*"],
|
||||
"@controllers/*": ["./src/controllers/*"],
|
||||
"@rules/*": ["./src/middlewares/rules/*"],
|
||||
"@utils/*": ["./src/utils/*"],
|
||||
"@utils/*": ["./src/utils/*"]
|
||||
}
|
||||
},
|
||||
"lib": ["es2015"]
|
||||
|
|
|
|||
2285
app/rts/yarn.lock
2285
app/rts/yarn.lock
File diff suppressed because it is too large
Load Diff
|
|
@ -8,21 +8,27 @@ import {
|
|||
isPropertyNode,
|
||||
isPropertyAFunctionNode,
|
||||
getAST,
|
||||
extractInfoFromCode,
|
||||
extractIdentifierInfoFromCode,
|
||||
extractInvalidTopLevelMemberExpressionsFromCode,
|
||||
getFunctionalParamsFromNode,
|
||||
isTypeOfFunction,
|
||||
MemberExpressionData,
|
||||
} from './src';
|
||||
IdentifierInfo,
|
||||
} from "./src";
|
||||
|
||||
// constants
|
||||
import { ECMA_VERSION, SourceType, NodeTypes } from './src/constants';
|
||||
import { ECMA_VERSION, SourceType, NodeTypes } from "./src/constants";
|
||||
|
||||
// JSObjects
|
||||
import { parseJSObjectWithAST } from './src/jsObject';
|
||||
import { parseJSObjectWithAST } from "./src/jsObject";
|
||||
|
||||
// types or intefaces should be exported with type keyword, while enums can be exported like normal functions
|
||||
export type { ObjectExpression, PropertyNode, MemberExpressionData };
|
||||
export type {
|
||||
ObjectExpression,
|
||||
PropertyNode,
|
||||
MemberExpressionData,
|
||||
IdentifierInfo,
|
||||
};
|
||||
|
||||
export {
|
||||
isIdentifierNode,
|
||||
|
|
@ -32,7 +38,7 @@ export {
|
|||
isPropertyNode,
|
||||
isPropertyAFunctionNode,
|
||||
getAST,
|
||||
extractInfoFromCode,
|
||||
extractIdentifierInfoFromCode,
|
||||
extractInvalidTopLevelMemberExpressionsFromCode,
|
||||
getFunctionalParamsFromNode,
|
||||
isTypeOfFunction,
|
||||
|
|
|
|||
|
|
@ -1,85 +1,85 @@
|
|||
import { extractInfoFromCode } from '../src/index';
|
||||
import { parseJSObjectWithAST } from '../src/jsObject';
|
||||
import { extractIdentifierInfoFromCode } from "../src/index";
|
||||
import { parseJSObjectWithAST } from "../src/jsObject";
|
||||
|
||||
describe('getAllIdentifiers', () => {
|
||||
it('works properly', () => {
|
||||
describe("getAllIdentifiers", () => {
|
||||
it("works properly", () => {
|
||||
const cases: { script: string; expectedResults: string[] }[] = [
|
||||
{
|
||||
// Entity reference
|
||||
script: 'DirectTableReference',
|
||||
expectedResults: ['DirectTableReference'],
|
||||
script: "DirectTableReference",
|
||||
expectedResults: ["DirectTableReference"],
|
||||
},
|
||||
{
|
||||
// One level nesting
|
||||
script: 'TableDataReference.data',
|
||||
expectedResults: ['TableDataReference.data'],
|
||||
script: "TableDataReference.data",
|
||||
expectedResults: ["TableDataReference.data"],
|
||||
},
|
||||
{
|
||||
// Deep nesting
|
||||
script: 'TableDataDetailsReference.data.details',
|
||||
expectedResults: ['TableDataDetailsReference.data.details'],
|
||||
script: "TableDataDetailsReference.data.details",
|
||||
expectedResults: ["TableDataDetailsReference.data.details"],
|
||||
},
|
||||
{
|
||||
// Deep nesting
|
||||
script: 'TableDataDetailsMoreReference.data.details.more',
|
||||
expectedResults: ['TableDataDetailsMoreReference.data.details.more'],
|
||||
script: "TableDataDetailsMoreReference.data.details.more",
|
||||
expectedResults: ["TableDataDetailsMoreReference.data.details.more"],
|
||||
},
|
||||
{
|
||||
// Deep optional chaining
|
||||
script: 'TableDataOptionalReference.data?.details.more',
|
||||
expectedResults: ['TableDataOptionalReference.data'],
|
||||
script: "TableDataOptionalReference.data?.details.more",
|
||||
expectedResults: ["TableDataOptionalReference.data"],
|
||||
},
|
||||
{
|
||||
// Deep optional chaining with logical operator
|
||||
script:
|
||||
'TableDataOptionalWithLogical.data?.details.more || FallbackTableData.data',
|
||||
"TableDataOptionalWithLogical.data?.details.more || FallbackTableData.data",
|
||||
expectedResults: [
|
||||
'TableDataOptionalWithLogical.data',
|
||||
'FallbackTableData.data',
|
||||
"TableDataOptionalWithLogical.data",
|
||||
"FallbackTableData.data",
|
||||
],
|
||||
},
|
||||
{
|
||||
// null coalescing
|
||||
script: 'TableDataOptionalWithLogical.data ?? FallbackTableData.data',
|
||||
script: "TableDataOptionalWithLogical.data ?? FallbackTableData.data",
|
||||
expectedResults: [
|
||||
'TableDataOptionalWithLogical.data',
|
||||
'FallbackTableData.data',
|
||||
"TableDataOptionalWithLogical.data",
|
||||
"FallbackTableData.data",
|
||||
],
|
||||
},
|
||||
{
|
||||
// Basic map function
|
||||
script: 'Table5.data.map(c => ({ name: c.name }))',
|
||||
expectedResults: ['Table5.data.map'],
|
||||
script: "Table5.data.map(c => ({ name: c.name }))",
|
||||
expectedResults: ["Table5.data.map"],
|
||||
},
|
||||
{
|
||||
// Literal property search
|
||||
script: "Table6['data']",
|
||||
expectedResults: ['Table6'],
|
||||
expectedResults: ["Table6"],
|
||||
},
|
||||
{
|
||||
// Deep literal property search
|
||||
script: "TableDataOptionalReference['data'].details",
|
||||
expectedResults: ['TableDataOptionalReference'],
|
||||
expectedResults: ["TableDataOptionalReference"],
|
||||
},
|
||||
{
|
||||
// Array index search
|
||||
script: 'array[8]',
|
||||
expectedResults: ['array[8]'],
|
||||
script: "array[8]",
|
||||
expectedResults: ["array[8]"],
|
||||
},
|
||||
{
|
||||
// Deep array index search
|
||||
script: 'Table7.data[4]',
|
||||
expectedResults: ['Table7.data[4]'],
|
||||
script: "Table7.data[4]",
|
||||
expectedResults: ["Table7.data[4]"],
|
||||
},
|
||||
{
|
||||
// Deep array index search
|
||||
script: 'Table7.data[4].value',
|
||||
expectedResults: ['Table7.data[4].value'],
|
||||
script: "Table7.data[4].value",
|
||||
expectedResults: ["Table7.data[4].value"],
|
||||
},
|
||||
{
|
||||
// string literal and array index search
|
||||
script: "Table['data'][9]",
|
||||
expectedResults: ['Table'],
|
||||
expectedResults: ["Table"],
|
||||
},
|
||||
{
|
||||
// array index and string literal search
|
||||
|
|
@ -88,29 +88,29 @@ describe('getAllIdentifiers', () => {
|
|||
},
|
||||
{
|
||||
// Index identifier search
|
||||
script: 'Table8.data[row][name]',
|
||||
expectedResults: ['Table8.data', 'row'],
|
||||
script: "Table8.data[row][name]",
|
||||
expectedResults: ["Table8.data", "row"],
|
||||
},
|
||||
{
|
||||
// Index identifier search with global
|
||||
script: 'Table9.data[appsmith.store.row]',
|
||||
expectedResults: ['Table9.data', 'appsmith.store.row'],
|
||||
script: "Table9.data[appsmith.store.row]",
|
||||
expectedResults: ["Table9.data", "appsmith.store.row"],
|
||||
},
|
||||
{
|
||||
// Index literal with further nested lookups
|
||||
script: 'Table10.data[row].name',
|
||||
expectedResults: ['Table10.data', 'row'],
|
||||
script: "Table10.data[row].name",
|
||||
expectedResults: ["Table10.data", "row"],
|
||||
},
|
||||
{
|
||||
// IIFE and if conditions
|
||||
script:
|
||||
'(function(){ if(Table11.isVisible) { return Api1.data } else { return Api2.data } })()',
|
||||
expectedResults: ['Table11.isVisible', 'Api1.data', 'Api2.data'],
|
||||
"(function(){ if(Table11.isVisible) { return Api1.data } else { return Api2.data } })()",
|
||||
expectedResults: ["Table11.isVisible", "Api1.data", "Api2.data"],
|
||||
},
|
||||
{
|
||||
// Functions and arguments
|
||||
script: 'JSObject1.run(Api1.data, Api2.data)',
|
||||
expectedResults: ['JSObject1.run', 'Api1.data', 'Api2.data'],
|
||||
script: "JSObject1.run(Api1.data, Api2.data)",
|
||||
expectedResults: ["JSObject1.run", "Api1.data", "Api2.data"],
|
||||
},
|
||||
{
|
||||
// IIFE - without braces
|
||||
|
|
@ -124,7 +124,7 @@ describe('getAllIdentifiers', () => {
|
|||
return obj[index]
|
||||
|
||||
}()`,
|
||||
expectedResults: ['Input1.text'],
|
||||
expectedResults: ["Input1.text"],
|
||||
},
|
||||
{
|
||||
// IIFE
|
||||
|
|
@ -138,7 +138,7 @@ describe('getAllIdentifiers', () => {
|
|||
return obj[index]
|
||||
|
||||
})()`,
|
||||
expectedResults: ['Input2.text'],
|
||||
expectedResults: ["Input2.text"],
|
||||
},
|
||||
{
|
||||
// arrow IIFE - without braces - will fail
|
||||
|
|
@ -166,19 +166,19 @@ describe('getAllIdentifiers', () => {
|
|||
return obj[index]
|
||||
|
||||
})()`,
|
||||
expectedResults: ['Input4.text'],
|
||||
expectedResults: ["Input4.text"],
|
||||
},
|
||||
{
|
||||
// Direct object access
|
||||
script: `{ "a": 123 }[Input5.text]`,
|
||||
expectedResults: ['Input5.text'],
|
||||
expectedResults: ["Input5.text"],
|
||||
},
|
||||
{
|
||||
// Function declaration and default arguments
|
||||
script: `function run(apiData = Api1.data) {
|
||||
return apiData;
|
||||
}`,
|
||||
expectedResults: ['Api1.data'],
|
||||
expectedResults: ["Api1.data"],
|
||||
},
|
||||
{
|
||||
// Function declaration with arguments
|
||||
|
|
@ -197,7 +197,7 @@ describe('getAllIdentifiers', () => {
|
|||
row = row += 1;
|
||||
}
|
||||
}`,
|
||||
expectedResults: ['Table12.data'],
|
||||
expectedResults: ["Table12.data"],
|
||||
},
|
||||
{
|
||||
// function with variables
|
||||
|
|
@ -209,17 +209,17 @@ describe('getAllIdentifiers', () => {
|
|||
row = row += 1;
|
||||
}
|
||||
}`,
|
||||
expectedResults: ['Table13.data'],
|
||||
expectedResults: ["Table13.data"],
|
||||
},
|
||||
{
|
||||
// expression with arithmetic operations
|
||||
script: `Table14.data + 15`,
|
||||
expectedResults: ['Table14.data'],
|
||||
expectedResults: ["Table14.data"],
|
||||
},
|
||||
{
|
||||
// expression with logical operations
|
||||
script: `Table15.data || [{}]`,
|
||||
expectedResults: ['Table15.data'],
|
||||
expectedResults: ["Table15.data"],
|
||||
},
|
||||
// JavaScript built in classes should not be valid identifiers
|
||||
{
|
||||
|
|
@ -229,7 +229,7 @@ describe('getAllIdentifiers', () => {
|
|||
const randomNumber = Math.random();
|
||||
return Promise.all([firstApiRun, secondApiRun])
|
||||
}()`,
|
||||
expectedResults: ['Api1.run', 'Api2.run'],
|
||||
expectedResults: ["Api1.run", "Api2.run"],
|
||||
},
|
||||
// Global dependencies should not be valid identifiers
|
||||
{
|
||||
|
|
@ -251,7 +251,7 @@ describe('getAllIdentifiers', () => {
|
|||
console.log(joinedName)
|
||||
return Api2.name
|
||||
}()`,
|
||||
expectedResults: ['Api2.name'],
|
||||
expectedResults: ["Api2.name"],
|
||||
},
|
||||
// identifiers and member expressions derived from params should not be valid identifiers
|
||||
{
|
||||
|
|
@ -274,19 +274,19 @@ describe('getAllIdentifiers', () => {
|
|||
script: `function(){
|
||||
return appsmith.user
|
||||
}()`,
|
||||
expectedResults: ['appsmith.user'],
|
||||
expectedResults: ["appsmith.user"],
|
||||
},
|
||||
];
|
||||
|
||||
cases.forEach((perCase) => {
|
||||
const { references } = extractInfoFromCode(perCase.script, 2);
|
||||
const { references } = extractIdentifierInfoFromCode(perCase.script, 2);
|
||||
expect(references).toStrictEqual(perCase.expectedResults);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseJSObjectWithAST', () => {
|
||||
it('parse js object', () => {
|
||||
describe("parseJSObjectWithAST", () => {
|
||||
it("parse js object", () => {
|
||||
const body = `{
|
||||
myVar1: [],
|
||||
myVar2: {},
|
||||
|
|
@ -299,25 +299,25 @@ describe('parseJSObjectWithAST', () => {
|
|||
}`;
|
||||
const parsedObject = [
|
||||
{
|
||||
key: 'myVar1',
|
||||
value: '[]',
|
||||
type: 'ArrayExpression',
|
||||
key: "myVar1",
|
||||
value: "[]",
|
||||
type: "ArrayExpression",
|
||||
},
|
||||
{
|
||||
key: 'myVar2',
|
||||
value: '{}',
|
||||
type: 'ObjectExpression',
|
||||
key: "myVar2",
|
||||
value: "{}",
|
||||
type: "ObjectExpression",
|
||||
},
|
||||
{
|
||||
key: 'myFun1',
|
||||
value: '() => {}',
|
||||
type: 'ArrowFunctionExpression',
|
||||
key: "myFun1",
|
||||
value: "() => {}",
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [],
|
||||
},
|
||||
{
|
||||
key: 'myFun2',
|
||||
value: 'async () => {}',
|
||||
type: 'ArrowFunctionExpression',
|
||||
key: "myFun2",
|
||||
value: "async () => {}",
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [],
|
||||
},
|
||||
];
|
||||
|
|
@ -325,7 +325,7 @@ describe('parseJSObjectWithAST', () => {
|
|||
expect(resultParsedObject).toStrictEqual(parsedObject);
|
||||
});
|
||||
|
||||
it('parse js object with literal', () => {
|
||||
it("parse js object with literal", () => {
|
||||
const body = `{
|
||||
myVar1: [],
|
||||
myVar2: {
|
||||
|
|
@ -340,25 +340,25 @@ describe('parseJSObjectWithAST', () => {
|
|||
}`;
|
||||
const parsedObject = [
|
||||
{
|
||||
key: 'myVar1',
|
||||
value: '[]',
|
||||
type: 'ArrayExpression',
|
||||
key: "myVar1",
|
||||
value: "[]",
|
||||
type: "ArrayExpression",
|
||||
},
|
||||
{
|
||||
key: 'myVar2',
|
||||
key: "myVar2",
|
||||
value: '{\n "a": "app"\n}',
|
||||
type: 'ObjectExpression',
|
||||
type: "ObjectExpression",
|
||||
},
|
||||
{
|
||||
key: 'myFun1',
|
||||
value: '() => {}',
|
||||
type: 'ArrowFunctionExpression',
|
||||
key: "myFun1",
|
||||
value: "() => {}",
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [],
|
||||
},
|
||||
{
|
||||
key: 'myFun2',
|
||||
value: 'async () => {}',
|
||||
type: 'ArrowFunctionExpression',
|
||||
key: "myFun2",
|
||||
value: "async () => {}",
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [],
|
||||
},
|
||||
];
|
||||
|
|
@ -366,7 +366,7 @@ describe('parseJSObjectWithAST', () => {
|
|||
expect(resultParsedObject).toStrictEqual(parsedObject);
|
||||
});
|
||||
|
||||
it('parse js object with variable declaration inside function', () => {
|
||||
it("parse js object with variable declaration inside function", () => {
|
||||
const body = `{
|
||||
myFun1: () => {
|
||||
const a = {
|
||||
|
|
@ -382,7 +382,7 @@ describe('parseJSObjectWithAST', () => {
|
|||
}`;
|
||||
const parsedObject = [
|
||||
{
|
||||
key: 'myFun1',
|
||||
key: "myFun1",
|
||||
value: `() => {
|
||||
const a = {
|
||||
conditions: [],
|
||||
|
|
@ -391,13 +391,13 @@ describe('parseJSObjectWithAST', () => {
|
|||
testFunc2: function () {}
|
||||
};
|
||||
}`,
|
||||
type: 'ArrowFunctionExpression',
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [],
|
||||
},
|
||||
{
|
||||
key: 'myFun2',
|
||||
value: 'async () => {}',
|
||||
type: 'ArrowFunctionExpression',
|
||||
key: "myFun2",
|
||||
value: "async () => {}",
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [],
|
||||
},
|
||||
];
|
||||
|
|
@ -405,7 +405,7 @@ describe('parseJSObjectWithAST', () => {
|
|||
expect(resultParsedObject).toStrictEqual(parsedObject);
|
||||
});
|
||||
|
||||
it('parse js object with params of all types', () => {
|
||||
it("parse js object with params of all types", () => {
|
||||
const body = `{
|
||||
myFun2: async (a,b = Array(1,2,3),c = "", d = [], e = this.myVar1, f = {}, g = function(){}, h = Object.assign({}), i = String(), j = storeValue()) => {
|
||||
//use async-await or promises
|
||||
|
|
@ -414,49 +414,49 @@ describe('parseJSObjectWithAST', () => {
|
|||
|
||||
const parsedObject = [
|
||||
{
|
||||
key: 'myFun2',
|
||||
key: "myFun2",
|
||||
value:
|
||||
'async (a, b = Array(1, 2, 3), c = "", d = [], e = this.myVar1, f = {}, g = function () {}, h = Object.assign({}), i = String(), j = storeValue()) => {}',
|
||||
type: 'ArrowFunctionExpression',
|
||||
type: "ArrowFunctionExpression",
|
||||
arguments: [
|
||||
{
|
||||
paramName: 'a',
|
||||
paramName: "a",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'b',
|
||||
paramName: "b",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'c',
|
||||
paramName: "c",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'd',
|
||||
paramName: "d",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'e',
|
||||
paramName: "e",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'f',
|
||||
paramName: "f",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'g',
|
||||
paramName: "g",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'h',
|
||||
paramName: "h",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'i',
|
||||
paramName: "i",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
paramName: 'j',
|
||||
paramName: "j",
|
||||
defaultValue: undefined,
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { parse, Node, SourceLocation, Options } from 'acorn';
|
||||
import { ancestor, simple } from 'acorn-walk';
|
||||
import { ECMA_VERSION, NodeTypes } from './constants/ast';
|
||||
import { has, isFinite, isString, memoize, toPath } from 'lodash';
|
||||
import { isTrueObject, sanitizeScript } from './utils';
|
||||
import { parse, Node, SourceLocation, Options } from "acorn";
|
||||
import { ancestor, simple } from "acorn-walk";
|
||||
import { ECMA_VERSION, NodeTypes } from "./constants/ast";
|
||||
import { has, isFinite, isString, memoize, toPath } from "lodash";
|
||||
import { isTrueObject, sanitizeScript } from "./utils";
|
||||
|
||||
/*
|
||||
* Valuable links:
|
||||
|
|
@ -90,7 +90,7 @@ export interface PropertyNode extends Node {
|
|||
type: NodeTypes.Property;
|
||||
key: LiteralNode | IdentifierNode;
|
||||
value: Node;
|
||||
kind: 'init' | 'get' | 'set';
|
||||
kind: "init" | "get" | "set";
|
||||
}
|
||||
|
||||
// Node with location details
|
||||
|
|
@ -98,7 +98,7 @@ type NodeWithLocation<NodeType> = NodeType & {
|
|||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
type AstOptions = Omit<Options, 'ecmaVersion'>;
|
||||
type AstOptions = Omit<Options, "ecmaVersion">;
|
||||
|
||||
/* We need these functions to typescript casts the nodes with the correct types */
|
||||
export const isIdentifierNode = (node: Node): node is IdentifierNode => {
|
||||
|
|
@ -196,23 +196,23 @@ export const getAST = memoize((code: string, options?: AstOptions) =>
|
|||
* @param code: The piece of script where references need to be extracted from
|
||||
*/
|
||||
|
||||
interface ExtractInfoFromCode {
|
||||
export interface IdentifierInfo {
|
||||
references: string[];
|
||||
functionalParams: string[];
|
||||
variables: string[];
|
||||
}
|
||||
export const extractInfoFromCode = (
|
||||
export const extractIdentifierInfoFromCode = (
|
||||
code: string,
|
||||
evaluationVersion: number,
|
||||
invalidIdentifiers?: Record<string, unknown>
|
||||
): ExtractInfoFromCode => {
|
||||
): IdentifierInfo => {
|
||||
// List of all references found
|
||||
const references = new Set<string>();
|
||||
// List of variables declared within the script. All identifiers and member expressions derived from declared variables will be removed
|
||||
const variableDeclarations = new Set<string>();
|
||||
// List of functional params declared within the script. All identifiers and member expressions derived from functional params will be removed
|
||||
let functionalParams = new Set<string>();
|
||||
let ast: Node = { end: 0, start: 0, type: '' };
|
||||
let ast: Node = { end: 0, start: 0, type: "" };
|
||||
try {
|
||||
const sanitizedScript = sanitizeScript(code, evaluationVersion);
|
||||
/* wrapCode - Wrapping code in a function, since all code/script get wrapped with a function during evaluation.
|
||||
|
|
@ -377,7 +377,7 @@ export const getFunctionalParamsFromNode = (
|
|||
|
||||
const constructFinalMemberExpIdentifier = (
|
||||
node: MemberExpressionNode,
|
||||
child = ''
|
||||
child = ""
|
||||
): string => {
|
||||
const propertyAccessor = getPropertyAccessor(node.property);
|
||||
if (isIdentifierNode(node.object)) {
|
||||
|
|
@ -438,7 +438,7 @@ export const extractInvalidTopLevelMemberExpressionsFromCode = (
|
|||
const invalidTopLevelMemberExpressions = new Set<MemberExpressionData>();
|
||||
const variableDeclarations = new Set<string>();
|
||||
let functionalParams = new Set<string>();
|
||||
let ast: Node = { end: 0, start: 0, type: '' };
|
||||
let ast: Node = { end: 0, start: 0, type: "" };
|
||||
try {
|
||||
const sanitizedScript = sanitizeScript(code, evaluationVersion);
|
||||
const wrappedCode = wrapCode(sanitizedScript);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user