Execution params in actions (#2128)
This commit is contained in:
parent
7826cee7c3
commit
5bd2cc1ea4
139
app/client/cypress/fixtures/executionParamsDsl.json
Normal file
139
app/client/cypress/fixtures/executionParamsDsl.json
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
{
|
||||
"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 1224,
|
||||
"snapColumns": 16,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 1254,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 33,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"dynamicBindingPathList": [],
|
||||
"version": 4,
|
||||
"minHeight": 1292,
|
||||
"parentColumnSpace": 1,
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"widgetName": "Container3",
|
||||
"type": "CONTAINER_WIDGET",
|
||||
"containerStyle": "card",
|
||||
"isVisible": true,
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 75.25,
|
||||
"parentRowSpace": 38,
|
||||
"dynamicBindingPathList": [],
|
||||
"leftColumn": 0,
|
||||
"rightColumn": 16,
|
||||
"topRow": 0,
|
||||
"bottomRow": 23,
|
||||
"snapColumns": 16,
|
||||
"orientation": "VERTICAL",
|
||||
"children": [
|
||||
{
|
||||
"backgroundColor": "transparent",
|
||||
"widgetName": "8muuok24ny",
|
||||
"type": "CANVAS_WIDGET",
|
||||
"containerStyle": "none",
|
||||
"isVisible": true,
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 1,
|
||||
"parentRowSpace": 1,
|
||||
"leftColumn": 0,
|
||||
"rightColumn": 1204,
|
||||
"topRow": 0,
|
||||
"bottomRow": 532,
|
||||
"snapColumns": 16,
|
||||
"orientation": "VERTICAL",
|
||||
"children": [
|
||||
{
|
||||
"isVisible": true,
|
||||
"label": "Data",
|
||||
"widgetName": "Table1",
|
||||
"tableData": "",
|
||||
"type": "TABLE_WIDGET",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 71.75,
|
||||
"parentRowSpace": 38,
|
||||
"leftColumn": 2,
|
||||
"rightColumn": 10,
|
||||
"topRow": 3,
|
||||
"bottomRow": 10,
|
||||
"parentId": "tyiwk4xuq0",
|
||||
"widgetId": "5up3r2iuvs",
|
||||
"dynamicBindingPathList": []
|
||||
},
|
||||
{
|
||||
"widgetName": "StaticButton",
|
||||
"rightColumn": 14,
|
||||
"onClick": "",
|
||||
"isDefaultClickDisabled": true,
|
||||
"widgetId": "3p92qmlzfl",
|
||||
"buttonStyle": "PRIMARY_BUTTON",
|
||||
"topRow": 3,
|
||||
"bottomRow": 4,
|
||||
"parentRowSpace": 38,
|
||||
"isVisible": true,
|
||||
"type": "BUTTON_WIDGET",
|
||||
"dynamicBindingPathList": [],
|
||||
"parentId": "8muuok24ny",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 71.75,
|
||||
"leftColumn": 11,
|
||||
"text": "Run Static",
|
||||
"isDisabled": false
|
||||
},
|
||||
{
|
||||
"widgetName": "DynamicButton",
|
||||
"rightColumn": 14,
|
||||
"onClick": "",
|
||||
"isDefaultClickDisabled": true,
|
||||
"widgetId": "asdasdlnud",
|
||||
"buttonStyle": "PRIMARY_BUTTON",
|
||||
"topRow": 4,
|
||||
"bottomRow": 5,
|
||||
"parentRowSpace": 38,
|
||||
"isVisible": true,
|
||||
"type": "BUTTON_WIDGET",
|
||||
"dynamicBindingPathList": [],
|
||||
"parentId": "8muuok24ny",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 71.75,
|
||||
"leftColumn": 11,
|
||||
"text": "Run Dynamic",
|
||||
"isDisabled": false
|
||||
},
|
||||
{
|
||||
"isVisible": true,
|
||||
"inputType": "TEXT",
|
||||
"label": "Endpoint",
|
||||
"widgetName": "EndpointInput",
|
||||
"defaultText": "todos",
|
||||
"type": "INPUT_WIDGET",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 71.75,
|
||||
"parentRowSpace": 38,
|
||||
"leftColumn": 9,
|
||||
"rightColumn": 14,
|
||||
"topRow": 1,
|
||||
"bottomRow": 2,
|
||||
"parentId": "0",
|
||||
"widgetId": "ufr2ik3x1q"
|
||||
}
|
||||
],
|
||||
"widgetId": "tyiwk4xuq0",
|
||||
"detachFromLayout": true,
|
||||
"canExtend": false
|
||||
}
|
||||
],
|
||||
"widgetId": "3oe1ka7jon"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
const dsl = require("../../../fixtures/executionParamsDsl.json");
|
||||
const publishPage = require("../../../locators/publishWidgetspage.json");
|
||||
const commonlocators = require("../../../locators/commonlocators.json");
|
||||
|
||||
describe("API Panel Test Functionality", function() {
|
||||
before(() => {
|
||||
cy.addDsl(dsl);
|
||||
});
|
||||
it("Will pass execution params", function() {
|
||||
// Create the Api
|
||||
cy.NavigateToAPI_Panel();
|
||||
cy.CreateAPI("MultiApi");
|
||||
cy.enterDatasourceAndPath(
|
||||
"https://jsonplaceholder.typicode.com/",
|
||||
"{{this.params.endpoint || 'posts'}}",
|
||||
);
|
||||
cy.WaitAutoSave();
|
||||
// Run it
|
||||
cy.RunAPI();
|
||||
|
||||
// Bind the table
|
||||
cy.SearchEntityandOpen("Table1");
|
||||
cy.testJsontext("tabledata", "{{MultiApi.data");
|
||||
// Assert 'posts' data (default)
|
||||
cy.readTabledataPublish("0", "2").then(cellData => {
|
||||
expect(cellData).to.be.equal(
|
||||
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
|
||||
);
|
||||
});
|
||||
|
||||
// Choose static button
|
||||
cy.SearchEntityandOpen("StaticButton");
|
||||
// toggle js of onClick
|
||||
cy.get(".t--property-control-onclick")
|
||||
.find(".t--js-toggle")
|
||||
.click({ force: true });
|
||||
// Bind with MultiApi with static value
|
||||
cy.testJsontext(
|
||||
"onclick",
|
||||
"{{MultiApi.run(undefined, undefined, { endpoint: 'users",
|
||||
);
|
||||
cy.get(commonlocators.editPropCrossButton).click();
|
||||
|
||||
// Choose dynamic button
|
||||
cy.SearchEntityandOpen("DynamicButton");
|
||||
// toggle js of onClick
|
||||
cy.get(".t--property-control-onclick")
|
||||
.find(".t--js-toggle")
|
||||
.click({ force: true });
|
||||
// Bind with MultiApi with dynamicValue value
|
||||
cy.testJsontext(
|
||||
"onclick",
|
||||
"{{MultiApi.run(undefined, undefined, { endpoint: EndpointInput.text",
|
||||
);
|
||||
|
||||
// Publish the app
|
||||
cy.PublishtheApp();
|
||||
cy.wait("@postExecute");
|
||||
|
||||
// Assert on load data in table
|
||||
cy.readTabledataPublish("0", "2").then(cellData => {
|
||||
expect(cellData).to.be.equal(
|
||||
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
|
||||
);
|
||||
});
|
||||
|
||||
// Click Static button
|
||||
cy.get(publishPage.buttonWidget)
|
||||
.first()
|
||||
.click();
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(2000);
|
||||
// Assert statically bound "users" data
|
||||
cy.readTabledataPublish("1", "1").then(cellData => {
|
||||
expect(cellData).to.be.equal("Ervin Howell");
|
||||
});
|
||||
|
||||
// Click dynamic button
|
||||
cy.get(publishPage.buttonWidget)
|
||||
.eq(1)
|
||||
.click();
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(2000);
|
||||
// Assert dynamically bound "todos" data
|
||||
cy.readTabledataPublish("0", "2").then(cellData => {
|
||||
expect(cellData).to.be.equal("delectus aut autem");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -72,3 +72,4 @@ export interface ExecuteErrorPayload {
|
|||
// Group 2 = path (/nested/path)
|
||||
// Group 3 = params (?param=123¶m2=12)
|
||||
export const urlGroupsRegexExp = /^(https?:\/{2}\S+?)(\/\S*?)(\?\S*)?$/;
|
||||
export const EXECUTION_PARAM_KEY = "executionParams";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||
import { EXECUTION_PARAM_KEY } from "./ActionConstants";
|
||||
|
||||
// Always add a validator function in ./Validators for these types
|
||||
export const VALIDATION_TYPES = {
|
||||
|
|
@ -39,7 +40,7 @@ export type Validator = (
|
|||
|
||||
export const ISO_DATE_FORMAT = "YYYY-MM-DDTHH:mm:ss.SSSZ";
|
||||
|
||||
export const JAVSCRIPT_KEYWORDS = {
|
||||
export const JAVASCRIPT_KEYWORDS = {
|
||||
true: "true",
|
||||
await: "await",
|
||||
break: "break",
|
||||
|
|
@ -87,3 +88,10 @@ export const JAVSCRIPT_KEYWORDS = {
|
|||
with: "with",
|
||||
yield: "yield",
|
||||
};
|
||||
|
||||
export const DATA_TREE_KEYWORDS = {
|
||||
actionPaths: "actionPaths",
|
||||
appsmith: "appsmith",
|
||||
pageList: "pageList",
|
||||
[EXECUTION_PARAM_KEY]: EXECUTION_PARAM_KEY,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
EventType,
|
||||
ExecuteActionPayload,
|
||||
ExecuteActionPayloadEvent,
|
||||
EXECUTION_PARAM_KEY,
|
||||
PageAction,
|
||||
} from "constants/ActionConstants";
|
||||
import * as log from "loglevel";
|
||||
|
|
@ -63,7 +64,7 @@ import {
|
|||
import { AppState } from "reducers";
|
||||
import { mapToPropList } from "utils/AppsmithUtils";
|
||||
import { validateResponse } from "sagas/ErrorSagas";
|
||||
import { ToastType, TypeOptions } from "react-toastify";
|
||||
import { TypeOptions } from "react-toastify";
|
||||
import { PLUGIN_TYPE_API } from "constants/ApiEditorConstants";
|
||||
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "constants/ApiConstants";
|
||||
import { updateAppStore } from "actions/pageActions";
|
||||
|
|
@ -71,7 +72,7 @@ import { getAppStoreName } from "constants/AppConstants";
|
|||
import downloadjs from "downloadjs";
|
||||
import { getType, Types } from "utils/TypeHelpers";
|
||||
import { Toaster } from "components/ads/Toast";
|
||||
import { Variant, ToastVariant } from "components/ads/common";
|
||||
import { Variant } from "components/ads/common";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
|
|
@ -239,62 +240,35 @@ const isErrorResponse = (response: ActionApiResponse) => {
|
|||
return !response.data.isExecutionSuccess;
|
||||
};
|
||||
|
||||
export function* evaluateDynamicBoundValueSaga(path: string): any {
|
||||
return yield call(evaluateSingleValue, `{{${path}}}`);
|
||||
export function* evaluateDynamicBoundValueSaga(
|
||||
valueToEvaluate: string,
|
||||
params?: Record<string, unknown>,
|
||||
): any {
|
||||
return yield call(evaluateSingleValue, `{{${valueToEvaluate}}}`, params);
|
||||
}
|
||||
|
||||
const EXECUTION_PARAM_PATH = "this.params";
|
||||
const getExecutionParamPath = (key: string) => `${EXECUTION_PARAM_PATH}.${key}`;
|
||||
const EXECUTION_PARAM_REFERENCE_REGEX = /this.params/g;
|
||||
|
||||
export function* getActionParams(
|
||||
bindings: string[] | undefined,
|
||||
executionParams?: Record<string, any>,
|
||||
) {
|
||||
if (_.isNil(bindings)) return [];
|
||||
let dataTreeBindings = bindings;
|
||||
const evaluatedExecutionParams = yield evaluateDynamicBoundValueSaga(
|
||||
JSON.stringify(executionParams),
|
||||
);
|
||||
|
||||
if (executionParams && Object.keys(executionParams).length) {
|
||||
// List of params in the path format
|
||||
const executionParamsPathList = Object.keys(executionParams).map(
|
||||
getExecutionParamPath,
|
||||
);
|
||||
const paramSearchRegex = new RegExp(executionParamsPathList.join("|"), "g");
|
||||
// Bindings with references to execution params
|
||||
const executionBindings = bindings.filter(binding =>
|
||||
paramSearchRegex.test(binding),
|
||||
);
|
||||
const bindingsForExecutionParams = bindings.map(binding =>
|
||||
binding.replace(EXECUTION_PARAM_REFERENCE_REGEX, EXECUTION_PARAM_KEY),
|
||||
);
|
||||
|
||||
// Replace references with values
|
||||
const replacedBindings = executionBindings.map(binding => {
|
||||
let replaced = binding;
|
||||
const matches = binding.match(paramSearchRegex);
|
||||
if (matches && matches.length) {
|
||||
matches.forEach(match => {
|
||||
// we add one for substring index to account for '.'
|
||||
const paramKey = match.substring(EXECUTION_PARAM_PATH.length + 1);
|
||||
let paramValue = executionParams[paramKey];
|
||||
if (paramValue) {
|
||||
if (typeof paramValue === "object") {
|
||||
paramValue = JSON.stringify(paramValue);
|
||||
}
|
||||
replaced = replaced.replace(match, paramValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
return replaced;
|
||||
});
|
||||
// Replace binding with replaced bindings for evaluation
|
||||
dataTreeBindings = dataTreeBindings.map(key => {
|
||||
if (executionBindings.includes(key)) {
|
||||
return replacedBindings[executionBindings.indexOf(key)];
|
||||
}
|
||||
return key;
|
||||
});
|
||||
}
|
||||
// Evaluate all values
|
||||
const values: any = yield all(
|
||||
dataTreeBindings.map((binding: string) => {
|
||||
return call(evaluateDynamicBoundValueSaga, binding);
|
||||
bindingsForExecutionParams.map((binding: string) => {
|
||||
return call(
|
||||
evaluateDynamicBoundValueSaga,
|
||||
binding,
|
||||
evaluatedExecutionParams,
|
||||
);
|
||||
}),
|
||||
);
|
||||
// convert to object and transform non string values
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import PerformanceTracker, {
|
|||
import { Variant } from "components/ads/common";
|
||||
import { Toaster } from "components/ads/Toast";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { EXECUTION_PARAM_KEY } from "../constants/ActionConstants";
|
||||
|
||||
let evaluationWorker: Worker;
|
||||
let workerChannel: EventChannel<any>;
|
||||
|
|
@ -110,9 +111,13 @@ function* evaluateTreeSaga(postEvalActions?: ReduxAction<unknown>[]) {
|
|||
}
|
||||
}
|
||||
|
||||
export function* evaluateSingleValue(binding: string) {
|
||||
export function* evaluateSingleValue(
|
||||
binding: string,
|
||||
executionParams: Record<string, any> = {},
|
||||
) {
|
||||
if (evaluationWorker) {
|
||||
const dataTree = yield select(getDataTree);
|
||||
dataTree[EXECUTION_PARAM_KEY] = executionParams;
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.EVAL_SINGLE,
|
||||
dataTree,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import { Action } from "entities/Action";
|
|||
import moment from "moment-timezone";
|
||||
import { WidgetProps } from "../widgets/BaseWidget";
|
||||
|
||||
type StringTuple = [string, string];
|
||||
|
||||
export const removeBindingsFromActionObject = (obj: Action) => {
|
||||
const string = JSON.stringify(obj);
|
||||
const withBindings = string.replace(DATA_BIND_REGEX_GLOBAL, "{{ }}");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import { JAVSCRIPT_KEYWORDS } from "constants/WidgetValidation";
|
||||
import {
|
||||
DATA_TREE_KEYWORDS,
|
||||
JAVASCRIPT_KEYWORDS,
|
||||
} from "constants/WidgetValidation";
|
||||
import { GLOBAL_FUNCTIONS } from "./autocomplete/EntityDefinitions";
|
||||
export const snapToGrid = (
|
||||
columnWidth: number,
|
||||
rowHeight: number,
|
||||
|
|
@ -165,7 +169,7 @@ export const convertArrayToSentence = (arr: string[]) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* checks if the name is conflciting with
|
||||
* checks if the name is conflicting with
|
||||
* 1. API names,
|
||||
* 2. Queries name
|
||||
* 3. Javascript reserved names
|
||||
|
|
@ -180,9 +184,10 @@ export const isNameValid = (
|
|||
name: string,
|
||||
invalidNames: Record<string, any>,
|
||||
) => {
|
||||
if (name in JAVSCRIPT_KEYWORDS || name in invalidNames) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !(
|
||||
name in JAVASCRIPT_KEYWORDS ||
|
||||
name in DATA_TREE_KEYWORDS ||
|
||||
name in GLOBAL_FUNCTIONS ||
|
||||
name in invalidNames
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user