4615 lines
146 KiB
React
Executable File
4615 lines
146 KiB
React
Executable File
import React, { useState, useEffect, useLayoutEffect, useMemo } from "react";
|
|
import { toast } from 'react-toastify';
|
|
import { makeStyles, createStyles } from "@mui/styles";
|
|
import theme from '../theme.jsx';
|
|
|
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
|
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
|
import { GetParsedPaths } from "../views/Apps.jsx";
|
|
import { sortByKey } from "../views/AngularWorkflow.jsx";
|
|
import { NestedMenuItem } from "mui-nested-menu";
|
|
import { parsedDatatypeImages } from "../components/AppFramework.jsx";
|
|
import { green, yellow, red } from "../views/AngularWorkflow.jsx"
|
|
//import { useAlert
|
|
|
|
import {
|
|
Chip,
|
|
ButtonGroup,
|
|
Popper,
|
|
TextField,
|
|
TextareaAutosize,
|
|
Drawer,
|
|
Button,
|
|
Paper,
|
|
Tabs,
|
|
InputAdornment,
|
|
Tab,
|
|
ButtonBase,
|
|
Tooltip,
|
|
Select,
|
|
MenuItem,
|
|
Divider,
|
|
Dialog,
|
|
Modal,
|
|
DialogActions,
|
|
DialogTitle,
|
|
InputLabel,
|
|
DialogContent,
|
|
FormControl,
|
|
IconButton,
|
|
Menu,
|
|
Input,
|
|
FormGroup,
|
|
FormControlLabel,
|
|
Typography,
|
|
Checkbox,
|
|
Breadcrumbs,
|
|
CircularProgress,
|
|
Switch,
|
|
Collapse,
|
|
Autocomplete,
|
|
Box
|
|
} from "@mui/material";
|
|
|
|
import {
|
|
HelpOutline as HelpOutlineIcon,
|
|
OpenInFull as OpenInFullIcon,
|
|
Description as DescriptionIcon,
|
|
GetApp as GetAppIcon,
|
|
Search as SearchIcon,
|
|
ArrowUpward as ArrowUpwardIcon,
|
|
Visibility as VisibilityIcon,
|
|
Done as DoneIcon,
|
|
Close as CloseIcon,
|
|
Error as ErrorIcon,
|
|
FindReplace as FindreplaceIcon,
|
|
ArrowLeft as ArrowLeftIcon,
|
|
Cached as CachedIcon,
|
|
DirectionsRun as DirectionsRunIcon,
|
|
Add as AddIcon,
|
|
Polymer as PolymerIcon,
|
|
FormatListNumbered as FormatListNumberedIcon,
|
|
Create as CreateIcon,
|
|
PlayArrow as PlayArrowIcon,
|
|
AspectRatio as AspectRatioIcon,
|
|
MoreVert as MoreVertIcon,
|
|
Apps as AppsIcon,
|
|
Schedule as ScheduleIcon,
|
|
FavoriteBorder as FavoriteBorderIcon,
|
|
Pause as PauseIcon,
|
|
Delete as DeleteIcon,
|
|
AddCircleOutline as AddCircleOutlineIcon,
|
|
Save as SaveIcon,
|
|
KeyboardArrowLeft as KeyboardArrowLeftIcon,
|
|
KeyboardArrowRight as KeyboardArrowRightIcon,
|
|
ArrowBack as ArrowBackIcon,
|
|
Settings as SettingsIcon,
|
|
LockOpen as LockOpenIcon,
|
|
ExpandMore as ExpandMoreIcon,
|
|
VpnKey as VpnKeyIcon,
|
|
AutoFixHigh as AutoFixHighIcon,
|
|
Circle as CircleIcon,
|
|
SquareFoot as SquareFootIcon,
|
|
Storage as StorageIcon,
|
|
Check as CheckIcon,
|
|
PriorityHigh as PriorityHighIcon,
|
|
} from '@mui/icons-material';
|
|
|
|
export const useStyles = makeStyles({
|
|
root: {
|
|
"& .MuiAutocomplete-listbox": {
|
|
border: "2px solid grey",
|
|
color: "white",
|
|
fontSize: 18,
|
|
"& li:nth-child(even)": {
|
|
backgroundColor: "#CCC",
|
|
},
|
|
"& li:nth-child(odd)": {
|
|
backgroundColor: "#FFF",
|
|
},
|
|
},
|
|
},
|
|
inputRoot: {
|
|
color: "white",
|
|
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
borderColor: "#f86a3e",
|
|
},
|
|
},
|
|
});
|
|
|
|
const openApiFieldDesc = "Generated by OpenAPI body example";
|
|
const ParsedAction = (props) => {
|
|
const {
|
|
workflow,
|
|
files,
|
|
setWorkflow,
|
|
setAction,
|
|
setSelectedAction,
|
|
setUpdate,
|
|
appActionArguments,
|
|
selectedApp,
|
|
workflowExecutions,
|
|
setSelectedResult,
|
|
selectedAction,
|
|
setSelectedApp,
|
|
setSelectedTrigger,
|
|
setSelectedEdge,
|
|
setCurrentView,
|
|
cy,
|
|
setAuthenticationModalOpen,
|
|
setVariablesModalOpen,
|
|
setCodeModalOpen,
|
|
selectedNameChange,
|
|
rightsidebarStyle,
|
|
showEnvironment,
|
|
selectedActionEnvironment,
|
|
environments,
|
|
setNewSelectedAction,
|
|
appApiViewStyle,
|
|
globalUrl,
|
|
setSelectedActionEnvironment,
|
|
requiresAuthentication,
|
|
hideExtraTypes,
|
|
scrollConfig,
|
|
setScrollConfig,
|
|
authenticationType,
|
|
appAuthentication,
|
|
getAppAuthentication,
|
|
actionDelayChange,
|
|
getParents,
|
|
isCloud,
|
|
lastSaved,
|
|
setLastSaved,
|
|
setShowVideo,
|
|
toolsAppId,
|
|
aiSubmit,
|
|
|
|
expansionModalOpen,
|
|
setExpansionModalOpen,
|
|
fixExample,
|
|
|
|
listCache,
|
|
setActiveDialog,
|
|
authGroups,
|
|
apps,
|
|
setEditorData,
|
|
setcodedata,
|
|
setAiQueryModalOpen,
|
|
} = props;
|
|
|
|
let navigate = useNavigate()
|
|
const classes = useStyles()
|
|
|
|
const [hideBody, setHideBody] = React.useState(false)
|
|
const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false)
|
|
const [appActionName, setAppActionName] = React.useState(selectedAction?.label);
|
|
const [delay, setDelay] = React.useState(selectedAction?.execution_delay || 0);
|
|
const [prevActionName, setPrevActionName] = React.useState(selectedAction?.label);
|
|
const [fieldCount, setFieldCount] = React.useState(0);
|
|
const [hiddenDescription, setHiddenDescription] = React.useState(true);
|
|
const [hiddenParameters, setHiddenParameters] = React.useState(true);
|
|
const [autoCompleting, setAutocompleting] = React.useState(false);
|
|
const [selectedActionParameters, setSelectedActionParameters] = React.useState(selectedAction?.parameters || []);
|
|
const [selectedVariableParameter, setSelectedVariableParameter] = React.useState("");
|
|
const [paramUpdate, setParamUpdate] = React.useState("");
|
|
const [actionlist, setActionlist] = React.useState([]);
|
|
const [jsonList, setJsonList] = React.useState([]);
|
|
const [showDropdown, setShowDropdown] = React.useState(false);
|
|
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0);
|
|
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
|
|
const [menuPosition, setMenuPosition] = useState(null);
|
|
const [uiBox, setUiBox] = useState(null);
|
|
const isIntegration = selectedAction.app_id === "integration"
|
|
const isAgent = selectedAction.app_id === "shuffle_agent"
|
|
const [distributeAuthToSuborgs, setDistributeAuthToSuborgs] = useState(selectedAction?.selectedAuthentication?.suborg_distributed || false)
|
|
|
|
useEffect(() => {
|
|
if (setLastSaved !== undefined) {
|
|
setLastSaved(false)
|
|
}
|
|
}, [expansionModalOpen])
|
|
|
|
useEffect(() => {
|
|
// Changes the order of params to show in order:
|
|
// auth, required, optional
|
|
var changed = false
|
|
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
|
|
return
|
|
}
|
|
|
|
// Fixing required fields with a shitty structure :)
|
|
if (selectedApp !== undefined && selectedApp !== null && selectedApp.generated === true && selectedAction !== undefined && selectedAction !== null && selectedAction.name !== undefined && selectedAction.name !== null && selectedApp.actions !== undefined && selectedApp.actions !== null && selectedApp.actions.length > 0 && (selectedAction.required_body_fields === undefined || selectedAction.required_body_fields === null || selectedAction.required_body_fields.length === 0)) {
|
|
// Check for required fields
|
|
for (var actionkey in selectedApp.actions) {
|
|
var action = selectedApp.actions[actionkey]
|
|
if (action.name === selectedAction.name) {
|
|
selectedAction.required_body_fields = action.required_body_fields
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if missing parameters?
|
|
var auth = []
|
|
var required = []
|
|
var optional = []
|
|
|
|
var bodyfield = []
|
|
var special_optional = []
|
|
var generated_optional = []
|
|
|
|
var keyorder = []
|
|
|
|
for (let paramkey in selectedActionParameters) {
|
|
var param = selectedActionParameters[paramkey]
|
|
keyorder.push(param.name)
|
|
|
|
if (param?.configuration === true) {
|
|
auth.push(param)
|
|
continue
|
|
}
|
|
|
|
if (param?.value === undefined || param?.value === null) {
|
|
console.log("Invalid value: ", param)
|
|
continue
|
|
}
|
|
|
|
if (typeof param.value === "array") {
|
|
param.value = param.value.join(",")
|
|
}
|
|
|
|
if (typeof param.value === "object") {
|
|
try {
|
|
// Check if it's an array or object
|
|
if (param.value.length !== undefined) {
|
|
param.value = param.value.join(",")
|
|
} else {
|
|
param.value = JSON.stringify(param.value)
|
|
}
|
|
} catch (e) {
|
|
console.log("Error parsing JSON for param value: ", param, e)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if (param?.value?.toLowerCase()?.includes("secret. replace")) {
|
|
param.value = ""
|
|
}
|
|
|
|
if (selectedApp?.generated === true && param?.name === "body") {
|
|
param.required = true
|
|
bodyfield.push(param)
|
|
continue
|
|
}
|
|
|
|
if (param?.required === false && param?.name?.startsWith("${") && param?.name?.endsWith("}")) {
|
|
// Check if it's a required param
|
|
param.autocompleted = true
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
|
|
|
if (selectedAction.required_body_fields.includes(param.name)) {
|
|
param.required = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if (param.required) {
|
|
required.push(param)
|
|
continue
|
|
}
|
|
|
|
if (param?.name === "headers" || param?.name === "queries") {
|
|
special_optional.push(param)
|
|
continue
|
|
}
|
|
|
|
if (hideBody && param?.description?.includes("Generated")) {
|
|
continue
|
|
}
|
|
|
|
if (param?.field_active === true) {
|
|
param.autocompleted = true
|
|
generated_optional.push(param)
|
|
continue
|
|
}
|
|
|
|
optional.push(param)
|
|
}
|
|
|
|
// Sort order: auth > body(used for simple/advanced) > required > optional
|
|
// Optional field order:
|
|
// 1. headers & queries
|
|
// 2. other fields
|
|
// 3. generated fields & all else
|
|
|
|
|
|
const newparams = auth
|
|
.concat(bodyfield)
|
|
.concat(required)
|
|
.concat(generated_optional)
|
|
.concat(special_optional)
|
|
.concat(optional)
|
|
|
|
var newkeyorder = []
|
|
for (let paramkey in newparams) {
|
|
//console.log("Param: ", newparams[paramkey])
|
|
|
|
newkeyorder.push(newparams[paramkey].name)
|
|
}
|
|
|
|
if (keyorder.join(",") !== newkeyorder.join(",")) {
|
|
//toast("KEYORDER CHANGED!")
|
|
|
|
setSelectedActionParameters(newparams)
|
|
selectedAction.parameters = newparams
|
|
setSelectedAction(selectedAction)
|
|
}
|
|
}, [selectedActionParameters])
|
|
|
|
useEffect(() => {
|
|
const shouldHide = localStorage.getItem("hideBody")
|
|
if (shouldHide !== null) {
|
|
const ishiding = shouldHide !== "true"
|
|
if (ishiding !== hideBody) {
|
|
setHideBody(ishiding)
|
|
}
|
|
}
|
|
|
|
if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) {
|
|
|
|
if (environments !== undefined && environments !== null && environments.length > 0) {
|
|
if (selectedAction.environment !== undefined && selectedAction.environment !== null) {
|
|
|
|
const foundenv = environments.find(env => env.id === selectedAction.environment || selectedAction.environment === env.Name)
|
|
|
|
if (foundenv !== undefined && foundenv !== null) {
|
|
setSelectedActionEnvironment(foundenv)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fix apps with fewer actions
|
|
if (selectedApp !== undefined && selectedApp !== null && selectedApp.actions !== undefined && selectedApp.actions !== null && selectedApp.actions.length <= 1 && apps !== undefined && apps !== null && apps.length > 0) {
|
|
// 1. Check local storage (?)
|
|
// 2. Check the "apps" list
|
|
const foundApp = apps.find(app => app.id === selectedApp.id)
|
|
if (foundApp !== undefined && foundApp !== null && foundApp.actions !== undefined && foundApp.actions !== null && foundApp.actions.length > 1) {
|
|
setSelectedApp(foundApp)
|
|
}
|
|
|
|
}
|
|
}, [])
|
|
|
|
const keywords = [
|
|
"len(",
|
|
"lower(",
|
|
"upper(",
|
|
"trim(",
|
|
"split(",
|
|
"length(",
|
|
"number(",
|
|
"parse(",
|
|
"join(",
|
|
];
|
|
|
|
const getApp = (appId, setApp) => {
|
|
fetch(globalUrl + "/api/v1/apps/" + appId + "/config?openapi=false", {
|
|
headers: {
|
|
Accept: "application/json",
|
|
},
|
|
credentials: "include",
|
|
})
|
|
.then((response) => {
|
|
if (response.status === 200) {
|
|
//toast("Successfully GOT app "+appId)
|
|
} else {
|
|
toast("Failed getting app");
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
console.log("RESPONSE: ", responseJson);
|
|
|
|
const parsedapp =
|
|
responseJson.app !== undefined && responseJson.app !== null
|
|
? JSON.parse(atob(responseJson.app))
|
|
: {};
|
|
console.log("PARSED: ", parsedapp);
|
|
//data = parsedapp.body === undefined ? parsedapp : parsedapp.body
|
|
|
|
if (
|
|
setApp &&
|
|
parsedapp.actions !== undefined &&
|
|
parsedapp.actions !== null
|
|
) {
|
|
console.log("Inside first if");
|
|
if (
|
|
selectedApp.versions !== undefined &&
|
|
selectedApp.versions !== null
|
|
) {
|
|
parsedapp.versions = selectedApp.versions;
|
|
}
|
|
|
|
if (
|
|
selectedApp.loop_versions !== undefined &&
|
|
selectedApp.loop_versions !== null
|
|
) {
|
|
parsedapp.loop_versions = selectedApp.loop_versions;
|
|
}
|
|
|
|
// Find authentication, and if it works?
|
|
// If authentication has less OR more fields, it has to change
|
|
//console.log(selected
|
|
|
|
console.log("Inside first if2");
|
|
var foundAction = parsedapp.actions.find(
|
|
(action) =>
|
|
action.name.toLowerCase() === selectedAction.name.toLowerCase()
|
|
);
|
|
if (foundAction !== null && foundAction !== undefined) {
|
|
var foundparams = [];
|
|
for (let [paramkey, paramkeyval] in Object.entries(foundAction.parameters)) {
|
|
const param = foundAction.parameters[paramkey];
|
|
|
|
const foundParam = selectedAction.parameters.find(
|
|
(item) => item.name.toLowerCase() === param.name.toLowerCase()
|
|
);
|
|
if (foundParam === undefined) {
|
|
console.log("COULDNT find Param: ", param);
|
|
} else {
|
|
foundAction.parameters[paramkey] = foundParam;
|
|
}
|
|
//foundparams.push(param.name)
|
|
}
|
|
} else {
|
|
toast("Couldn't find action " + selectedAction.name);
|
|
}
|
|
|
|
selectedAction.errors = [];
|
|
selectedAction.is_valid = true;
|
|
|
|
// Updating params for the new action
|
|
selectedAction.parameters = foundAction.parameters;
|
|
selectedAction.app_id = appId;
|
|
selectedAction.app_version = parsedapp.app_version;
|
|
|
|
setSelectedAction(selectedAction);
|
|
setSelectedApp(parsedapp);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
toast(error.toString());
|
|
});
|
|
};
|
|
|
|
const changeDistribution = (data) => {
|
|
editAuthenticationConfig(data.id, "suborg_distribute")
|
|
}
|
|
|
|
const editAuthenticationConfig = (id, parentAction) => {
|
|
const data = {
|
|
id: id,
|
|
action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere",
|
|
}
|
|
|
|
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config";
|
|
|
|
fetch(url, {
|
|
mode: "cors",
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
credentials: "include",
|
|
crossDomain: true,
|
|
withCredentials: true,
|
|
headers: {
|
|
"Content-Type": "application/json; charset=utf-8",
|
|
},
|
|
})
|
|
.then((response) =>
|
|
response.json().then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
toast("Failed overwriting appauth");
|
|
} else {
|
|
if (distributeAuthToSuborgs) {
|
|
toast.success("Successfully updated auth");
|
|
} else {
|
|
toast.success("Successfully distributed auth to suborgs");
|
|
}
|
|
setTimeout(() => {
|
|
getAppAuthentication();
|
|
setDistributeAuthToSuborgs(!distributeAuthToSuborgs)
|
|
}, 1000);
|
|
}
|
|
})
|
|
)
|
|
.catch((error) => {
|
|
toast("Err: " + error.toString());
|
|
});
|
|
};
|
|
|
|
const defineStartnode = () => {
|
|
if (cy === undefined) {
|
|
return;
|
|
}
|
|
|
|
var oldstartnode = cy.getElementById(workflow.start);
|
|
if (oldstartnode.length > 0) {
|
|
oldstartnode[0].data("isStartNode", false);
|
|
var oldnodecnt = workflow.actions.findIndex(
|
|
(a) => a.id === workflow.start
|
|
);
|
|
if (workflow.actions[oldnodecnt] !== undefined) {
|
|
workflow.actions[oldnodecnt].isStartNode = false;
|
|
}
|
|
}
|
|
|
|
var newstartnode = cy.getElementById(selectedAction.id);
|
|
if (newstartnode.length > 0) {
|
|
newstartnode[0].data("isStartNode", true);
|
|
var newnodecnt = workflow.actions.findIndex(
|
|
(a) => a.id === selectedAction.id
|
|
);
|
|
console.log("NEW NODE CNT: ", newnodecnt);
|
|
if (workflow.actions[newnodecnt] !== undefined) {
|
|
workflow.actions[newnodecnt].isStartNode = true;
|
|
console.log(workflow.actions[newnodecnt]);
|
|
}
|
|
}
|
|
|
|
// Find branches with triggers as source nodes
|
|
// Move these targets to be the new node
|
|
// Set arrows pointing to new startnode with errors
|
|
//for (var key in workflow.branches) {
|
|
// var item = workflow.branches[key]
|
|
// if (item.destination_id === oldstartnode[0].data()["id"]) {
|
|
// var curbranch = cy.getElementById(item.id)
|
|
// if (curbranch.length > 0) {
|
|
// //console.log(curbranch[0].data())
|
|
// //curbranch[0].data("target", selectedAction.id)
|
|
// //curbranch[0].data("hasErrors", true)
|
|
// //workflow.branches[key].destination_id = selectedAction.id
|
|
// //console.log(curbranch[0].data())
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
setUpdate("start_node" + selectedAction.id);
|
|
workflow.start = selectedAction.id;
|
|
setWorkflow(workflow);
|
|
//setStartNode(selectedAction.id)
|
|
};
|
|
|
|
useEffect(() => {
|
|
// Only set app action name if it has changed
|
|
if (selectedAction.label !== appActionName) {
|
|
setAppActionName(selectedAction.label);
|
|
|
|
const shouldHide = localStorage.getItem("hideBody")
|
|
if (shouldHide !== null) {
|
|
const ishiding = shouldHide !== "true"
|
|
if (ishiding !== hideBody) {
|
|
setHideBody(ishiding)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selectedAction.label !== prevActionName) {
|
|
setPrevActionName(selectedAction.label)
|
|
}
|
|
|
|
// Only set delay if it has changed
|
|
const newDelay = selectedAction?.execution_delay || 0;
|
|
if (newDelay !== delay) {
|
|
setDelay(newDelay);
|
|
}
|
|
|
|
// Only set selected action parameters if they have changed
|
|
if (selectedAction?.parameters?.length > 0 && selectedAction.label !== appActionName) {
|
|
//console.log("PARAMS CHANGED DURING APPCHANGE: ", selectedAction.parameters)
|
|
setSelectedActionParameters(selectedAction.parameters);
|
|
}
|
|
|
|
// Only set selected variable parameter if it is null or undefined
|
|
if (!selectedVariableParameter && workflow.workflow_variables?.length > 0) {
|
|
setSelectedVariableParameter(workflow.workflow_variables[0].name);
|
|
}
|
|
}, [selectedAction, selectedApp, setNewSelectedAction, workflow, workflowExecutions, getParents])
|
|
|
|
useEffect(() => {
|
|
const newActionList = [];
|
|
const parentActionList = [];
|
|
|
|
// Process workflowExecutions
|
|
if (workflowExecutions.length > 0) {
|
|
for (let execution of workflowExecutions) {
|
|
const execArg = execution.execution_argument;
|
|
if (execArg && execArg.length > 0) {
|
|
const valid = validateJson(execArg);
|
|
if (valid.valid) {
|
|
newActionList.push({
|
|
type: "Execution Argument",
|
|
name: "Execution Argument",
|
|
value: "$exec",
|
|
highlight: "exec",
|
|
autocomplete: "exec",
|
|
example: valid.result,
|
|
})
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add default Execution Argument if none were added
|
|
if (newActionList.length === 0) {
|
|
newActionList.push({
|
|
type: "Execution Argument",
|
|
name: "Execution Argument",
|
|
value: "$exec",
|
|
highlight: "exec",
|
|
autocomplete: "exec",
|
|
example: "",
|
|
})
|
|
}
|
|
|
|
// Look for cachekey
|
|
if (newActionList.find((item) => item.type === "Shuffle DB") === undefined) {
|
|
let cacheKey = {
|
|
type: "Shuffle DB",
|
|
name: "Shuffle DB",
|
|
value: "$shuffle_cache",
|
|
highlight: "shuffle_cache",
|
|
autocomplete: "shuffle_cache",
|
|
example: "",
|
|
};
|
|
|
|
if (listCache?.keys?.length > 0) {
|
|
cacheKey.example = {};
|
|
for (let item of listCache.keys) {
|
|
if (item.key) {
|
|
let itemValue = item.value ?? "";
|
|
if (itemValue.length > 10000) {
|
|
itemValue = "";
|
|
}
|
|
cacheKey.example[item.key.split(" ").join("_")] = { value: itemValue };
|
|
}
|
|
}
|
|
}
|
|
|
|
newActionList.push(cacheKey);
|
|
}
|
|
|
|
// Process workflow variables
|
|
if (workflow.workflow_variables?.length > 0) {
|
|
for (let variable of workflow.workflow_variables) {
|
|
newActionList.push({
|
|
type: "workflow_variable",
|
|
name: variable.name,
|
|
value: variable.value,
|
|
id: variable.id,
|
|
autocomplete: variable.name.split(" ").join("_"),
|
|
example: variable.value,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Process execution variables
|
|
if (workflow.execution_variables?.length > 0) {
|
|
for (let variable of workflow.execution_variables) {
|
|
let exampleOutput = "";
|
|
for (let exec of workflowExecutions) {
|
|
const foundExec = exec.execution_variables?.find(exvar => exvar.name === variable.name);
|
|
if (foundExec?.value) {
|
|
exampleOutput = foundExec.value;
|
|
break;
|
|
}
|
|
}
|
|
newActionList.push({
|
|
type: "execution_variable",
|
|
name: variable.name,
|
|
value: variable.value,
|
|
id: variable.id,
|
|
autocomplete: variable.name.split(" ").join("_"),
|
|
example: exampleOutput,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Process parent actions if getParents is provided
|
|
if (getParents) {
|
|
const parents = getParents(selectedAction);
|
|
if (parents.length > 1) {
|
|
const labels = [];
|
|
for (let parentNode of parents) {
|
|
if (parentNode.label !== "Execution Argument" && !labels.includes(parentNode.label)) {
|
|
labels.push(parentNode.label);
|
|
let exampleData = parentNode.example ?? "";
|
|
if (!exampleData && workflowExecutions.length > 0) {
|
|
for (let exec of workflowExecutions) {
|
|
const foundResult = exec.results?.find(result => result.action.id === parentNode.id);
|
|
if (foundResult) {
|
|
const valid = validateJson(foundResult.result);
|
|
if (valid.valid && valid.result.success !== false) {
|
|
exampleData = valid.result;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parentNode.label === undefined) {
|
|
parentNode.label = ""
|
|
}
|
|
|
|
newActionList.push({
|
|
type: "action",
|
|
id: parentNode.id,
|
|
name: parentNode.label,
|
|
autocomplete: parentNode.label.split(" ").join("_"),
|
|
example: exampleData,
|
|
});
|
|
|
|
parentActionList.push({
|
|
type: "action",
|
|
id: parentNode.id,
|
|
name: parentNode.label,
|
|
autocomplete: parentNode.label.split(" ").join("_"),
|
|
example: exampleData,
|
|
});
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let newParameters = selectedAction?.parameters?.map((param) => {
|
|
let paramvalue = param.value === undefined || param.value === null ? "" : param.value;
|
|
let errorVars = [];
|
|
|
|
if (paramvalue.includes("$")) {
|
|
let actions = workflow.actions?.map((action) => {
|
|
return "$" + action.label?.toLowerCase();
|
|
})
|
|
|
|
if (newActionList?.length > 0) {
|
|
let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase());
|
|
let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action))
|
|
notPresentAction?.forEach((action) => {
|
|
action = action.replace(" ", "_");
|
|
if (paramvalue.includes(action)) {
|
|
errorVars.push(action);
|
|
// paramvalue = paramvalue.replace(action, "")
|
|
// paramvalue = paramvalue.replace(/^\s*[\r\n]/gm, "");
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
let message = "";
|
|
if (errorVars.length > 0) {
|
|
if (errorVars.length === 1) {
|
|
message = errorVars[0] + " is not accessible in this action.";
|
|
} else {
|
|
message = errorVars.join(", ") + " are not accessible in this action.";
|
|
}
|
|
}
|
|
|
|
if (param?.configuration && param?.name !== "url") {
|
|
let regex = /(^|[^\\])\$/;
|
|
if (regex.test(paramvalue)) {
|
|
if (message.length > 0) {
|
|
message += "\nUse \"\\$\" instead of \"$\" if you want to escape $ (1)";
|
|
} else {
|
|
message = "Use \"\\$\" instead of \"$\" if you want to escape $ (2)";
|
|
}
|
|
}
|
|
}
|
|
return { ...param, value: paramvalue, error: message }
|
|
});
|
|
|
|
setSelectedActionParameters(newParameters);
|
|
setActionlist(newActionList);
|
|
}, [workflow.execution_variables, paramUpdate, workflow.workflow_variables, workflowExecutions, workflow, selectedAction, listCache, getParents, setNewSelectedAction]);
|
|
|
|
useEffect(() => {
|
|
selectedNameChange(appActionName)
|
|
|
|
if (actionDelayChange !== undefined) {
|
|
actionDelayChange(delay)
|
|
}
|
|
}, [appActionName, delay])
|
|
|
|
const handleParamChange = (event, count, data) => {
|
|
const newParams = [...selectedActionParameters];
|
|
newParams.map((param) => {
|
|
if (param.name === data.name) {
|
|
param.value = event.target.value;
|
|
}
|
|
})
|
|
setSelectedActionParameters(newParams);
|
|
setParamUpdate(event.target.value);
|
|
changeActionParameter(event, count, data)
|
|
}
|
|
|
|
const calculateHelpertext = (input_data) => {
|
|
var helperText = ""
|
|
var looperText = ""
|
|
//const found = input_data.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g)
|
|
var found = input_data.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
|
|
|
|
if (found !== null && found !== undefined) {
|
|
var new_occurences = []
|
|
for (let [key, keyval] in Object.entries(found)) {
|
|
if (found[key][0] !== "\\") {
|
|
new_occurences.push(found[key])
|
|
}
|
|
}
|
|
|
|
found = new_occurences.valueOf()
|
|
}
|
|
|
|
if (found !== null) {
|
|
try {
|
|
// When the found array is empty.
|
|
for (let i = 0; i < found.length; i++) {
|
|
const variableSplit = found[i].split(".#")
|
|
if ((variableSplit.length - 1) > 1) {
|
|
//console.log("Larger than 1: ", variableSplit)
|
|
if (looperText.length === 0) {
|
|
looperText += "PS: Double looping (.#.#) may cause problems."
|
|
}
|
|
}
|
|
|
|
var foundSlice = false
|
|
for (let j = 0; j < actionlist.length; j++) {
|
|
//console.log("ACTION: ", found[i], actionlist[j])
|
|
//console.log("ACTION :", found[i].split(".")[0].slice(1,).toLowerCase(), actionlist[j].autocomplete.toLowerCase())
|
|
if (found[i].split(".")[0].slice(1,).toLowerCase() == actionlist[j].autocomplete.toLowerCase()) {
|
|
//console.log("Found: ", found[i])
|
|
// Validate path?
|
|
|
|
foundSlice = true
|
|
}
|
|
}
|
|
|
|
if (!foundSlice) {
|
|
if (!helperText.includes("Invalid variables")) {
|
|
helperText += "Invalid variables: "
|
|
}
|
|
helperText += found[i] + ", "
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log("Parsing error: ", e)
|
|
}
|
|
}
|
|
|
|
if (looperText.length > 0) {
|
|
if (helperText.length > 0) {
|
|
helperText += ". "
|
|
}
|
|
|
|
helperText += looperText
|
|
}
|
|
|
|
return helperText
|
|
}
|
|
|
|
const changeActionParameter = (event, count, data, viewForceUpdate) => {
|
|
//console.log("Action change: ", selectedAction, data)
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
// PARAM FIX - Gonna use the ID field, even though it's a hack
|
|
const paramcheck = selectedAction.parameters.find((param) => param.name === "body");
|
|
|
|
if (paramcheck !== undefined) {
|
|
// Escapes all double quotes
|
|
//var toReplace = event.target.value.trim()
|
|
var toReplace = event.target.value
|
|
|
|
|
|
if (!toReplace.startsWith("{") && !toReplace.startsWith("[")) {
|
|
toReplace = toReplace.replaceAll('\\"', '"').replaceAll('"', '\\"')
|
|
}
|
|
|
|
if (
|
|
paramcheck["value_replace"] === undefined ||
|
|
paramcheck["value_replace"] === null
|
|
) {
|
|
paramcheck["value_replace"] = [
|
|
{
|
|
key: data.name,
|
|
value: toReplace,
|
|
},
|
|
];
|
|
|
|
} else {
|
|
const subparamindex = paramcheck["value_replace"].findIndex(
|
|
(param) => param.key === data.name
|
|
);
|
|
if (subparamindex === -1) {
|
|
paramcheck["value_replace"].push({
|
|
key: data.name,
|
|
value: toReplace,
|
|
});
|
|
} else {
|
|
paramcheck["value_replace"][subparamindex]["value"] = toReplace;
|
|
}
|
|
}
|
|
|
|
if (selectedActionParameters[count].value_replace === undefined) {
|
|
selectedActionParameters[count].value_replace = paramcheck
|
|
}
|
|
|
|
if (selectedAction?.parameters[count] !== undefined && selectedAction?.parameters[count].value_replace === undefined) {
|
|
selectedAction.parameters[count].value_replace = paramcheck
|
|
}
|
|
|
|
if (paramcheck["value_replace"] === undefined) {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck
|
|
|
|
if (selectedAction?.parameters[count] !== undefined) {
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck
|
|
}
|
|
} else {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck["value_replace"];
|
|
if (selectedAction?.parameters[count] !== undefined) {
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"];
|
|
}
|
|
}
|
|
setSelectedAction(selectedAction);
|
|
//setUpdate(Math.random())
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (event.target.value[event.target.value.length - 1] === "$") {
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false);
|
|
setShowDropdown(true);
|
|
setShowDropdownNumber(count);
|
|
}
|
|
} else {
|
|
if (showDropdown) {
|
|
setShowDropdown(false);
|
|
}
|
|
}
|
|
|
|
// bad detection mechanism probably
|
|
if (event.target.value[event.target.value.length - 1] === "." && actionlist.length > 0) {
|
|
console.log("GET THE LAST ARGUMENT FOR NODE!");
|
|
// THIS IS AN EXAMPLE OF SHOWING IT
|
|
/*
|
|
|
|
const inputdata = {"data": "1.2.3.4", "dataType": "4.5.6.6"}
|
|
setJsonList(GetParsedPaths(inputdata, ""))
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
console.log(jsonList)
|
|
*/
|
|
|
|
// Search for the item backwards
|
|
// 1. Reverse search backwards from . -> $
|
|
// 2. Search the actionlist for the item
|
|
// 3. Find the data for the specific item
|
|
|
|
var curstring = "";
|
|
var record = false;
|
|
for (let [key, keyval] in Object.entries(selectedActionParameters[count].value)) {
|
|
const item = selectedActionParameters[count].value[key];
|
|
if (record) {
|
|
curstring += item;
|
|
}
|
|
|
|
if (item === "$") {
|
|
record = true;
|
|
curstring = "";
|
|
}
|
|
}
|
|
|
|
//console.log("CURSTRING: ", curstring)
|
|
if (curstring.length > 0 && actionlist !== null) {
|
|
// Search back in the action list
|
|
curstring = curstring.split(" ").join("_").toLowerCase();
|
|
var actionItem = actionlist.find(
|
|
(data) =>
|
|
data.autocomplete.split(" ").join("_").toLowerCase() === curstring
|
|
);
|
|
if (actionItem !== undefined) {
|
|
console.log("Found item: ", actionItem);
|
|
|
|
//actionItem.example = actionItem.example.trim()
|
|
//actionItem.example = actionItem.example.split(" None").join(" \"None\"")
|
|
//actionItem.example = actionItem.example.split("\'").join("\"")
|
|
|
|
var jsonvalid = true;
|
|
try {
|
|
const tmp = String(JSON.parse(actionItem.example));
|
|
if (
|
|
!actionItem.example.includes("{") &&
|
|
!actionItem.example.includes("[")
|
|
) {
|
|
jsonvalid = false;
|
|
}
|
|
} catch (e) {
|
|
jsonvalid = false;
|
|
}
|
|
|
|
if (jsonvalid) {
|
|
setJsonList(GetParsedPaths(JSON.parse(actionItem.example), ""));
|
|
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false);
|
|
setShowDropdown(true);
|
|
setShowDropdownNumber(count);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (jsonList.length > 0) {
|
|
setJsonList([]);
|
|
}
|
|
}
|
|
|
|
selectedActionParameters[count].autocompleted = false
|
|
selectedAction.parameters[count].autocompleted = false
|
|
selectedActionParameters[count].value = event.target.value;
|
|
selectedAction.parameters[count].value = event.target.value;
|
|
|
|
var forceUpdate = false
|
|
if (isCloud && (selectedAction.app_name === "Shuffle Tools" || selectedAction.app_name === "email") && (selectedAction.name === "send_email_shuffle" || selectedAction.name === "send_sms_shuffle") && data.name === "apikey") {
|
|
console.log("APIKEY - this shouldn't show up!")
|
|
}
|
|
|
|
if (selectedAction.app_name === "Shuffle Tools" && selectedAction.name === "filter_list" && data.name === "input_list") {
|
|
//console.log("FILTER LIST!: ", event, count, data)
|
|
const parsedvalue = event.target.value
|
|
if (parsedvalue.includes(".#")) {
|
|
const splitparsed = parsedvalue.split(".#.")
|
|
//console.log("Cant contain #: ", splitparsed)
|
|
if (splitparsed.length > 1) {
|
|
data.value = splitparsed[0]
|
|
|
|
selectedActionParameters[count].value = splitparsed[0]
|
|
selectedAction.parameters[count].value = splitparsed[0]
|
|
|
|
selectedActionParameters[1].value = splitparsed[1]
|
|
selectedAction.parameters[1].value = splitparsed[1]
|
|
} else {
|
|
// Remove .# and after
|
|
const splitparsed = parsedvalue.split(".#")
|
|
data.value = splitparsed[0]
|
|
selectedActionParameters[0].value = splitparsed[0]
|
|
selectedAction.parameters[0].value = splitparsed[0]
|
|
|
|
selectedActionParameters[1].value = ""
|
|
selectedAction.parameters[1].value = ""
|
|
|
|
toast.warn("No value found in the list. Please select an item in the list to filter based on.")
|
|
}
|
|
|
|
forceUpdate = true
|
|
selectedActionParameters[0].autocompleted = true
|
|
selectedAction.parameters[0].autocompleted = true
|
|
selectedActionParameters[1].autocompleted = true
|
|
selectedAction.parameters[1].autocompleted = true
|
|
}
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
if (forceUpdate || viewForceUpdate === true) {
|
|
setUpdate(Math.random())
|
|
}
|
|
|
|
//console.log("END OF THIS THING")
|
|
//setUpdate(event.target.value)
|
|
};
|
|
|
|
|
|
const changeActionParameterCodeMirror = (event, count, data) => {
|
|
if (data.startsWith("${") && data.endsWith("}")) {
|
|
// PARAM FIX - Gonna use the ID field, even though it's a hack
|
|
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
|
|
if (paramcheck !== undefined) {
|
|
// Escapes all double quotes
|
|
const toReplace = event.target.value.trim().replaceAll("\\\"", "\"").replaceAll("\"", "\\\"");
|
|
console.log("REPLACE WITH: ", toReplace)
|
|
if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) {
|
|
paramcheck["value_replace"] = [{
|
|
"key": data.name,
|
|
"value": toReplace,
|
|
}]
|
|
|
|
console.log("IN IF: ", paramcheck)
|
|
|
|
} else {
|
|
const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name)
|
|
if (subparamindex === -1) {
|
|
paramcheck["value_replace"].push({
|
|
"key": data.name,
|
|
"value": toReplace,
|
|
})
|
|
} else {
|
|
paramcheck["value_replace"][subparamindex]["value"] = toReplace
|
|
}
|
|
}
|
|
//console.log("PARAM: ", paramcheck)
|
|
//if (paramcheck.id === undefined) {
|
|
// console.log("Normal paramcheck")
|
|
//} else {
|
|
// selectedActionParameters[count]["value_replace"] = paramcheck
|
|
// selectedAction.parameters[count]["value_replace"] = paramcheck
|
|
//}
|
|
|
|
if (paramcheck["value_replace"] === undefined) {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck
|
|
} else {
|
|
selectedActionParameters[count]["value_replace"] = paramcheck["value_replace"]
|
|
selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"]
|
|
}
|
|
console.log("RESULT: ", selectedAction)
|
|
setSelectedAction(selectedAction)
|
|
//setUpdate(Math.random())
|
|
return
|
|
}
|
|
}
|
|
|
|
if (event.target.value[event.target.value.length - 1] === "$") {
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
} else {
|
|
if (showDropdown) {
|
|
setShowDropdown(false)
|
|
}
|
|
}
|
|
|
|
|
|
// bad detection mechanism probably
|
|
if (event.target.value[event.target.value.length - 1] === "." && actionlist.length > 0) {
|
|
console.log("GET THE LAST ARGUMENT FOR NODE!")
|
|
// THIS IS AN EXAMPLE OF SHOWING IT
|
|
/*
|
|
const inputdata = {"data": "1.2.3.4", "dataType": "4.5.6.6"}
|
|
setJsonList(GetParsedPaths(inputdata, ""))
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
console.log(jsonList)
|
|
*/
|
|
|
|
// Search for the item backwards
|
|
// 1. Reverse search backwards from . -> $
|
|
// 2. Search the actionlist for the item
|
|
// 3. Find the data for the specific item
|
|
|
|
var curstring = ""
|
|
var record = false
|
|
for (let [key, keyval] in Object.entries(selectedActionParameters[count].value)) {
|
|
const item = selectedActionParameters[count].value[key]
|
|
if (record) {
|
|
curstring += item
|
|
}
|
|
|
|
if (item === "$") {
|
|
record = true
|
|
curstring = ""
|
|
}
|
|
}
|
|
|
|
//console.log("CURSTRING: ", curstring)
|
|
if (curstring.length > 0 && actionlist !== null) {
|
|
// Search back in the action list
|
|
curstring = curstring.split(" ").join("_").toLowerCase()
|
|
var actionItem = actionlist.find(data => data.autocomplete.split(" ").join("_").toLowerCase() === curstring)
|
|
if (actionItem !== undefined) {
|
|
console.log("Found item: ", actionItem)
|
|
|
|
//actionItem.example = actionItem.example.trim()
|
|
//actionItem.example = actionItem.example.split(" None").join(" \"None\"")
|
|
//actionItem.example = actionItem.example.split("\'").join("\"")
|
|
|
|
var jsonvalid = true
|
|
try {
|
|
const tmp = String(JSON.parse(actionItem.example))
|
|
if (!actionItem.example.includes("{") && !actionItem.example.includes("[")) {
|
|
jsonvalid = false
|
|
}
|
|
} catch (e) {
|
|
jsonvalid = false
|
|
}
|
|
|
|
if (jsonvalid) {
|
|
setJsonList(GetParsedPaths(JSON.parse(actionItem.example), ""))
|
|
|
|
if (!showDropdown) {
|
|
setShowAutocomplete(false)
|
|
setShowDropdown(true)
|
|
setShowDropdownNumber(count)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (jsonList.length > 0) {
|
|
setJsonList([])
|
|
}
|
|
}
|
|
|
|
setTimeout(() => {
|
|
selectedActionParameters[count].autocompleted = false
|
|
selectedAction.parameters[count].autocompleted = false
|
|
selectedActionParameters[count].value = data
|
|
selectedAction.parameters[count].value = data
|
|
}, 100);
|
|
setSelectedAction(selectedAction)
|
|
//setUpdate(Math.random())
|
|
//setUpdate(event.target.value)
|
|
}
|
|
|
|
|
|
const changeActionParameterVariable = (fieldvalue, count) => {
|
|
//console.log("CALLED THIS ONE WITH VALUE!", fieldvalue)
|
|
//if (selectedVariableParameter === fieldvalue) {
|
|
// return
|
|
//}
|
|
|
|
setSelectedVariableParameter(fieldvalue);
|
|
|
|
selectedActionParameters[count].action_field = fieldvalue;
|
|
selectedAction.parameters = selectedActionParameters;
|
|
|
|
setSelectedApp(selectedApp);
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(fieldvalue);
|
|
};
|
|
|
|
// Sets ACTION_RESULT things
|
|
const changeActionParameterActionResult = (fieldvalue, count) => {
|
|
//cy.nodes().forEach(function( ele ) {
|
|
// if (ele.data()["label"] === fieldvalue) {
|
|
// selectedActionParameters[count].action_field = ele.id()
|
|
// return
|
|
// }
|
|
//});
|
|
|
|
selectedActionParameters[count].action_field = fieldvalue;
|
|
selectedAction.parameters = selectedActionParameters;
|
|
|
|
// FIXME - check if startnode
|
|
|
|
// Set value
|
|
setSelectedApp(selectedApp);
|
|
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
};
|
|
|
|
const changeActionParameterVariant = (data, count) => {
|
|
selectedActionParameters[count].variant = data;
|
|
selectedActionParameters[count].value = "";
|
|
|
|
if (data === "ACTION_RESULT" && getParents !== undefined) {
|
|
var parents = getParents(selectedAction);
|
|
if (parents.length > 0) {
|
|
selectedActionParameters[count].action_field = parents[0].label;
|
|
} else {
|
|
selectedActionParameters[count].action_field = "";
|
|
}
|
|
} else if (data === "WORKFLOW_VARIABLE") {
|
|
if (
|
|
workflow.workflow_variables !== null &&
|
|
workflow.workflow_variables !== undefined &&
|
|
workflow.workflow_variables.length > 0
|
|
) {
|
|
selectedActionParameters[count].action_field =
|
|
workflow.workflow_variables[0].name;
|
|
}
|
|
}
|
|
|
|
selectedAction.parameters = selectedActionParameters;
|
|
|
|
// This is a stupid workaround to make it refresh rofl
|
|
setSelectedAction({});
|
|
|
|
if (setSelectedTrigger !== undefined) {
|
|
setSelectedTrigger({});
|
|
setSelectedApp({});
|
|
setSelectedEdge({});
|
|
}
|
|
// FIXME - check if startnode
|
|
|
|
// Set value
|
|
setSelectedApp(selectedApp);
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
};
|
|
|
|
|
|
const returnHelperText = (name, value) => {
|
|
if (name === undefined || value === undefined || name === null || value === null || name === "" || value === "") {
|
|
//console.log("RETURNING EMPTY")
|
|
return ""
|
|
}
|
|
|
|
var helperText = ""
|
|
if (name.includes("url")) {
|
|
if (value.includes("localhost") || value.includes("127.0.0.1")) {
|
|
helperText = "Can't use localhost in Shuffle. Please change to server's IP."
|
|
}
|
|
}
|
|
|
|
// Helpertext for openapi fields
|
|
//if (helperText === "" && name === "body" && selectedApp.generated && selectedApp.activated) {
|
|
// helperText = <span style={{color: "white", marginBottom: 5, marginleft: 5}}>
|
|
//}
|
|
|
|
return helperText
|
|
}
|
|
|
|
const errorHelperText = (name, value, error) => {
|
|
return (
|
|
<div style={{ whiteSpace: 'pre-line' }}>
|
|
{error}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
|
|
const analyzeFields = () => {
|
|
|
|
if (selectedAction === undefined || selectedAction === null) {
|
|
return null
|
|
}
|
|
|
|
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
|
|
return null
|
|
}
|
|
|
|
// Only shuffle tools for now
|
|
if (selectedAction.app_name !== "Shuffle Tools") {
|
|
return null
|
|
}
|
|
|
|
// Custom rules
|
|
if (selectedAction.name === "set_cache_value") {
|
|
var actionKey = ""
|
|
var actionValue = ""
|
|
for (let [key, keyval] in Object.entries(selectedActionParameters)) {
|
|
const param = selectedActionParameters[key]
|
|
if (param.name === "key") {
|
|
actionKey = param.value
|
|
}
|
|
|
|
if (param.name === "value") {
|
|
actionValue = param.value
|
|
}
|
|
}
|
|
|
|
if (actionKey === "" || actionValue === "") {
|
|
return null
|
|
}
|
|
|
|
if (!actionKey.includes(".#") && actionValue.includes(".#")) {
|
|
return <span>When the key ({actionKey}) is static, but the value is a list ({actionValue}), it will overwrite the list. You may be looking for the <span onClick={() => { }} style={{ cursor: "pointer", color: "#FF8544", }}>Check Cache Contains</span> action instead.</span>
|
|
}
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
const suggestionInfo = () => {
|
|
const suggestionText = analyzeFields()
|
|
if (suggestionText === undefined || suggestionText === null) {
|
|
return null
|
|
}
|
|
|
|
if (selectedAction.errors === undefined || selectedAction.errors === null || selectedAction.errors.length === 0) {
|
|
selectedAction.errors = ["Suggestion: " + suggestionText]
|
|
}
|
|
|
|
return <Paper style={{ padding: 10, backgroundColor: theme.palette.surfaceColor, border: "1px solid red", }}>
|
|
<Typography variant="body" style={{ color: "white", }}>
|
|
<b>Tip:</b> {suggestionText}
|
|
</Typography>
|
|
</Paper>
|
|
}
|
|
|
|
// FIXME: Issue #40 - selectedActionParameters not reset
|
|
if (Object.getOwnPropertyNames(selectedAction)?.length > 0 && selectedActionParameters?.length > 0) {
|
|
var wrapperapp = {
|
|
"id": "",
|
|
"name": "noapp",
|
|
"large_image": "",
|
|
}
|
|
|
|
var actionname = selectedAction.name.toLowerCase()
|
|
if (actionname === "email" || actionname === "communication") {
|
|
actionname = "comms"
|
|
}
|
|
|
|
if (isIntegration) {
|
|
|
|
// Check if actionname uppercase is in the parsedDatatypeImages() dictionary
|
|
if (parsedDatatypeImages()[actionname.toUpperCase()] !== undefined) {
|
|
var newimage = parsedDatatypeImages()[actionname.toUpperCase()]
|
|
//newimage = <img src={newimage} style={{width: 35, height: 35, marginRight: 5, borderRadius: 5, cursor: "pointer", border: noAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)"}} />
|
|
wrapperapp.large_image = newimage
|
|
} else {
|
|
console.log("Couldn't find actionname: ", actionname)
|
|
}
|
|
}
|
|
|
|
var authWritten = false;
|
|
var noAppSelected = false
|
|
if (selectedAction.parameters !== undefined && selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
|
|
var paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name")
|
|
if (paramIndex === -1 || selectedAction.parameters[paramIndex].value === "" || selectedAction.parameters[paramIndex].value === "noapp") {
|
|
// Check the actual value and if it's the same
|
|
noAppSelected = true
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
const ActionSelectOption = (actionprops) => {
|
|
const { option, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops;
|
|
const [hover, setHover] = React.useState(false);
|
|
|
|
return (
|
|
<Tooltip
|
|
color="secondary"
|
|
title={newActiondescription}
|
|
placement="left"
|
|
>
|
|
<div style={{
|
|
cursor: "pointer",
|
|
padding: 8,
|
|
paddingLeft: 14,
|
|
paddingBottom: 4,
|
|
backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor,
|
|
}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
|
onClick={(event) => {
|
|
// event.preventDefault()
|
|
//setSelectedAction(actionprops)
|
|
//setShowActionList(false)
|
|
//setUpdate(Math.random())
|
|
//
|
|
if (option !== undefined && option !== null) {
|
|
setNewSelectedAction({
|
|
target: {
|
|
value: option.name
|
|
}
|
|
});
|
|
}
|
|
|
|
document.activeElement.blur();
|
|
|
|
const disabledUiBox = localStorage.getItem("disabled_ui_box")
|
|
if (disabledUiBox === "true") {
|
|
} else {
|
|
setHiddenDescription(false)
|
|
}
|
|
}}
|
|
>
|
|
<div style={{ display: "flex", marginBottom: 0, }}>
|
|
<span
|
|
style={{
|
|
marginRight: 10,
|
|
marginTop: "auto",
|
|
marginBottom: 0,
|
|
}}
|
|
>
|
|
{useIcon}
|
|
</span>
|
|
<span style={{ marginBottom: 0, marginTop: 3, }}>{newActionname}</span>
|
|
</div>
|
|
{extraDescription.length > 0 ?
|
|
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block", }}>
|
|
{extraDescription}
|
|
</Typography>
|
|
: null}
|
|
</div>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
|
|
const sortByCategoryLabel = (a, b) => {
|
|
const aHasCategoryLabel = a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0
|
|
const bHasCategoryLabel = b.category_label !== undefined && b.category_label !== null && b.category_label.length > 0
|
|
|
|
// Sort by existence and length of "category_label"
|
|
if (aHasCategoryLabel && !bHasCategoryLabel) {
|
|
return -1
|
|
} else if (!aHasCategoryLabel && bHasCategoryLabel) {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Function to deduplicate based on the "name" field
|
|
const deduplicateByName = (array) => {
|
|
const uniqueNames = {};
|
|
return array.filter(item => {
|
|
if (!item.hasOwnProperty('name') || !item.name.length) {
|
|
return true
|
|
}
|
|
if (!uniqueNames[item.name]) {
|
|
uniqueNames[item.name] = true
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
// Gets the most important actions first
|
|
const renderedActionOptions = deduplicateByName((
|
|
selectedApp.actions === undefined || selectedApp.actions === null ? [] :
|
|
selectedApp.actions.filter((a) =>
|
|
a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))
|
|
).sort(sortByCategoryLabel))
|
|
|
|
|
|
const selectedAppIcon = selectedAction.large_image
|
|
|
|
var newAppname = selectedAction?.name?.charAt(0).toUpperCase() + selectedAction?.name?.substring(1)
|
|
if (newAppname === undefined || newAppname === null) {
|
|
newAppname = ""
|
|
} else {
|
|
try {
|
|
newAppname = newAppname?.replaceAll("_", " ")
|
|
} catch (e) {
|
|
console.log("Error in replace newappname: ", e)
|
|
}
|
|
|
|
}
|
|
|
|
var optionalFound = false
|
|
return (
|
|
<div style={appApiViewStyle} id="parsed_action_view">
|
|
|
|
{hideExtraTypes === true ? null : (
|
|
<span>
|
|
<div style={{ display: "flex", minHeight: 40, marginBottom: 30 }}>
|
|
<div style={{ flex: 1 }}>
|
|
<div style={{ display: "flex", }}
|
|
onClick={() => {
|
|
//window.open("/apps/${selectedAction.app_id}", "_blank")
|
|
}}
|
|
>
|
|
<Tooltip title={"App: " + selectedAction.app_name} placement="top">
|
|
<img src={selectedAppIcon} style={{
|
|
width: 30,
|
|
height: 30,
|
|
marginRight: 10,
|
|
borderRadius: 5,
|
|
marginTop: 13,
|
|
border: "2px solid rgba(255,255,255,0.3)",
|
|
}} />
|
|
</Tooltip>
|
|
|
|
<h3 style={{}}>
|
|
{newAppname}
|
|
</h3>
|
|
</div>
|
|
<div style={{ display: "flex", marginTop: 0, }}>
|
|
<IconButton
|
|
style={{
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
height: 30,
|
|
paddingLeft: 0,
|
|
paddingRight: 0,
|
|
}}
|
|
onClick={() => {
|
|
if (workflowExecutions.length > 0) {
|
|
// Look for the ID
|
|
var found = false;
|
|
for (let [key, keyval] in Object.entries(workflowExecutions)) {
|
|
if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) {
|
|
continue;
|
|
}
|
|
|
|
var foundResult = workflowExecutions[key].results.find(
|
|
(result) => result.action.id === selectedAction.id
|
|
)
|
|
|
|
if (foundResult === undefined || foundResult === null) {
|
|
continue
|
|
}
|
|
|
|
const oldstartnode = cy.getElementById(selectedAction.id);
|
|
if (oldstartnode !== undefined && oldstartnode !== null) {
|
|
const foundname = oldstartnode.data("label")
|
|
if (foundname !== undefined && foundname !== null) {
|
|
foundResult.action.label = foundname
|
|
}
|
|
}
|
|
|
|
setSelectedResult(foundResult);
|
|
if (setCodeModalOpen !== undefined) {
|
|
setCodeModalOpen(true)
|
|
|
|
found = true
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
if (!found) {
|
|
toast("No result for this action yet. Please run the workflow first.")
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title="See previous results for this action"
|
|
placement="top"
|
|
>
|
|
<ArrowLeftIcon style={{ color: "rgba(255,255,255,0.7)" }} />
|
|
</Tooltip>
|
|
</IconButton>
|
|
<IconButton
|
|
style={{
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
height: 30,
|
|
marginLeft: 15,
|
|
paddingRight: 0,
|
|
}}
|
|
onClick={() => {
|
|
setAuthenticationModalOpen(true)
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title="Find app documentation"
|
|
placement="top"
|
|
>
|
|
<DescriptionIcon style={{ color: "rgba(255,255,255,0.7)" }} />
|
|
</Tooltip>
|
|
</IconButton>
|
|
|
|
<IconButton
|
|
style={{
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
height: 30,
|
|
marginLeft: 15,
|
|
paddingRight: 0,
|
|
}}
|
|
disabled={autoCompleting}
|
|
onClick={() => {
|
|
if (setAiQueryModalOpen !== undefined) {
|
|
setAiQueryModalOpen(true)
|
|
} else {
|
|
aiSubmit("Fill based on previous values", undefined, undefined, selectedAction)
|
|
}
|
|
|
|
setAutocompleting(true)
|
|
setTimeout(() => {
|
|
setAutocompleting(false)
|
|
}, 3000)
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Autocomplete the action"}
|
|
placement="top"
|
|
>
|
|
{autoCompleting ?
|
|
<CircularProgress style={{ height: 20, width: 20, }} />
|
|
:
|
|
<AutoFixHighIcon style={{ color: "rgba(255,255,255,0.7)", height: 24, }} />
|
|
}
|
|
</Tooltip>
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
{selectedApp.versions !== null &&
|
|
selectedApp.versions !== undefined &&
|
|
selectedApp.versions.length > 1 ? (
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
value={selectedAction.app_version}
|
|
onChange={(event) => {
|
|
const newversion = selectedApp.versions.find(
|
|
(tmpApp) => tmpApp.version == event.target.value
|
|
)
|
|
|
|
if (newversion !== undefined && newversion !== null) {
|
|
getApp(newversion.id, true)
|
|
}
|
|
|
|
// Change in all actions in the workflow at the same time and add a toast.success() about it
|
|
for (var actionkey in workflow.actions) {
|
|
const action = workflow.actions[actionkey]
|
|
if (action.app_name === selectedAction.app_name) {
|
|
workflow.actions[actionkey].app_version = event.target.value
|
|
}
|
|
}
|
|
|
|
toast.success("Changed version of all nodes to " + event.target.value)
|
|
}}
|
|
style={{
|
|
marginTop: 10,
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: 35,
|
|
marginleft: 10,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
>
|
|
{selectedApp.versions.map((data, index) => {
|
|
return (
|
|
<MenuItem
|
|
key={index}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data.version}
|
|
>
|
|
{data.version}
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
<div style={{ display: "flex" }}>
|
|
<div style={{ flex: 5 }}>
|
|
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Name</Typography>
|
|
<TextField
|
|
style={theme.palette.textFieldStyle}
|
|
InputProps={{
|
|
style: theme.palette.innerTextfieldStyle,
|
|
disableUnderline: true,
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
placeholder={selectedAction.label}
|
|
value={appActionName}
|
|
onChange={(event) => {
|
|
let newValue = event.target.value
|
|
newValue = newValue.replaceAll(" ", "_")
|
|
setAppActionName(newValue)
|
|
}}
|
|
onBlur={(e) => {
|
|
// Copy the name value
|
|
const name = e.target.value
|
|
const parsedBaseLabel = "$" + prevActionName.toLowerCase().replaceAll(" ", "_")
|
|
const newname = "$" + name.toLowerCase().replaceAll(" ", "_")
|
|
|
|
// Check if it's the same as the current name in use
|
|
//if (name === selectedAction.label) {
|
|
// console.log("Returning from name thing")
|
|
// return
|
|
//}
|
|
|
|
// Change in actions, triggers & conditions
|
|
// Highlight the changes somehow with a glow?
|
|
if (workflow.branches !== undefined && workflow.branches !== null) {
|
|
for (let [key, keyval] in Object.entries(workflow.branches)) {
|
|
if (workflow.branches[key].conditions !== undefined && workflow.branches[key].conditions !== null) {
|
|
for (let [subkey, subkeyval] in Object.entries(workflow.branches[key].conditions)) {
|
|
const condition = workflow.branches[key].conditions[subkey]
|
|
const sourceparam = condition.source
|
|
const destinationparam = condition.destination
|
|
|
|
// Should have a smarter way of discovering node names
|
|
// Finding index(es) and replacing at the location
|
|
if (sourceparam.value.includes("$")) {
|
|
try {
|
|
var cnt = -1
|
|
var previous = 0
|
|
while (true) {
|
|
cnt += 1
|
|
// Need to make sure e.g. changing the first here doesn't change the 2nd
|
|
// $change_me
|
|
// $change_me_2
|
|
|
|
const foundindex = sourceparam.value.toLowerCase().indexOf(parsedBaseLabel, previous)
|
|
if (foundindex === previous && foundindex !== 0) {
|
|
break
|
|
}
|
|
|
|
if (foundindex >= 0) {
|
|
previous = foundindex + newname.length
|
|
// Need to add diff of length to word
|
|
|
|
// Check location:
|
|
// If it's a-zA-Z_ then don't replace
|
|
if (sourceparam.value.length > foundindex + parsedBaseLabel.length) {
|
|
const regex = /[a-zA-Z0-9_]/g;
|
|
const match = sourceparam.value[foundindex + parsedBaseLabel.length].match(regex);
|
|
if (match !== null) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
console.log("Old found: ", workflow.branches[key].conditions[subkey].source.value)
|
|
const extralength = newname.length - parsedBaseLabel.length
|
|
sourceparam.value = sourceparam.value.substring(0, foundindex) + newname + sourceparam.value.substring(foundindex - extralength + newname.length, sourceparam.value.length)
|
|
|
|
console.log("New: ", workflow.branches[key].conditions[subkey].source.value)
|
|
} else {
|
|
break
|
|
}
|
|
|
|
// Break no matter what after 5 replaces. May need to increase
|
|
if (cnt >= 5) {
|
|
break
|
|
}
|
|
|
|
}
|
|
} catch (e) {
|
|
console.log("Failed value replacement based on index: ", e)
|
|
}
|
|
}
|
|
|
|
if (destinationparam.value.includes("$")) {
|
|
try {
|
|
var cnt = -1
|
|
var previous = 0
|
|
while (true) {
|
|
cnt += 1
|
|
// Need to make sure e.g. changing the first here doesn't change the 2nd
|
|
// $change_me
|
|
// $change_me_2
|
|
|
|
const foundindex = destinationparam.value.toLowerCase().indexOf(parsedBaseLabel, previous)
|
|
if (foundindex === previous && foundindex !== 0) {
|
|
break
|
|
}
|
|
|
|
if (foundindex >= 0) {
|
|
previous = foundindex + newname.length
|
|
// Need to add diff of length to word
|
|
|
|
// Check location:
|
|
// If it's a-zA-Z_ then don't replace
|
|
if (destinationparam.value.length > foundindex + parsedBaseLabel.length) {
|
|
const regex = /[a-zA-Z0-9_]/g;
|
|
const match = destinationparam.value[foundindex + parsedBaseLabel.length].match(regex);
|
|
if (match !== null) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
console.log("Old found: ", workflow.branches[key].conditions[subkey].destination.value)
|
|
const extralength = newname.length - parsedBaseLabel.length
|
|
destinationparam.value = destinationparam.value.substring(0, foundindex) + newname + destinationparam.value.substring(foundindex - extralength + newname.length, destinationparam.value.length)
|
|
|
|
console.log("New: ", workflow.branches[key].conditions[subkey].destination.value)
|
|
} else {
|
|
break
|
|
}
|
|
|
|
// Break no matter what after 5 replaces. May need to increase
|
|
if (cnt >= 5) {
|
|
break
|
|
}
|
|
|
|
}
|
|
} catch (e) {
|
|
console.log("Failed value replacement based on index: ", e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let [key, keyval] in Object.entries(workflow.actions)) {
|
|
if (workflow.actions[key].id === selectedAction.id) {
|
|
continue
|
|
}
|
|
|
|
const params = workflow.actions[key].parameters
|
|
if (params === null || params === undefined) {
|
|
continue
|
|
}
|
|
|
|
for (let [subkey, subkeyval] in Object.entries(params)) {
|
|
const param = workflow.actions[key].parameters[subkey];
|
|
if (!param.value.includes("$")) {
|
|
continue
|
|
}
|
|
|
|
// Should have a smarter way of discovering node names
|
|
// Do regex?
|
|
// Finding index(es) and replacing at the location
|
|
//
|
|
|
|
try {
|
|
var cnt = -1
|
|
var previous = 0
|
|
while (true) {
|
|
cnt += 1
|
|
// Need to make sure e.g. changing the first here doesn't change the 2nd
|
|
// $change_me
|
|
// $change_me_2
|
|
const foundindex = param.value.toLowerCase().indexOf(parsedBaseLabel, previous)
|
|
if (foundindex === previous && foundindex !== 0) {
|
|
break
|
|
}
|
|
|
|
if (foundindex >= 0) {
|
|
previous = foundindex + newname.length
|
|
// Need to add diff of length to word
|
|
|
|
// Check location:
|
|
// If it's a-zA-Z_ then don't replace
|
|
if (param.value.length > foundindex + parsedBaseLabel.length) {
|
|
const regex = /[a-zA-Z0-9_]/g;
|
|
const match = param.value[foundindex + parsedBaseLabel.length].match(regex);
|
|
if (match !== null) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
const extralength = newname.length - parsedBaseLabel.length
|
|
param.value = param.value.substring(0, foundindex) + newname + param.value.substring(foundindex - extralength + newname.length, param.value.length)
|
|
|
|
} else {
|
|
break
|
|
}
|
|
|
|
// Break no matter what after 5 replaces. May need to increase
|
|
if (cnt >= 5) {
|
|
break
|
|
}
|
|
|
|
}
|
|
} catch (e) {
|
|
console.log("Failed value replacement based on index: ", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
setWorkflow(workflow)
|
|
setUpdate(Math.random())
|
|
setPrevActionName(name)
|
|
}}
|
|
/>
|
|
</div>
|
|
{/*!isCloud ? null :*/}
|
|
<div style={{ flex: 1, marginLeft: 5, }}>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Delay before action executes (in seconds)"}
|
|
placement="top"
|
|
>
|
|
<span>
|
|
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Delay</Typography>
|
|
<TextField
|
|
InputProps={{
|
|
style: theme.palette.innerTextfieldStyle,
|
|
disableUnderline: true,
|
|
}}
|
|
placeholder={selectedAction.execution_delay}
|
|
value={delay}
|
|
onChange={(event) => {
|
|
setDelay(event.target.value)
|
|
}}
|
|
/>
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
{/**/}
|
|
</div>
|
|
</span>
|
|
)}
|
|
|
|
{selectedApp.name !== undefined &&
|
|
selectedAction.authentication !== null &&
|
|
selectedAction.authentication !== undefined &&
|
|
selectedAction.authentication.length === 0 &&
|
|
requiresAuthentication ? (
|
|
<div style={{ marginTop: 15 }}>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Add authentication option"}
|
|
placement="top"
|
|
>
|
|
<span>
|
|
<Button
|
|
color="primary"
|
|
style={{
|
|
textTransform: "none",
|
|
}}
|
|
fullWidth
|
|
variant="contained"
|
|
onClick={() => {
|
|
//if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) {
|
|
// return null
|
|
//}
|
|
|
|
setAuthenticationModalOpen(true);
|
|
}}
|
|
>
|
|
<AddIcon style={{ marginRight: 10 }} /> Authenticate{" "}
|
|
{selectedApp.name.replaceAll("_", " ")}
|
|
</Button>
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
) : null}
|
|
|
|
{selectedAction.authentication !== undefined &&
|
|
selectedAction.authentication !== null &&
|
|
selectedAction.authentication.length > 0 ? (
|
|
|
|
<div style={{ marginTop: 15, position: "relative", }}>
|
|
<Typography style={{ color: "rgba(255,255,255,0.7)" }}>Authentication</Typography>
|
|
<Tooltip
|
|
arrow
|
|
title={
|
|
workflow?.suborg_distribution?.length > 0 && Object.getOwnPropertyNames(selectedAction?.selectedAuthentication).length !== 0 ? (
|
|
<React.Fragment>
|
|
<div style={{padding: 10, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0)"}}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={distributeAuthToSuborgs}
|
|
onChange={(event) => {
|
|
changeDistribution(selectedAction?.selectedAuthentication, "auth")
|
|
}}
|
|
name="distributeAuth"
|
|
color="primary"
|
|
/>
|
|
}
|
|
label="Distribute auth to suborgs"
|
|
/>
|
|
</div>
|
|
</React.Fragment>
|
|
) : null
|
|
} placement="left">
|
|
<div style={{ display: "flex" }}>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
labelId="select-app-auth"
|
|
value={
|
|
selectedAction.authentication_id === "authgroups" ? "authgroups" :
|
|
Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0
|
|
? "No selection"
|
|
: selectedAction.selectedAuthentication
|
|
}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
maxWidth: 250,
|
|
},
|
|
}}
|
|
fullWidth
|
|
onChange={(e) => {
|
|
|
|
if (e.target.value === "No selection") {
|
|
selectedAction.selectedAuthentication = {};
|
|
selectedAction.authentication_id = "";
|
|
|
|
for (let [key, keyval] in Object.entries(selectedAction.parameters)) {
|
|
if (selectedAction.parameters[key].configuration === false) {
|
|
//console.log("FIELDSKIP: ", selectedAction.parameters[key].name)
|
|
continue
|
|
}
|
|
|
|
if (selectedAction.parameters[key].name === "url" && authenticationType?.type === "oauth2-app" && selectedAction.parameters[key].value.includes("http")) {
|
|
continue
|
|
}
|
|
|
|
if (selectedAction.parameters[key].example !== undefined && selectedAction.parameters[key].example !== null && selectedAction.parameters[key].example !== "") {
|
|
if (selectedAction.parameters[key].example.toLowerCase().includes("apik") || selectedAction.parameters[key].example.toLowerCase().includes("key") || selectedAction.parameters[key].example.toLowerCase().includes("pass") || selectedAction.parameters[key].example.toLowerCase().includes("****")) {
|
|
selectedAction.parameters[key].value = ""
|
|
} else {
|
|
selectedAction.parameters[key].value = selectedAction.parameters[key].example
|
|
}
|
|
|
|
} else {
|
|
selectedAction.parameters[key].value = ""
|
|
}
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
|
|
} else if (e.target.value === "authgroups") {
|
|
if (authGroups !== undefined && authGroups !== null && authGroups.length === 0) {
|
|
toast("No auth groups created. Opening window to create one")
|
|
|
|
setTimeout(() => {
|
|
window.open("/admin?tab=app_auth", "_blank")
|
|
}, 2500)
|
|
} else {
|
|
selectedAction.selectedAuthentication = {};
|
|
selectedAction.authentication_id = "authgroups"
|
|
|
|
for (let [key, keyval] in Object.entries(selectedAction.parameters)) {
|
|
//console.log(selectedAction.parameters[key])
|
|
if (selectedAction.parameters[key].configuration) {
|
|
|
|
if (selectedAction.parameters[key].name === "url" && authenticationType?.type === "oauth2-app") {
|
|
} else {
|
|
selectedAction.parameters[key].value = "authgroup controlled"
|
|
}
|
|
}
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
}
|
|
} else {
|
|
selectedAction.selectedAuthentication = e.target.value;
|
|
selectedAction.authentication_id = e.target.value.id;
|
|
|
|
setDistributeAuthToSuborgs(e.target.value?.suborg_distributed || false)
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
}
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: 35,
|
|
maxWidth: rightsidebarStyle.maxWidth - 80,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value="No selection"
|
|
>
|
|
<em>No selection</em>
|
|
</MenuItem>
|
|
{selectedAction.authentication.map((data) => {
|
|
if (data.last_modified === true) {
|
|
//console.log("LAST MODIFIED: ", data.label)
|
|
}
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data.id}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
maxWidth: 500,
|
|
overflowX: "auto",
|
|
}}
|
|
value={data}
|
|
>
|
|
|
|
{data?.validation?.valid === true ?
|
|
<Tooltip title="Authentication has been validated" placement="top">
|
|
<Chip
|
|
style={{ marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", borderColor: green, maxHeight: 25, }}
|
|
label={"Valid"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
</Tooltip>
|
|
: null}
|
|
{data?.last_modified === true ?
|
|
<Chip
|
|
style={{ marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", maxHeight: 25, }}
|
|
label={"Latest"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
: null}
|
|
{/*data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ?
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
|
|
label={data.app.app_version}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
: null*/}
|
|
{data.label}
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
|
|
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
|
|
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value="authgroups"
|
|
disabled
|
|
>
|
|
<em>Auth Groups</em>
|
|
</MenuItem>
|
|
|
|
</Select>
|
|
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Add authentication option"}
|
|
placement="top"
|
|
>
|
|
<IconButton
|
|
color="primary"
|
|
variant="outlined"
|
|
style={{}}
|
|
onClick={() => {
|
|
setAuthenticationModalOpen(true);
|
|
}}
|
|
>
|
|
<AddIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</div>
|
|
</Tooltip>
|
|
</div>
|
|
) : null}
|
|
|
|
{selectedAction.authentication_id === "authgroups" && (authGroups === undefined || authGroups === null || authGroups.length === 0) ?
|
|
<a href="/admin?tab=app_auth" target="_blank" style={{ textDecoration: "none", color: "#FF8544", }}>
|
|
<Typography variant="body2" style={{ marginTop: 5, }}>
|
|
Create your first Authentication group
|
|
</Typography>
|
|
</a>
|
|
: null}
|
|
|
|
|
|
{/*showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
|
|
<div style={{ marginTop: "20px" }}>
|
|
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Environment</Typography>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
value={
|
|
selectedActionEnvironment === undefined || selectedActionEnvironment === null || selectedActionEnvironment.Name === undefined || selectedActionEnvironment.Name === null ? isCloud ? "Cloud" : "Shuffle" : selectedActionEnvironment.Name
|
|
}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
onChange={(e) => {
|
|
const env = environments.find((a) => a.Name === e.target.value);
|
|
setSelectedActionEnvironment(env);
|
|
selectedAction.environment = env.Name;
|
|
setSelectedAction(selectedAction);
|
|
|
|
for (let actionkey in workflow.actions) {
|
|
workflow.actions[actionkey].environment = env.Name
|
|
}
|
|
setWorkflow(workflow)
|
|
toast.success("Set environment for ALL actions to " + env.Name)
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: "50px",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
{environments.map((data, index) => {
|
|
if (data.archived === true) {
|
|
return null
|
|
}
|
|
|
|
const isRunning = data.running_ip !== ""
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data.Name}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data.Name}
|
|
>
|
|
|
|
{data.Name === "cloud" || data.Name === "Cloud" ? null : !isRunning ?
|
|
<a href={`/admin?tab=environments&env=${data.Name}`} target="_blank" style={{textDecoration: "none",}}>
|
|
<Tooltip title={"Click to configure the environment"} placement="top">
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", backgroundColor: red, }}
|
|
label={"Stopped"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
window.open(`/admin?tab=environments&env=${data.Name}`, "_blank", "noopener,noreferrer")
|
|
}}
|
|
|
|
|
|
/>
|
|
</Tooltip>
|
|
</a>
|
|
: null}
|
|
|
|
{data.default === true ?
|
|
<Chip
|
|
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
|
|
label={"Default"}
|
|
variant="outlined"
|
|
color="secondary"
|
|
/>
|
|
: null}
|
|
|
|
|
|
{data.Name}
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
|
|
</div>
|
|
) : null*/}
|
|
|
|
{workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
|
|
<div style={{ marginTop: "20px" }}>
|
|
<Typography>Execution variable (optional)</Typography>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
value={
|
|
selectedAction.execution_variable !== undefined
|
|
&& selectedAction.execution_variable !== null
|
|
&& selectedAction.execution_variable.name !== undefined
|
|
&& selectedAction.execution_variable.name !== null
|
|
&& selectedAction.execution_variable.name.length > 0
|
|
? selectedAction.execution_variable.name
|
|
: "No selection"
|
|
}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
onChange={(e) => {
|
|
if (e.target.value === "No selection") {
|
|
selectedAction.execution_variable = { name: "No selection" };
|
|
} else {
|
|
const value = workflow.execution_variables.find(
|
|
(a) => a.name === e.target.value
|
|
);
|
|
selectedAction.execution_variable = value;
|
|
}
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
height: "50px",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value="No selection"
|
|
>
|
|
<em>No selection</em>
|
|
</MenuItem>
|
|
<Divider style={{ backgroundColor: theme.palette.inputColor }} />
|
|
{workflow.execution_variables.map((data) => (
|
|
<MenuItem
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data.name}
|
|
>
|
|
{data.name}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</div>
|
|
) : null}
|
|
|
|
<Divider
|
|
style={{
|
|
marginTop: "20px",
|
|
height: "1px",
|
|
width: "100%",
|
|
backgroundColor: "rgb(91, 96, 100)",
|
|
}}
|
|
/>
|
|
<div style={{ flex: "6", marginTop: "20px" }}>
|
|
{/*hideExtraTypes ? null :
|
|
<div style={{marginBottom: 5}}>
|
|
<b>Actions</b>
|
|
</div>
|
|
*/}
|
|
|
|
{setNewSelectedAction !== undefined ? (
|
|
<Autocomplete
|
|
id="action_search"
|
|
disabled={isAgent || (selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0)}
|
|
autoHighlight
|
|
value={selectedAction}
|
|
classes={{ inputRoot: classes.inputRoot }}
|
|
groupBy={(option) => {
|
|
// FIXME: Sorting
|
|
// Most popular
|
|
// Is categorized
|
|
// Uncategorized
|
|
return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
|
|
}}
|
|
renderGroup={(params) => {
|
|
|
|
return (
|
|
<li key={params.key}>
|
|
<Typography variant="body1" style={{ textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
|
|
<Typography variant="body2">{params.children}</Typography>
|
|
</li>
|
|
)
|
|
}}
|
|
options={renderedActionOptions}
|
|
ListboxProps={{
|
|
style: {
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
color: "white",
|
|
},
|
|
}}
|
|
filterOptions={(options, { inputValue }) => {
|
|
const lowercaseValue = inputValue.toLowerCase()
|
|
options = options.filter(x => x.name.replaceAll("_", " ").toLowerCase().includes(lowercaseValue) || x.description.toLowerCase().includes(lowercaseValue))
|
|
|
|
return options
|
|
}}
|
|
getOptionLabel={(option) => {
|
|
if (option === undefined || option === null || option.name === undefined || option.name === null) {
|
|
return null;
|
|
}
|
|
|
|
const newname = (
|
|
option.name.charAt(0).toUpperCase() + option.name.substring(1)
|
|
).replaceAll("_", " ");
|
|
|
|
return newname;
|
|
}}
|
|
fullWidth
|
|
sx={{
|
|
'& .MuiOutlinedInput-root': {
|
|
height: 40, // Adjust the input height
|
|
},
|
|
'& .MuiAutocomplete-input': {
|
|
padding: '8px', // Adjust the text padding
|
|
},
|
|
}}
|
|
|
|
style={{
|
|
backgroundColor: theme.palette.backgroundColor,
|
|
height: 35,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
onChange={(event, newValue) => {
|
|
// Workaround with event lol
|
|
if (newValue !== undefined && newValue !== null) {
|
|
setNewSelectedAction({
|
|
target: {
|
|
value: newValue.name
|
|
}
|
|
});
|
|
}
|
|
}}
|
|
renderOption={(props, option, state) => {
|
|
var newActionname = option.name;
|
|
if (option.label !== undefined && option.label !== null && option.label.length > 0) {
|
|
newActionname = option.label;
|
|
}
|
|
|
|
var newActiondescription = option.description;
|
|
//console.log("DESC: ", newActiondescription)
|
|
if (option.description === undefined || option.description === null) {
|
|
newActiondescription = "Description: No description defined for this action"
|
|
} else {
|
|
newActiondescription = "Description: " + newActiondescription
|
|
}
|
|
|
|
const iconInfo = GetIconInfo({ name: option.name });
|
|
const useIcon = iconInfo.originalIcon;
|
|
|
|
if (newActionname === undefined || newActionname === null) {
|
|
newActionname = "No name"
|
|
option.name = "No name"
|
|
option.label = "No name"
|
|
}
|
|
|
|
newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " ");
|
|
|
|
var method = ""
|
|
var extraDescription = ""
|
|
if (option.name.includes("get_")) {
|
|
method = "GET"
|
|
} else if (option.name.includes("post_")) {
|
|
method = "POST"
|
|
} else if (option.name.includes("put_")) {
|
|
method = "PUT"
|
|
} else if (option.name.includes("patch_")) {
|
|
method = "PATCH"
|
|
} else if (option.name.includes("delete_")) {
|
|
method = "DELETE"
|
|
} else if (option.name.includes("options_")) {
|
|
method = "OPTIONS"
|
|
} else if (option.name.includes("connect_")) {
|
|
method = "CONNECT"
|
|
}
|
|
|
|
// FIXME: Should it require a base URL?
|
|
if (method.length > 0 && option.description !== undefined && option.description !== null && option.description.includes("http")) {
|
|
var extraUrl = ""
|
|
const descSplit = option.description.split("\n")
|
|
// Last line of descSplit
|
|
if (descSplit.length > 0) {
|
|
extraUrl = descSplit[descSplit.length - 1]
|
|
}
|
|
|
|
if (extraUrl.length > 0) {
|
|
if (extraUrl.includes(" ")) {
|
|
extraUrl = extraUrl.split(" ")[0]
|
|
}
|
|
|
|
if (extraUrl.includes("#")) {
|
|
extraUrl = extraUrl.split("#")[0]
|
|
}
|
|
|
|
extraDescription = `${method} ${extraUrl}`
|
|
} else {
|
|
//console.log("No url found. Check again :)")
|
|
}
|
|
}
|
|
|
|
return (
|
|
<ActionSelectOption
|
|
{...props}
|
|
option={option}
|
|
newActiondescription={newActiondescription}
|
|
useIcon={useIcon}
|
|
newActionname={newActionname}
|
|
extraDescription={extraDescription}
|
|
/>
|
|
);
|
|
}}
|
|
renderInput={(params) => {
|
|
if (params.inputProps?.value) {
|
|
const prefixes = ["Post", "Put", "Patch"];
|
|
for (let prefix of prefixes) {
|
|
if (params.inputProps.value.startsWith(prefix)) {
|
|
let newValue = params.inputProps.value.replace(prefix + " ", "");
|
|
if (newValue.length > 1) {
|
|
newValue = newValue.charAt(0).toUpperCase() + newValue.substring(1);
|
|
}
|
|
// Set the new value without mutating inputProps
|
|
params = { ...params, inputProps: { ...params.inputProps, value: newValue } };
|
|
break;
|
|
}
|
|
}
|
|
// Check if it starts with "Get List" and method is "Get"
|
|
if (params.inputProps.value.startsWith("Get List")) {
|
|
console.log("Get List");
|
|
}
|
|
}
|
|
|
|
|
|
const actionDescription = null
|
|
|
|
return (
|
|
<Tooltip title={actionDescription}
|
|
placement="right"
|
|
open={!hiddenDescription}
|
|
PopperProps={{
|
|
sx: {
|
|
'& .MuiTooltip-tooltip': {
|
|
backgroundColor: 'transparent',
|
|
boxShadow: 'none',
|
|
},
|
|
'& .MuiTooltip-arrow': {
|
|
color: 'transparent',
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<TextField
|
|
{...params}
|
|
|
|
data-lpignore="true"
|
|
autocomplete="off"
|
|
dataLPIgnore="true"
|
|
autoComplete="off"
|
|
|
|
color="primary"
|
|
id="checkbox-search"
|
|
variant="body1"
|
|
style={theme.palette.textFieldStyle}
|
|
label={isIntegration ? "Choose a category" : "Find Actions"}
|
|
variant="outlined"
|
|
name={`disable_autocomplete_${Math.random()}`}
|
|
disabled={isAgent || (selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0)}
|
|
/>
|
|
</Tooltip>
|
|
);
|
|
}}
|
|
/>
|
|
) : null}
|
|
|
|
<div
|
|
style={{
|
|
marginTop: "10px",
|
|
borderColor: "white",
|
|
borderWidth: "2px",
|
|
marginBottom: hideExtraTypes ? 50 : 200,
|
|
}}
|
|
> {
|
|
selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
|
|
<div style={{ marginTop: hideExtraTypes ? 10 : 30 }}>
|
|
{isIntegration || isAgent ?
|
|
apps !== undefined && apps !== null && apps.length > 0 ?
|
|
<div style={{ display: "flex", maxWidth: 335, overflowX: "auto", overflowY: "hidden", }}>
|
|
<div onClick={() => {
|
|
|
|
selectedAction.example = "noapp"
|
|
selectedAction.large_image = newimage
|
|
if (cy !== undefined && cy !== null) {
|
|
const foundnode = cy.getElementById(selectedAction.id)
|
|
if (foundnode !== undefined && foundnode !== null) {
|
|
foundnode.data("large_image", newimage)
|
|
}
|
|
}
|
|
|
|
const iconInfo = GetIconInfo(selectedAction)
|
|
if (iconInfo !== undefined && iconInfo !== null) {
|
|
selectedAction.fillGradient = iconInfo.fillGradient
|
|
|
|
selectedAction.iconBackground = iconInfo.iconBackgroundColor
|
|
selectedAction.fillstyle = "linear-gradient"
|
|
}
|
|
|
|
if (paramIndex === -1) {
|
|
console.log("Couldn't find app_name parameter")
|
|
selectedAction.parameters.push({
|
|
name: "app_name",
|
|
value: wrapperapp.name,
|
|
autocompleted: false,
|
|
})
|
|
} else {
|
|
selectedAction.parameters[paramIndex].value = wrapperapp.name
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
|
|
}}>
|
|
<Tooltip title={"Unselect which App to use"} placement="top">
|
|
<div style={{ textAlign: "center", }}>
|
|
<img
|
|
src={wrapperapp.large_image}
|
|
style={{
|
|
minWidth: 30,
|
|
maxWidth: 30,
|
|
minHeight: 30,
|
|
maxHeight: 30,
|
|
|
|
marginRight: 5,
|
|
borderRadius: 5,
|
|
cursor: "pointer",
|
|
border: noAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)",
|
|
|
|
paddingLeft: 5,
|
|
paddingTop: 5,
|
|
}} />
|
|
</div>
|
|
</Tooltip>
|
|
</div>
|
|
|
|
{apps.map((app, appIndex) => {
|
|
if (app.categories === undefined || app.categories === null || app.categories.length === 0) {
|
|
return null
|
|
}
|
|
|
|
var newactionname = actionname.toLowerCase()
|
|
if (isAgent === true) {
|
|
newactionname = "ai"
|
|
}
|
|
|
|
var found = false
|
|
for (var key in app.categories) {
|
|
if (app.categories[key].toLowerCase() !== newactionname) {
|
|
continue
|
|
}
|
|
|
|
found = true
|
|
break
|
|
}
|
|
|
|
if (!found) {
|
|
return null
|
|
}
|
|
|
|
var isAppSelected = false
|
|
const paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name")
|
|
if (paramIndex > -1) {
|
|
// Check the actual value and if it's the same
|
|
if (selectedAction.parameters[paramIndex].value === app.name) {
|
|
isAppSelected = true
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div onClick={() => {
|
|
selectedAction.example = ""
|
|
selectedAction.large_image = app.large_image
|
|
if (cy !== undefined && cy !== null) {
|
|
const foundnode = cy.getElementById(selectedAction.id)
|
|
if (foundnode !== undefined && foundnode !== null) {
|
|
foundnode.data("large_image", app.large_image)
|
|
}
|
|
}
|
|
|
|
if (paramIndex === -1) {
|
|
console.log("Couldn't find app_name parameter")
|
|
selectedAction.parameters.push({
|
|
name: "app_name",
|
|
value: app.name,
|
|
autocompleted: false,
|
|
})
|
|
} else {
|
|
selectedAction.parameters[paramIndex].value = app.name
|
|
}
|
|
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
|
|
}}>
|
|
<Tooltip title={`Select ${app.name.replaceAll("_", " ")}`} placement="top">
|
|
<img
|
|
src={app.large_image}
|
|
style={{
|
|
width: 35,
|
|
height: 35,
|
|
marginRight: 5,
|
|
borderRadius: 5,
|
|
cursor: "pointer",
|
|
border: isAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)",
|
|
}} />
|
|
</Tooltip>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
: null
|
|
:
|
|
<Tooltip
|
|
color="secondary"
|
|
title={"Click to learn more about this action"}
|
|
placement="top"
|
|
>
|
|
<div style={{ marginTop: 50, }} />
|
|
{/*
|
|
<Button
|
|
variant="text"
|
|
color="secondary"
|
|
style={{
|
|
justifyContent: "flex-start",
|
|
textAlign: "left",
|
|
textTransform: "none",
|
|
width: "100%",
|
|
marginTop: 15,
|
|
}}
|
|
fullWidth
|
|
disabled={selectedAction.description === undefined || selectedAction.description === null || selectedAction.description.length === 0}
|
|
onClick={() => {
|
|
setHiddenParameters(!hiddenParameters)
|
|
}}
|
|
>
|
|
<b>Parameters</b>
|
|
</Button>
|
|
*/}
|
|
</Tooltip>
|
|
}
|
|
|
|
{selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ?
|
|
<div>
|
|
<Typography variant="body1">
|
|
Select an app you want to use
|
|
</Typography>
|
|
<Autocomplete
|
|
id="template_action_search"
|
|
autoHighlight
|
|
value={selectedAction}
|
|
classes={{ inputRoot: classes.inputRoot }}
|
|
ListboxProps={{
|
|
style: {
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
},
|
|
}}
|
|
getOptionLabel={(option) => {
|
|
console.log("LABEL: ", option)
|
|
if (
|
|
option === undefined ||
|
|
option === null ||
|
|
option.app_name === undefined ||
|
|
option.app_name === null
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const newname = (
|
|
option.app_name.charAt(0).toUpperCase() + option.app_name.substring(1)
|
|
).replaceAll("_", " ");
|
|
return newname;
|
|
}}
|
|
options={selectedAction.matching_actions}
|
|
fullWidth
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
height: 35,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
onChange={(event, newValue) => {
|
|
console.log("SELECT: ", event, newValue)
|
|
// Workaround with event lol
|
|
//if (newValue !== undefined && newValue !== null) {
|
|
// setNewSelectedAction({ target: { value: newValue.name } });
|
|
//}
|
|
}}
|
|
renderOption={(props, data, state) => {
|
|
var newActionname = data.app_name;
|
|
if (
|
|
data.label !== undefined &&
|
|
data.label !== null &&
|
|
data.label.length > 0
|
|
) {
|
|
newActionname = data.label;
|
|
}
|
|
|
|
const iconInfo = GetIconInfo({ name: data.app_name });
|
|
const useIcon = iconInfo.originalIcon;
|
|
|
|
console.log("Actionname 1: ", newActionname)
|
|
|
|
newActionname = (
|
|
newActionname.charAt(0).toUpperCase() +
|
|
newActionname.substring(1)
|
|
).replaceAll("_", " ");
|
|
|
|
return (
|
|
<div style={{ display: "flex" }}>
|
|
<span
|
|
style={{
|
|
marginRight: 10,
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
}}
|
|
>
|
|
{useIcon}
|
|
</span>
|
|
<span style={{}}>{newActionname}</span>
|
|
</div>
|
|
);
|
|
}}
|
|
renderInput={(params) => {
|
|
if (params.inputProps !== undefined && params.inputProps !== null && params.inputProps.value !== undefined && params.inputProps.value !== null) {
|
|
const prefixes = ["Post", "Put", "Patch"]
|
|
for (let [key, keyval] in Object.entries(prefixes)) {
|
|
if (params.inputProps.value.startsWith(prefixes[key])) {
|
|
params.inputProps.value = params.inputProps.value.replace(prefixes[key] + " ", "", -1)
|
|
if (params.inputProps.value.length > 1) {
|
|
params.inputProps.value = params.inputProps.value.charAt(0).toUpperCase() + params.inputProps.value.substring(1)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
{...params}
|
|
label="Find App to Translate"
|
|
variant="outlined"
|
|
/>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
: null}
|
|
{selectedAction.description !== undefined && selectedAction.description !== null && selectedAction.description.length > 0 && hiddenParameters === false ? (
|
|
<div
|
|
style={{
|
|
border: "1px solid rgba(255,255,255,0.6)",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
marginTop: 15,
|
|
marginBottom: 10,
|
|
maxHeight: 70,
|
|
overflow: "auto",
|
|
padding: 15,
|
|
}}
|
|
>
|
|
<Typography style={{}}>
|
|
<b>Description</b>
|
|
</Typography>
|
|
<Typography style={{}}>
|
|
{selectedAction.description}
|
|
</Typography>
|
|
</div>
|
|
) : null}
|
|
|
|
{suggestionInfo()}
|
|
{selectedActionParameters?.map((data, count) => {
|
|
if (data.variant === "") {
|
|
data.variant = "STATIC_VALUE";
|
|
}
|
|
|
|
if ((isIntegration || isAgent) && data.name === "app_name") {
|
|
return null
|
|
}
|
|
|
|
/*
|
|
// Somehow autogenerate from the app itself
|
|
if (isAgent && data.name == "model") {
|
|
//console.log("Options: ", data.options)
|
|
}
|
|
*/
|
|
|
|
if (data.value === "authgroup controlled") {
|
|
if (data?.name === "url" && authenticationType?.type === "oauth2-app") {
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
|
|
if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) {
|
|
|
|
//selectedAction.parameters = selectedActionParameters
|
|
//console.log("PARAM BUG - length change(?): ", selectedAction)
|
|
}
|
|
|
|
//!selectedAction.auth_not_required &&
|
|
if (selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields[data.name] !== undefined) {
|
|
|
|
// This sets the placeholder in the frontend. (Replaced in backend)
|
|
if (selectedActionParameters[count] !== undefined) {
|
|
selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name]
|
|
}
|
|
|
|
if (selectedAction.parameters[count] !== undefined) {
|
|
selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name]
|
|
}
|
|
|
|
setSelectedAction(selectedAction);
|
|
//setUpdate(Math.random())
|
|
|
|
if (authWritten) {
|
|
return null;
|
|
}
|
|
|
|
authWritten = true;
|
|
return null
|
|
|
|
/*
|
|
// FIXME: Is this part necessary to show?
|
|
return (
|
|
<Typography
|
|
key={count}
|
|
id="skip_auth"
|
|
variant="body2"
|
|
color="textSecondary"
|
|
style={{ marginTop: 5 }}
|
|
>
|
|
Authentication fields are hidden
|
|
</Typography>
|
|
);
|
|
*/
|
|
}
|
|
|
|
|
|
// Added autofill to make this ALOT simpler
|
|
if (isCloud && (selectedAction.app_name === "Shuffle Tools" || selectedAction.app_name === "email") && (selectedAction.name === "send_email_shuffle" || selectedAction.name === "send_sms_shuffle") && data.name === "apikey") {
|
|
if (selectedActionParameters[count].length === 0) {
|
|
selectedAction.parameters[count].value = "TMP: Will be replaced during execution if cloud"
|
|
setSelectedAction(selectedAction)
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
var staticcolor = "inherit";
|
|
var actioncolor = "inherit";
|
|
var varcolor = "inherit";
|
|
var multiline
|
|
if (
|
|
data.multiline !== undefined &&
|
|
data.multiline !== null &&
|
|
data.multiline === true
|
|
) {
|
|
multiline = true;
|
|
}
|
|
|
|
// make data.value from array to comma separated string if it is an array
|
|
if (data.value !== undefined && data.value !== null && Array.isArray(data.value)) {
|
|
data.value = data.value.join(",")
|
|
}
|
|
|
|
if (data.value !== undefined && data.value !== null &&
|
|
data.value.startsWith("{") && data.value.endsWith("}")) {
|
|
multiline = true
|
|
}
|
|
|
|
var placeholder = "Value";
|
|
if (data.example !== undefined && data.example !== null && data.example.length > 0) {
|
|
|
|
placeholder = data.example;
|
|
|
|
// if (data.name === "url") {
|
|
// data.value = data.example;
|
|
// }
|
|
// In case of data.example
|
|
if (data.value === undefined || data.value === null) {
|
|
data.value = ""
|
|
}
|
|
|
|
if (data.value.length === 0) {
|
|
if (data.name.toLowerCase() === "headers") {
|
|
//console.log("Should show headers field instead with + and -!")
|
|
|
|
// Check if file ID exists
|
|
//
|
|
const fileFound = selectedActionParameters.find(param => param.name === "file_id")
|
|
if (fileFound === undefined || fileFound === null) {
|
|
data.value = data.example
|
|
} else {
|
|
// Purposely unset it if set by default when using files
|
|
data.value = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (data.name !== "queries" && data.name !== "key" && data.name !== "value" ) {
|
|
data.value = data.example
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
if (selectedAction.name === "custom_action" && data.name === "body") {
|
|
for (var key in selectedActionParameters) {
|
|
const param = selectedActionParameters[key]
|
|
if (param.name === "method") {
|
|
if (param.value === "GET") {
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
const paramcheck = selectedAction.parameters.find((param) => param.name === "body");
|
|
|
|
|
|
if (paramcheck !== undefined && paramcheck !== null) {
|
|
if (
|
|
paramcheck["value_replace"] !== undefined &&
|
|
paramcheck["value_replace"] !== null
|
|
) {
|
|
//console.log("IN THE VALUE REPLACE: ", paramcheck["value_replace"])
|
|
const subparamindex = paramcheck["value_replace"].findIndex(
|
|
(param) => param.key === data.name
|
|
);
|
|
if (subparamindex !== -1) {
|
|
data.value =
|
|
paramcheck["value_replace"][subparamindex]["value"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var showCacheConfig = false
|
|
if (data.name === "key" && selectedAction.name.includes("cache") && selectedAction.app_name === "Shuffle Tools") {
|
|
// Show a key popout button
|
|
showCacheConfig = true
|
|
}
|
|
|
|
var disabled = false;
|
|
var rows = "3";
|
|
var openApiHelperText = "This is an OpenAPI specific field";
|
|
|
|
|
|
if (selectedApp.generated && data.name === "headers") {
|
|
//console.log("HEADER: ", data)
|
|
//if (data.value.length === 0) {
|
|
//}
|
|
//setSelectedActionParameters(selectedActionParameters)
|
|
}
|
|
|
|
var hideBodyButtonValue = (
|
|
<div
|
|
key={data.name}
|
|
id="hide_body_button"
|
|
style={{
|
|
marginTop: 30,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
alignItems: "center",
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
<Tabs
|
|
value={hideBody === true ? 1 : 0}
|
|
indicatorColor="primary"
|
|
style={{
|
|
width: "100%",
|
|
display: "flex",
|
|
textAlign: "center",
|
|
textTransform: "none",
|
|
}}
|
|
>
|
|
<Tab
|
|
label={
|
|
"Simple"
|
|
}
|
|
style={{
|
|
flex: 1,
|
|
textTransform: "none",
|
|
borderBottom: "1px solid rgba(255,255,255,0.3)",
|
|
}}
|
|
onClick={() => {
|
|
// Set localstorage
|
|
localStorage.setItem("hideBody", "true")
|
|
|
|
setHideBody(false)
|
|
const updatedParameters = selectedActionParameters.map((param) => {
|
|
if (param.name === "body") {
|
|
return {
|
|
...param,
|
|
id: "UNTOGGLED",
|
|
}
|
|
}
|
|
|
|
if (param.description === openApiFieldDesc) {
|
|
// Check required fields here
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
|
// Look for the field name in the required_body_fields
|
|
if (selectedAction.required_body_fields.includes(param.name)) {
|
|
param.required = true
|
|
} else {
|
|
param.required = false
|
|
}
|
|
}
|
|
|
|
return { ...param, field_active: true }
|
|
}
|
|
|
|
return param
|
|
})
|
|
|
|
setSelectedActionParameters(updatedParameters)
|
|
}}
|
|
/>
|
|
<Tab
|
|
label={
|
|
"Advanced"
|
|
}
|
|
style={{
|
|
flex: 1,
|
|
textTransform: "none",
|
|
borderBottom: "1px solid rgba(255,255,255,0.3)",
|
|
}}
|
|
onClick={() => {
|
|
localStorage.setItem("hideBody", "false")
|
|
setHideBody(true)
|
|
// Make sure the body field is shown
|
|
var foundvalue = false
|
|
const updatedParameters = selectedActionParameters.map((param) => {
|
|
if (param.name === "body") {
|
|
return {
|
|
...param,
|
|
id: "TOGGLED",
|
|
}
|
|
}
|
|
|
|
if (param.description === openApiFieldDesc) {
|
|
if (param.value.length > 0) {
|
|
foundvalue = true
|
|
}
|
|
|
|
return { ...param, field_active: false }
|
|
}
|
|
|
|
return param
|
|
})
|
|
|
|
if (foundvalue === true) {
|
|
toast.info("Please fill either Simple fields OR Advanced body, not both")
|
|
}
|
|
|
|
setSelectedActionParameters(updatedParameters)
|
|
}}
|
|
/>
|
|
</Tabs>
|
|
</div>
|
|
)
|
|
|
|
var showButtonField = false
|
|
if (selectedApp.generated === true && data.name === "body") {
|
|
const regex = /\${(\w+)}/g;
|
|
const found = placeholder.match(regex);
|
|
|
|
var newhidebody = hideBody
|
|
showButtonField = true
|
|
if (found === undefined || found === null || found.length === 0) {
|
|
newhidebody = false
|
|
hideBodyButtonValue = null
|
|
|
|
if (hideBody === false) {
|
|
setHideBody(true)
|
|
}
|
|
}
|
|
|
|
if (newhidebody === true) {
|
|
//toast("BODYBUTTON TRUE")
|
|
} else {
|
|
|
|
rows = "1";
|
|
disabled = true;
|
|
openApiHelperText = "OpenAPI spec: fill the following fields.";
|
|
|
|
var changed = false;
|
|
var tempArray = []
|
|
for (let specKey in found) {
|
|
const tmpitem = found[specKey];
|
|
var skip = false;
|
|
|
|
for (let innerkey in selectedActionParameters) {
|
|
if (selectedActionParameters[innerkey].name === tmpitem) {
|
|
skip = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (skip) {
|
|
//console.log("SKIPPING ", tmpitem)
|
|
continue;
|
|
}
|
|
|
|
changed = true;
|
|
var isRequired = false
|
|
// Check if original field name is in the selectedAction.required_body_fields
|
|
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null) {
|
|
for (let innerkey in selectedAction.required_body_fields) {
|
|
if (selectedAction.required_body_fields[innerkey] === tmpitem) {
|
|
isRequired = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
tempArray.push({
|
|
action_field: "",
|
|
configuration: false,
|
|
description: openApiFieldDesc,
|
|
example: "",
|
|
id: "",
|
|
multiline: false,
|
|
name: tmpitem,
|
|
options: null,
|
|
required: isRequired,
|
|
schema: { type: "string" },
|
|
skip_multicheck: false,
|
|
tags: null,
|
|
value: "",
|
|
variant: "STATIC_VALUE",
|
|
field_active: true,
|
|
|
|
autocompleted: false,
|
|
});
|
|
}
|
|
|
|
var required = selectedActionParameters.filter(item => item.required === true)
|
|
var notRequired = selectedActionParameters.filter(item => item.required === false)
|
|
|
|
if (tempArray.length > 0) {
|
|
// Sort tempArray based on tempArray.required
|
|
tempArray.sort((a, b) => (a.required < b.required) ? 1 : -1)
|
|
// Add all items to the selectedActionParameters array
|
|
for (let innerkey in tempArray) {
|
|
tempArray[innerkey].id = "ADDED"
|
|
|
|
if (tempArray[innerkey].required === true) {
|
|
required.push(tempArray[innerkey])
|
|
} else {
|
|
notRequired.push(tempArray[innerkey])
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
// Sort selectedActionParameters based on selectedActionParameters.required
|
|
// Find the "headers" and "queries" field names and put them on the first indexes anyway
|
|
var newArray = required.concat(notRequired)
|
|
|
|
|
|
setSelectedActionParameters(newArray)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
const clickedFieldId = "rightside_field_" + count;
|
|
|
|
var baseHelperText = ""
|
|
if (data !== undefined && data !== null && data.value !== undefined && data.value !== null && data.value.length > 0) {
|
|
baseHelperText = calculateHelpertext(data.value)
|
|
}
|
|
|
|
var tmpitem = data.name.valueOf();
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
tmpitem = tmpitem.slice(2, data.name.length - 1);
|
|
}
|
|
|
|
if (tmpitem === "from_shuffle") {
|
|
tmpitem = "from"
|
|
}
|
|
|
|
tmpitem = (
|
|
tmpitem.charAt(0).toUpperCase() + tmpitem.substring(1)
|
|
).replaceAll("_", " ");
|
|
|
|
if (tmpitem === "Username basic") {
|
|
tmpitem = "Username"
|
|
} else if (tmpitem === "Password basic") {
|
|
tmpitem = "Password"
|
|
}
|
|
|
|
// No longer multiline for new fields
|
|
//multiline = data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline
|
|
|
|
if (data.name === "body") {
|
|
//console.log("BODY: ", data)
|
|
if (hideBody === false) {
|
|
return hideBodyButtonValue
|
|
}
|
|
|
|
rows = "4"
|
|
multiline = true
|
|
disabled = false
|
|
}
|
|
|
|
const description = data.description === undefined ? "" : data?.description;
|
|
|
|
const tooltipDescription = (
|
|
<Box
|
|
p={1.5}
|
|
borderRadius={3}
|
|
boxShadow={2}
|
|
backgroundColor={theme.palette.textFieldStyle}
|
|
display="flex"
|
|
flexDirection="column"
|
|
>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
<Typography variant="body1" style={{ flexGrow: 1 }}>
|
|
{tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)}
|
|
</Typography>
|
|
<IconButton size="small"
|
|
onClick={() => {
|
|
setUiBox("closed")
|
|
}}
|
|
>
|
|
<CloseIcon fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
<Divider sx={{ backgroundColor: theme.palette.surfaceColor, marginTop: "5px", marginBottom: "10px", height: "3px" }} />
|
|
<Box display="flex" flexDirection="column">
|
|
<Typography variant="body2" mb={0.5}>
|
|
<strong>Required:</strong> {data.required === true || data.configuration === true ? "True" : "False"}
|
|
</Typography>
|
|
<Typography variant="body2" mb={0.5}>
|
|
<strong>Description:</strong> {description}
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
<strong>Ex. :</strong> {data?.example?.length > 0 ? data.example : "No example available"}
|
|
</Typography>
|
|
{
|
|
data?.configuration === true ?
|
|
(
|
|
<Typography variant="body2" mt={0.5}>
|
|
<strong>Auth: </strong>Use "\$" instead of "$"
|
|
</Typography>
|
|
) : null
|
|
}
|
|
|
|
<div onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
|
|
localStorage.setItem("disabled_ui_box", "true")
|
|
setUiBox("closed")
|
|
}}>
|
|
<Typography style={{ marginTop: 10, color: "#FF8544", cursor: "pointer", }} variant="body2">
|
|
Don't show again
|
|
</Typography>
|
|
</div>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
|
|
|
|
var datafield = (
|
|
<Tooltip
|
|
title={tooltipDescription}
|
|
placement="right"
|
|
open={clickedFieldId === uiBox}
|
|
style={{
|
|
zIndex: 0,
|
|
}}
|
|
PopperProps={{
|
|
sx: {
|
|
'& .MuiTooltip-tooltip': {
|
|
backgroundColor: 'transparent',
|
|
boxShadow: 'none',
|
|
},
|
|
'& .MuiTooltip-arrow': {
|
|
color: 'transparent',
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<TextField
|
|
autofill="off"
|
|
autoComplete="off"
|
|
id={clickedFieldId}
|
|
disabled={disabled}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
width: "100%",
|
|
maxHeight: multiline === true ? undefined : 40,
|
|
minHeight: 40,
|
|
|
|
border:
|
|
selectedActionParameters[count].required ||
|
|
selectedActionParameters[count].configuration
|
|
? "1px solid #FF8544"
|
|
: "",
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
maxHeight: multiline === true ? undefined : 40,
|
|
},
|
|
disableUnderline: true,
|
|
endAdornment: hideExtraTypes ? null : (
|
|
<InputAdornment position="end">
|
|
<ButtonGroup color="secondary" orientation={multiline ? "vertical" : "horizontal"}>
|
|
<Tooltip title="Autocomplete text" placement="bottom">
|
|
<AddCircleOutlineIcon
|
|
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, }}
|
|
onClick={(event) => {
|
|
event.preventDefault()
|
|
|
|
// Get cursor position
|
|
// This makes it so we can put it in the right location?
|
|
setMenuPosition({
|
|
top: event.pageY + 10,
|
|
left: event.pageX + 10,
|
|
});
|
|
setShowDropdownNumber(count);
|
|
setShowDropdown(true);
|
|
setShowAutocomplete(true);
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</ButtonGroup>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
multiline={multiline}
|
|
onClick={() => {
|
|
/*
|
|
setExpansionModalOpen(false);
|
|
*/
|
|
|
|
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
|
|
|
|
scrollConfig.selected = clickedFieldId
|
|
setScrollConfig(scrollConfig)
|
|
}
|
|
}}
|
|
rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows}
|
|
color="primary"
|
|
// defaultValue={data.value}
|
|
value={
|
|
data?.value
|
|
}
|
|
error={
|
|
data?.error?.length > 0 ? true : false
|
|
}
|
|
helperText={data?.error?.length > 0 ? errorHelperText(data?.name, data?.value, data?.error) : returnHelperText(data.name, data.value)}
|
|
//options={{
|
|
// theme: 'gruvbox-dark',
|
|
// keyMap: 'sublime',
|
|
// mode: 'python',
|
|
//}}
|
|
//height={multiline ? 50 : 150}
|
|
type={
|
|
placeholder.includes("***") ||
|
|
(data.configuration &&
|
|
(data.name.toLowerCase().includes("api") ||
|
|
data.name.toLowerCase().includes("key") ||
|
|
data.name.toLowerCase().includes("pass")))
|
|
? "password"
|
|
: "text"
|
|
}
|
|
placeholder={placeholder}
|
|
onChange={(event) => {
|
|
handleParamChange(event, count, data)
|
|
}}
|
|
onFocus={(event) => {
|
|
// Get local storage key "disabled_ui_box" and check if it's true
|
|
const disabledUiBox = localStorage.getItem("disabled_ui_box")
|
|
if (disabledUiBox === "true") {
|
|
} else {
|
|
//setUiBox(event.target.id)
|
|
}
|
|
}}
|
|
onBlur={(event) => {
|
|
handleParamChange(event, count, data)
|
|
|
|
baseHelperText = calculateHelpertext(event.target.value)
|
|
if (setLastSaved !== undefined) {
|
|
setLastSaved(false)
|
|
}
|
|
|
|
// Check if we clicked the tooltip or not
|
|
const tooltipid = "rightside_field_tooltip" + count
|
|
const foundElement = document.getElementById(tooltipid)
|
|
if (foundElement !== null && foundElement !== undefined) {
|
|
console.log("FOUND: ", foundElement)
|
|
} else {
|
|
//console.log("TOOLTIP -> NOT FOUND")
|
|
//setUiBox("closed")
|
|
}
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
)
|
|
|
|
// Finds headers from a string to be used for autocompletion
|
|
const findHeaders = (inputdata) => {
|
|
var splitdata = inputdata.split("\n")
|
|
|
|
var foundnewline = false
|
|
var allValues = []
|
|
for (let [key, keyval] in Object.entries(splitdata)) {
|
|
const line = splitdata[key]
|
|
if (line === "") {
|
|
foundnewline = true
|
|
continue
|
|
}
|
|
|
|
var splitvalue = ""
|
|
if (line.includes(":")) {
|
|
splitvalue = ":"
|
|
}
|
|
|
|
if (line.includes("=")) {
|
|
splitvalue = "="
|
|
}
|
|
|
|
if (splitvalue.length === 0) {
|
|
allValues.push({
|
|
key: line,
|
|
value: "",
|
|
})
|
|
continue
|
|
}
|
|
|
|
var splitKeys = line.split(splitvalue)
|
|
if (splitKeys.length > 1) {
|
|
allValues.push({
|
|
key: splitKeys[0].trim(),
|
|
value: splitKeys[1].trim(),
|
|
})
|
|
} else {
|
|
console.log("No keys for ", line)
|
|
}
|
|
}
|
|
|
|
// Just add one
|
|
if (foundnewline) {
|
|
allValues.push({
|
|
key: "",
|
|
value: "",
|
|
})
|
|
}
|
|
|
|
return allValues
|
|
}
|
|
|
|
if (data.name.toLowerCase() === "headers") {
|
|
//var tmpheaders = findHeaders(data.value)
|
|
var tmpheaders = findHeaders(selectedActionParameters[count].value)
|
|
const tmpdatafield =
|
|
<div>
|
|
{tmpheaders.map((inputdata, index) => {
|
|
const oldkey = inputdata.key
|
|
const oldval = inputdata.value
|
|
|
|
return (
|
|
<span key={index}>
|
|
<div style={{ display: "flex" }}>
|
|
<TextField
|
|
placeholder="Key"
|
|
style={{ flex: 5 }}
|
|
defaultValue={inputdata.key}
|
|
onBlur={(e) => {
|
|
console.log("Change from oldkey to new: ", oldkey, e.target.value)
|
|
|
|
// Find the right line to replace!
|
|
//const newval = selectedActionParameters[count].value.replace(oldval, e.target.value, 1)
|
|
const tmpsplit = selectedActionParameters[count].value.split("\n")
|
|
var valsplit = []
|
|
var add_empty = false
|
|
for (let [key, keyval] in Object.entries(tmpsplit)) {
|
|
if (tmpsplit[key] === "") {
|
|
add_empty = true
|
|
continue
|
|
}
|
|
|
|
valsplit.push(tmpsplit[key])
|
|
}
|
|
|
|
if (add_empty) {
|
|
valsplit.push("")
|
|
}
|
|
console.log("Split: ", valsplit)
|
|
|
|
var newarr = []
|
|
for (let [key, keyval] in Object.entries(valsplit)) {
|
|
var line = valsplit[key]
|
|
|
|
if (key == index) {
|
|
if (oldkey === "") {
|
|
if (line.includes("=") || line.includes(":")) {
|
|
newarr.push(e.target.value + line)
|
|
} else {
|
|
newarr.push(e.target.value + ": " + line)
|
|
}
|
|
} else {
|
|
newarr.push(line.replace(oldkey, e.target.value, 1))
|
|
}
|
|
|
|
} else {
|
|
newarr.push(line)
|
|
}
|
|
}
|
|
|
|
var newval = newarr.join("\n")
|
|
console.log("Fixed: ", newval)
|
|
|
|
selectedActionParameters[count].value = newval
|
|
selectedAction.parameters[count].value = newval
|
|
setSelectedAction(selectedAction)
|
|
setSelectedActionParameters(selectedActionParameters)
|
|
}}
|
|
/>
|
|
<TextField
|
|
placeholder="Value"
|
|
style={{ flex: 6 }}
|
|
defaultValue={inputdata.value}
|
|
onBlur={(e) => {
|
|
console.log("Change from oldval to new: ", oldval, e.target.value)
|
|
|
|
// Find the right line to replace!
|
|
//const newval = selectedActionParameters[count].value.replace(oldval, e.target.value, 1)
|
|
var tmpsplit = selectedActionParameters[count].value.split("\n")
|
|
var valsplit = []
|
|
var add_empty = false
|
|
for (let [key, keyval] in Object.entries(tmpsplit)) {
|
|
if (tmpsplit[key] === "") {
|
|
add_empty = true
|
|
continue
|
|
}
|
|
|
|
valsplit.push(tmpsplit[key])
|
|
}
|
|
|
|
if (add_empty) {
|
|
valsplit.push("")
|
|
}
|
|
console.log("Split: ", valsplit)
|
|
|
|
var newarr = []
|
|
for (let [key, keyval] in Object.entries(valsplit)) {
|
|
var line = valsplit[key]
|
|
|
|
if (key == index) {
|
|
if (oldval === "") {
|
|
if (line.includes("=") || line.includes(":")) {
|
|
newarr.push(line + e.target.value)
|
|
} else {
|
|
newarr.push(line + ": " + e.target.value)
|
|
}
|
|
} else {
|
|
newarr.push(line.replace(oldval, e.target.value, 1))
|
|
}
|
|
|
|
} else {
|
|
newarr.push(line)
|
|
}
|
|
}
|
|
|
|
var newval = newarr.join("\n")
|
|
console.log("Fixed: ", newval)
|
|
|
|
selectedActionParameters[count].value = newval
|
|
selectedAction.parameters[count].value = newval
|
|
setSelectedAction(selectedAction)
|
|
setSelectedActionParameters(selectedActionParameters)
|
|
}}
|
|
/>
|
|
</div>
|
|
</span>
|
|
)
|
|
})}
|
|
<Button variant="outlined" fullWidth onClick={() => {
|
|
console.log("Add header")
|
|
|
|
selectedActionParameters[count].value += "\n"
|
|
selectedAction.parameters[count].value += "\n"
|
|
setSelectedActionParameters(selectedActionParameters)
|
|
setSelectedAction(selectedAction)
|
|
setUpdate(Math.random())
|
|
}}>
|
|
<AddIcon style={{ marginRight: 10 }} /> Add header
|
|
</Button>
|
|
</div>
|
|
}
|
|
|
|
//const regexp = new RegExp("\W+\.", "g")
|
|
//let match
|
|
//while ((match = regexp.exec(data.value)) !== null) {
|
|
// console.log(`Found ${match[0]} start=${match.index} end=${regexp.lastIndex}.`);
|
|
//}
|
|
|
|
//const str = = data.value.search(submatch)
|
|
//console.log("FOUND? ", n)
|
|
//for (var key in keywords) {
|
|
// const keyword = keywords[key]
|
|
// if (data.value.includes(keyword)) {
|
|
// console.log("INCLUDED: ", keyword)
|
|
// }
|
|
//}
|
|
|
|
if (files !== undefined && files !== null && data.name.toLowerCase() === "file_category") {
|
|
//selectedActionParameters[count].options.length > 0
|
|
console.log("FileS: ", files)
|
|
if (files.namespaces !== undefined && files.namespaces !== null && files.namespaces.length > 0) {
|
|
data.options = files.namespaces
|
|
}
|
|
}
|
|
|
|
//const keywords = ["len", "lower", "upper", "trim", "split", "length", "number", "parse", "join"]
|
|
if (
|
|
selectedActionParameters[count].schema !== undefined &&
|
|
selectedActionParameters[count].schema !== null &&
|
|
selectedActionParameters[count].schema.type === "file"
|
|
) {
|
|
datafield = (
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
InputProps={{
|
|
endAdornment: hideExtraTypes ? null : (
|
|
<InputAdornment position="end">
|
|
<Tooltip title="Autocomplete text" placement="top">
|
|
<AddCircleOutlineIcon
|
|
style={{ cursor: "pointer" }}
|
|
onClick={(event) => {
|
|
setMenuPosition({
|
|
top: event.pageY + 10,
|
|
left: event.pageX + 10,
|
|
});
|
|
setShowDropdownNumber(count);
|
|
setShowDropdown(true);
|
|
setShowAutocomplete(true);
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
helperText={returnHelperText(data.name, data.value)}
|
|
fullWidth
|
|
multiline={multiline}
|
|
rows={"3"}
|
|
color="primary"
|
|
defaultValue={data.value}
|
|
type={"text"}
|
|
placeholder={"The file ID to get"}
|
|
id={"rightside_field_" + count}
|
|
onChange={(event) => {
|
|
changeActionParameter(event, count, data);
|
|
}}
|
|
onBlur={(event) => { }}
|
|
/>
|
|
)
|
|
} else if (
|
|
(data.options !== undefined && data.options !== null && data.options.length > 0) ||
|
|
(selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0)) {
|
|
const parsedoptions = data.options !== undefined && data.options !== null && data.options.length > 0 ? data.options : selectedActionParameters[count].options
|
|
|
|
if (selectedActionParameters[count].value === "") {
|
|
// && selectedActionParameters[count].required) {
|
|
// Rofl, dirty workaround :)
|
|
const e = {
|
|
target: {
|
|
value: parsedoptions[0],
|
|
},
|
|
};
|
|
|
|
changeActionParameter(e, count, data);
|
|
}
|
|
|
|
var multi = false
|
|
if (selectedActionParameters[count].multiselect !== undefined && selectedActionParameters[count].multiselect !== null && selectedActionParameters[count].multiselect === true) {
|
|
multi = true
|
|
|
|
selectedActionParameters[count].value = selectedActionParameters[count].value.split(",")
|
|
}
|
|
|
|
datafield = (
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
multiple={multi}
|
|
value={selectedActionParameters[count].value}
|
|
fullWidth
|
|
id={"rightside_field_" + count}
|
|
onChange={(e) => {
|
|
console.log("MULTI SELECT: ", multi, e.target.value)
|
|
|
|
changeActionParameter(e, count, data);
|
|
setUpdate(Math.random());
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
color: "white",
|
|
height: "50px",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
{parsedoptions.map(
|
|
(data, index) => {
|
|
const split_data = data.split("||");
|
|
var viewed_data = data
|
|
if (split_data.length > 1) {
|
|
viewed_data = split_data[0]
|
|
}
|
|
|
|
viewed_data = (viewed_data.charAt(0).toUpperCase() + viewed_data.slice(1)).replaceAll("_", " ")
|
|
|
|
// Check if it's selected or not and highlight
|
|
var selected = false
|
|
if (selectedActionParameters[count].value.includes(data)) {
|
|
selected = true
|
|
}
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data}
|
|
style={{
|
|
backgroundColor: selected ? theme.palette.backgroundColor : theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data}
|
|
>
|
|
{viewed_data}
|
|
</MenuItem>
|
|
);
|
|
}
|
|
)}
|
|
</Select>
|
|
);
|
|
} else if (data.variant === "STATIC_VALUE") {
|
|
staticcolor = "#FF8544";
|
|
}
|
|
|
|
if (data.field_active === false) {
|
|
//console.log("Field not active: ", data?.name)
|
|
return null
|
|
}
|
|
|
|
// Shows nested list of nodes > their JSON lists
|
|
const ActionlistWrapper = (props) => {
|
|
const handleMenuClose = () => {
|
|
setShowAutocomplete(false);
|
|
|
|
if (
|
|
!selectedActionParameters[count].value[
|
|
selectedActionParameters[count].value.length - 1
|
|
] === "$"
|
|
) {
|
|
setShowDropdown(false);
|
|
}
|
|
|
|
setUpdate(Math.random());
|
|
setMenuPosition(null);
|
|
};
|
|
|
|
const handleItemClick = (values) => {
|
|
if (values === undefined || values === null || values.length === 0) {
|
|
return;
|
|
}
|
|
|
|
var toComplete = selectedActionParameters[count].value.trim()
|
|
.endsWith("$")
|
|
? values[0].autocomplete
|
|
: "$" + values[0].autocomplete;
|
|
|
|
toComplete = toComplete.toLowerCase().replaceAll(" ", "_");
|
|
for (let [key, keyval] in Object.entries(values)) {
|
|
if (key == 0 || values[key].autocomplete.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
toComplete += values[key].autocomplete;
|
|
}
|
|
|
|
|
|
|
|
// Handles the fields under OpenAPI body to be parsed.
|
|
if (data.name.startsWith("${") && data.name.endsWith("}")) {
|
|
const paramcheck = selectedAction.parameters.find(
|
|
(param) => param.name === "body"
|
|
)
|
|
|
|
if (paramcheck !== undefined) {
|
|
if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) {
|
|
paramcheck["value_replace"] = [
|
|
{
|
|
key: data.name,
|
|
value: toComplete,
|
|
},
|
|
]
|
|
} else {
|
|
const subparamindex = paramcheck["value_replace"]
|
|
.findIndex((param) => param.key === data.name);
|
|
|
|
if (subparamindex === -1) {
|
|
paramcheck["value_replace"].push({
|
|
key: data.name,
|
|
value: toComplete,
|
|
})
|
|
|
|
} else {
|
|
paramcheck["value_replace"][subparamindex]["value"] +=
|
|
toComplete;
|
|
}
|
|
}
|
|
|
|
selectedActionParameters[count]["value_replace"] = paramcheck;
|
|
|
|
selectedAction.parameters = selectedActionParameters
|
|
//selectedAction.parameters[count]["value_replace"] = paramcheck;
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
|
|
setShowDropdown(false);
|
|
setMenuPosition(null);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.log("In nestedclick!!")
|
|
var newValue = selectedActionParameters[count].value + toComplete
|
|
changeActionParameter({ target: { value: newValue } }, count, data, true)
|
|
//selectedActionParameters[count].value += toComplete;
|
|
//selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
|
//setSelectedAction(selectedAction);
|
|
//setUpdate(Math.random());
|
|
|
|
setShowDropdown(false);
|
|
setMenuPosition(null);
|
|
};
|
|
|
|
const iconStyle = {
|
|
marginRight: 15,
|
|
};
|
|
|
|
return (
|
|
<Menu
|
|
anchorReference="anchorPosition"
|
|
anchorPosition={menuPosition}
|
|
onClose={() => {
|
|
handleMenuClose();
|
|
}}
|
|
open={!!menuPosition}
|
|
style={{
|
|
color: "white",
|
|
marginTop: 2,
|
|
maxHeight: 650,
|
|
}}
|
|
>
|
|
{actionlist.map((innerdata) => {
|
|
const icon =
|
|
innerdata.type === "action" ? (
|
|
<AppsIcon style={{ marginRight: 10 }} />
|
|
) : innerdata.type === "workflow_variable" ||
|
|
innerdata.type === "execution_variable" ? (
|
|
<FavoriteBorderIcon style={{ marginRight: 10 }} />
|
|
) : (
|
|
<ScheduleIcon style={{ marginRight: 10 }} />
|
|
);
|
|
|
|
const handleExecArgumentHover = (inside) => {
|
|
var exec_text_field = document.getElementById(
|
|
"execution_argument_input_field"
|
|
);
|
|
if (exec_text_field !== null) {
|
|
if (inside) {
|
|
exec_text_field.style.border = "2px solid #FF8544";
|
|
} else {
|
|
exec_text_field.style.border = "";
|
|
}
|
|
}
|
|
|
|
// Also doing arguments
|
|
if (
|
|
workflow.triggers !== undefined &&
|
|
workflow.triggers !== null &&
|
|
workflow.triggers.length > 0
|
|
) {
|
|
for (let [key, keyval] in Object.entries(workflow.triggers)) {
|
|
const item = workflow.triggers[key];
|
|
|
|
if (cy !== undefined) {
|
|
var node = cy.getElementById(item.id);
|
|
if (node.length > 0) {
|
|
if (inside) {
|
|
node.addClass("shuffle-hover-highlight");
|
|
} else {
|
|
node.removeClass("shuffle-hover-highlight");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleActionHover = (inside, actionId) => {
|
|
if (cy !== undefined) {
|
|
var node = cy.getElementById(actionId);
|
|
if (node.length > 0) {
|
|
if (inside) {
|
|
node.addClass("shuffle-hover-highlight");
|
|
} else {
|
|
node.removeClass("shuffle-hover-highlight");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleMouseover = () => {
|
|
if (innerdata.type === "Execution Argument") {
|
|
handleExecArgumentHover(true);
|
|
} else if (innerdata.type === "action") {
|
|
handleActionHover(true, innerdata.id);
|
|
}
|
|
};
|
|
|
|
const handleMouseOut = () => {
|
|
if (innerdata.type === "Execution Argument") {
|
|
handleExecArgumentHover(false);
|
|
} else if (innerdata.type === "action") {
|
|
handleActionHover(false, innerdata.id);
|
|
}
|
|
};
|
|
|
|
var parsedPaths = [];
|
|
if (innerdata.type === "workflow_variable") {
|
|
// Try to parse the value if it's a string that could be JSON
|
|
if (typeof innerdata.value === "string") {
|
|
try {
|
|
const parsedValue = JSON.parse(innerdata.value)
|
|
if (typeof parsedValue === "object") {
|
|
parsedPaths = GetParsedPaths(parsedValue, "");
|
|
}
|
|
} catch (e) {
|
|
// Not valid JSON, use the value directly
|
|
parsedPaths = GetParsedPaths(innerdata.value, "");
|
|
}
|
|
} else if (typeof innerdata.value === "object") {
|
|
parsedPaths = GetParsedPaths(innerdata.value, "");
|
|
}
|
|
} else if (typeof innerdata.example === "object") {
|
|
parsedPaths = GetParsedPaths(innerdata.example, "");
|
|
}
|
|
|
|
const coverColor = "#82ccc3"
|
|
//menuPosition.left -= 50
|
|
//menuPosition.top -= 250
|
|
//console.log("POS: ", menuPosition1)
|
|
var menuPosition1 = menuPosition
|
|
if (menuPosition1 === null) {
|
|
menuPosition1 = {
|
|
"left": 0,
|
|
"top": 0,
|
|
}
|
|
} else if (menuPosition1.top === null || menuPosition1.top === undefined) {
|
|
menuPosition1.top = 0
|
|
} else if (menuPosition1.left === null || menuPosition1.left === undefined) {
|
|
menuPosition1.left = 0
|
|
}
|
|
|
|
//console.log("POS1: ", menuPosition1)
|
|
|
|
return parsedPaths.length > 0 ? (
|
|
<NestedMenuItem
|
|
key={innerdata.name}
|
|
label={
|
|
<div style={{ display: "flex", marginLeft: 0, }}>
|
|
{icon} {innerdata.name}
|
|
</div>
|
|
}
|
|
parentMenuOpen={!!menuPosition}
|
|
style={{
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
maxHeight: 50,
|
|
overflow: "hidden",
|
|
}}
|
|
onClick={() => {
|
|
console.log(innerdata.example)
|
|
handleItemClick([innerdata]);
|
|
}}
|
|
>
|
|
<Paper style={{ minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top - 200, left: menuPosition1.left - 450, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)", }}>
|
|
<MenuItem
|
|
key={innerdata.name}
|
|
style={{
|
|
marginLeft: 15,
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
padding: 0,
|
|
position: "relative",
|
|
}}
|
|
value={innerdata}
|
|
onMouseOver={() => {
|
|
//console.log("HOVER: ", pathdata);
|
|
}}
|
|
onClick={() => {
|
|
handleItemClick([innerdata]);
|
|
}}
|
|
>
|
|
<Typography variant="h6" style={{ paddingBottom: 5 }}>
|
|
{innerdata.name}
|
|
</Typography>
|
|
</MenuItem>
|
|
{parsedPaths.map((pathdata, index) => {
|
|
// FIXME: Should be recursive in here
|
|
//<VpnKeyIcon style={iconStyle} />
|
|
const icon =
|
|
pathdata.type === "value" ? (
|
|
<span style={{ marginLeft: 9, }} />
|
|
) : pathdata.type === "list" ? (
|
|
<FormatListNumberedIcon style={{ marginLeft: 9, marginRight: 10, }} />
|
|
) : (
|
|
<CircleIcon style={{ marginLeft: 9, marginRight: 10, color: coverColor }} />
|
|
);
|
|
//<ExpandMoreIcon style={iconStyle} />
|
|
|
|
const indentation_count = (pathdata.name.match(/\./g) || []).length + 1
|
|
const baseIndent = <div style={{ marginLeft: 20, height: 30, width: 1, backgroundColor: coverColor, }} />
|
|
//const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0
|
|
const boxPadding = 0
|
|
const namesplit = pathdata.name.split(".")
|
|
const newname = namesplit[namesplit.length - 1]
|
|
return (
|
|
<MenuItem
|
|
key={pathdata.name}
|
|
style={{
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
padding: boxPadding,
|
|
}}
|
|
value={pathdata}
|
|
onMouseOver={() => {
|
|
//console.log("HOVER: ", pathdata);
|
|
}}
|
|
onClick={() => {
|
|
handleItemClick([innerdata, pathdata]);
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={`Ex. value: ${pathdata.value}`}
|
|
placement="left"
|
|
>
|
|
<div style={{ display: "flex", height: 30, }}>
|
|
{Array(indentation_count).fill().map((subdata, subindex) => {
|
|
return (
|
|
baseIndent
|
|
)
|
|
})}
|
|
{icon} {newname}
|
|
{pathdata.type === "list" ? <SquareFootIcon style={{ marginleft: 10, }} onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
|
|
console.log("INNER: ", innerdata, pathdata)
|
|
|
|
// Removing .list from autocomplete
|
|
var newname = pathdata.name
|
|
if (newname.length > 5) {
|
|
newname = newname.slice(0, newname.length - 5)
|
|
}
|
|
|
|
//selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}`
|
|
selectedActionParameters[count].value += `$${innerdata.name}.${newname}`
|
|
selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
setShowDropdown(false);
|
|
setMenuPosition(null);
|
|
}} /> : null}
|
|
</div>
|
|
</Tooltip>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Paper>
|
|
</NestedMenuItem>
|
|
) : (
|
|
<MenuItem
|
|
key={innerdata.name}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
minWidth: 250,
|
|
maxWidth: 250,
|
|
marginRight: 0,
|
|
}}
|
|
value={innerdata}
|
|
onMouseOver={() => handleMouseover()}
|
|
onMouseOut={() => {
|
|
handleMouseOut();
|
|
}}
|
|
onClick={() => {
|
|
handleItemClick([innerdata]);
|
|
}}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={`Value: ${innerdata.value}`}
|
|
placement="left"
|
|
>
|
|
<div style={{ display: "flex" }}>
|
|
{icon} {innerdata.name}
|
|
</div>
|
|
</Tooltip>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Menu>
|
|
);
|
|
}
|
|
|
|
const buttonTitle = `Authenticate API ${selectedApp.name.replaceAll("_", " ")}`
|
|
const hasAutocomplete = data?.autocompleted === true
|
|
if (data.variant === undefined || data.variant === null) {
|
|
data.variant = "STATIC_VALUE"
|
|
}
|
|
|
|
var isFirstOptional = optionalFound === false && data.configuration === false && data.required === false ? true : false
|
|
if (optionalFound === false && data.configuration === false && data.required === false) {
|
|
optionalFound = true
|
|
}
|
|
|
|
if (isFirstOptional) {
|
|
// Check if any required fields are found
|
|
var foundRequired = false
|
|
for (var key in selectedActionParameters) {
|
|
if (selectedActionParameters[key]?.required === true) {
|
|
foundRequired = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (!foundRequired) {
|
|
isFirstOptional = false
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div key={data.name} style={{ marginTop: isFirstOptional ? 55 : 5, }}>
|
|
{isFirstOptional ? <Divider style={{ backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null}
|
|
{showButtonField === true ? hideBodyButtonValue : null}
|
|
<div
|
|
style={{ marginTop: 18, marginBottom: 0, display: "flex" }}
|
|
>
|
|
{data.configuration === true ? (
|
|
<Tooltip
|
|
title={buttonTitle}
|
|
placement="top"
|
|
>
|
|
<LockOpenIcon
|
|
style={{
|
|
cursor: "pointer",
|
|
width: 24,
|
|
height: 24,
|
|
marginRight: 10,
|
|
color: "rgba(255,255,255,0.6)",
|
|
}}
|
|
onClick={() => {
|
|
setAuthenticationModalOpen(true);
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
) : null}
|
|
|
|
{hasAutocomplete === true ?
|
|
data.field_active === true ?
|
|
<Tooltip
|
|
color="primary"
|
|
title={"This is an Simplified field to make the body easier to use. NOT required according to the API documentation. If this is filled, it will be overridden in the Advanced Body"}
|
|
placement="top"
|
|
>
|
|
<PriorityHighIcon
|
|
style={{
|
|
color: "rgba(255,255,255,0.5)",
|
|
marginRight: 0,
|
|
}} />
|
|
</Tooltip>
|
|
|
|
:
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Field was autocompleted by Shuffle based on previous actions (same fields or parent nodes)"}
|
|
placement="top"
|
|
>
|
|
<AutoFixHighIcon style={{
|
|
color: "rgba(255,255,255,0.7)",
|
|
marginRight: 10,
|
|
}} />
|
|
</Tooltip>
|
|
:
|
|
null}
|
|
|
|
{showCacheConfig === true ?
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Explore your keys in Datastore"}
|
|
placement="top"
|
|
>
|
|
<a href="/admin?tab=datastore" target="_blank" style={{ textDecoration: "none" }}>
|
|
<StorageIcon style={{
|
|
color: "#FF8544",
|
|
marginRight: 10,
|
|
}} />
|
|
</a>
|
|
</Tooltip>
|
|
: null}
|
|
|
|
<div
|
|
style={{
|
|
flex: "10",
|
|
marginTop: "auto",
|
|
marginBottom: "auto",
|
|
color: "#C5C5C5",
|
|
}}
|
|
>
|
|
{tmpitem} <span style={{ color: theme.palette.main }}>{selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}</span>
|
|
</div>
|
|
|
|
<Tooltip title="Expand editor window" placement="top">
|
|
|
|
<OpenInFullIcon
|
|
style={{ color: "rgba(255,255,255,0.7)", cursor: "pointer", margin: multiline ? 5 : 0, height: 20, width: 20, }}
|
|
onMouseOver={(event) => {
|
|
const clickedField = document.getElementById(clickedFieldId)
|
|
if (clickedField !== null) {
|
|
clickedField.focus()
|
|
}
|
|
}}
|
|
onClick={(event) => {
|
|
// Set focus to the Textfield we just clicked
|
|
// This is to ensure focus is set correctly at all times with blur
|
|
const clickedField = document.getElementById(clickedFieldId)
|
|
if (clickedField !== null) {
|
|
clickedField.focus()
|
|
}
|
|
|
|
event.preventDefault()
|
|
setFieldCount(count)
|
|
setExpansionModalOpen(true)
|
|
setActiveDialog("codeeditor")
|
|
//setcodedata(data.value)
|
|
var parsedvalue = data.value
|
|
if (parsedvalue === undefined || parsedvalue === null) {
|
|
parsedvalue = ""
|
|
}
|
|
|
|
//console.log("Required fields: ", selectedActionParameters[count])
|
|
navigate(`?action_id=${selectedAction.id}&field=${data.name}&action_name=${selectedAction.name}`)
|
|
setEditorData({
|
|
"name": data.name,
|
|
"value": fixExample(parsedvalue),
|
|
"field_number": count,
|
|
"actionlist": actionlist,
|
|
"field_id": clickedFieldId,
|
|
|
|
"example": fixExample(selectedActionParameters[count].example),
|
|
})
|
|
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
|
|
|
|
</div>
|
|
{datafield}
|
|
{/*shufflecode*/}
|
|
{showDropdown &&
|
|
showDropdownNumber === count &&
|
|
data.variant === "STATIC_VALUE" &&
|
|
jsonList.length > 0 ? (
|
|
<FormControl fullWidth style={{ marginTop: 0 }}>
|
|
<InputLabel
|
|
id="action-autocompleter"
|
|
style={{ marginLeft: 10, color: "white" }}
|
|
>
|
|
Autocomplete
|
|
</InputLabel>
|
|
<Select
|
|
MenuProps={{
|
|
disableScrollLock: true,
|
|
}}
|
|
labelId="action-autocompleter"
|
|
SelectDisplayProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
onClose={() => {
|
|
setShowAutocomplete(false);
|
|
|
|
if (!selectedActionParameters[count].value[selectedActionParameters[count].value.length - 1] === ".") {
|
|
setShowDropdown(false);
|
|
}
|
|
|
|
setUpdate(Math.random());
|
|
}}
|
|
onClick={() => {
|
|
setShowAutocomplete(true)
|
|
}}
|
|
fullWidth
|
|
open={showAutocomplete}
|
|
style={{
|
|
color: "white",
|
|
height: 35,
|
|
marginTop: 2,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
onChange={(e) => {
|
|
console.log("SELECT ONCHANGE DONE")
|
|
|
|
if (selectedActionParameters[count].value[selectedActionParameters[count].value.length - 1] === ".") {
|
|
e.target.value.autocomplete = e.target.value.autocomplete.slice(1, e.target.value.autocomplete.length);
|
|
}
|
|
|
|
selectedActionParameters[count].value += e.target.value.autocomplete;
|
|
selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
|
setSelectedAction(selectedAction);
|
|
setUpdate(Math.random());
|
|
|
|
setShowDropdown(false);
|
|
}}
|
|
>
|
|
{jsonList.map((data) => {
|
|
const iconStyle = {
|
|
marginRight: 15,
|
|
};
|
|
|
|
const icon =
|
|
data.type === "value" ? (
|
|
<VpnKeyIcon style={iconStyle} />
|
|
) : data.type === "list" ? (
|
|
<FormatListNumberedIcon style={iconStyle} />
|
|
) : (
|
|
<ExpandMoreIcon style={iconStyle} />
|
|
)
|
|
|
|
return (
|
|
<MenuItem
|
|
key={data.name}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={data}
|
|
onMouseOver={() => { }}
|
|
>
|
|
<Tooltip
|
|
color="primary"
|
|
title={`Ex. value: ${data.value}`}
|
|
placement="left"
|
|
>
|
|
<div style={{ display: "flex" }}>
|
|
{icon} {data.name}
|
|
</div>
|
|
</Tooltip>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
</Select>
|
|
</FormControl>
|
|
) : null}
|
|
{showDropdown &&
|
|
showDropdownNumber === count &&
|
|
data.variant === "STATIC_VALUE" &&
|
|
jsonList.length === 0 ? (
|
|
<ActionlistWrapper actionlist={actionlist} />
|
|
) : null}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
: null
|
|
}
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ParsedAction;
|