Add support for run time params in action execution (#98)

Adds a third parameter to the Action.run function that can be referenced inside an action config
Usage
`{{ Api1.run(...,...,{key: value}) }}` inside property pane
`{{this.params.key}}` inside action pane

* You can reference data tree properties in the params values:
`{{ Api1.run(..., ..., { key: "Input1.text.toUpperCase()" })`
* Bindings can have both params and data tree values referenced. 
* Param values can be javascript functions
`body: {{ this.params.list.map(i => Input1.text + i ) }}`
This commit is contained in:
Hetu Nandu 2020-07-17 18:25:34 +05:30 committed by GitHub
parent 70c4dd5964
commit b6c710cfa2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 13 deletions

View File

@ -28,13 +28,14 @@ export type RunActionPayload = {
actionId: string;
onSuccess: string;
onError: string;
params: Record<string, any>;
};
export interface DataTreeAction extends Omit<ActionData, "data" | "config"> {
data: ActionResponse["body"];
actionId: string;
config: Partial<ActionConfig>;
run: ActionDispatcher<RunActionPayload, [string, string]> | {};
run: ActionDispatcher<RunActionPayload, [string, string, string]> | {};
dynamicBindingPathList: Property[];
ENTITY_TYPE: ENTITY_TYPE.ACTION;
}
@ -103,13 +104,19 @@ export class DataTreeFactory {
dynamicBindingPathList,
data: a.data ? a.data.body : {},
run: withFunctions
? function(this: DataTreeAction, onSuccess: string, onError: string) {
? function(
this: DataTreeAction,
onSuccess: string,
onError: string,
params = "",
) {
return {
type: "RUN_ACTION",
payload: {
actionId: this.actionId,
onSuccess: onSuccess ? `{{${onSuccess.toString()}}}` : "",
onError: onError ? `{{${onError.toString()}}}` : "",
params,
},
};
}

View File

@ -143,20 +143,68 @@ export function* evaluateDynamicBoundValueSaga(path: string): any {
return dynamicResult.result;
}
export function* getActionParams(jsonPathKeys: string[] | undefined) {
if (_.isNil(jsonPathKeys)) return [];
const EXECUTION_PARAM_PATH = "this.params";
const getExecutionParamPath = (key: string) => `${EXECUTION_PARAM_PATH}.${key}`;
export function* getActionParams(
bindings: string[] | undefined,
executionParams?: Record<string, any>,
) {
if (_.isNil(bindings)) return [];
let dataTreeBindings = bindings;
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),
);
// 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(
jsonPathKeys.map((jsonPath: string) => {
return call(evaluateDynamicBoundValueSaga, jsonPath);
dataTreeBindings.map((binding: string) => {
return call(evaluateDynamicBoundValueSaga, binding);
}),
);
const dynamicBindings: Record<string, string> = {};
jsonPathKeys.forEach((key, i) => {
// convert to object and transform non string values
const actionParams: Record<string, string> = {};
bindings.forEach((key, i) => {
let value = values[i];
if (typeof value === "object") value = JSON.stringify(value);
dynamicBindings[key] = value;
actionParams[key] = value;
});
return mapToPropList(dynamicBindings);
return mapToPropList(actionParams);
}
export function extractBindingsFromAction(action: Action) {
@ -175,11 +223,15 @@ export function* executeActionSaga(
apiAction: RunActionPayload,
event: ExecuteActionPayloadEvent,
) {
const { actionId, onSuccess, onError } = apiAction;
const { actionId, onSuccess, onError, params } = apiAction;
try {
yield put(executeApiActionRequest({ id: apiAction.actionId }));
const api: RestAction = yield select(getAction, actionId);
const params: Property[] = yield call(getActionParams, api.jsonPathKeys);
const actionParams: Property[] = yield call(
getActionParams,
api.jsonPathKeys,
params,
);
const pagination =
event.type === EventType.ON_NEXT_PAGE
? "NEXT"
@ -188,7 +240,7 @@ export function* executeActionSaga(
: undefined;
const executeActionRequest: ExecuteActionRequest = {
action: { id: actionId },
params,
params: actionParams,
paginationField: pagination,
};
const timeout = yield select(getActionTimeout, actionId);