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, Restore as RestoreIcon, } 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, suborgWorkflows, originalWorkflow, } = 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 [parentAction, setParentAction] = 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(() => { // This will have the OLD selectedAction, not the new one huh? // How do we map the fields correctly? if (selectedAction === undefined || selectedAction === null) { console.log("Selected action is undefined") return } console.log("Selected action: ", selectedAction?.name, selectedAction) }, [selectedAction]) */ 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 } if (selectedApp !== undefined && selectedApp !== null && selectedApp.generated !== true) { 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(",")) { //console.log("KEYORDER CHANGED! DID ACTION AS WELL?", keyorder, newkeyorder) 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); } if (selectedAction?.parent_controlled === true && workflow?.parentorg_workflow?.length > 0 && originalWorkflow?.id !== undefined && originalWorkflow?.id !== null && originalWorkflow?.id !== workflow?.id && originalWorkflow?.actions !== undefined && originalWorkflow?.actions !== null && originalWorkflow?.actions.length > 0) { // Due to ID remapping of actions not happening, this is easy for (var key in originalWorkflow.actions) { const curparentAction = originalWorkflow.actions[key] if (curparentAction.id === selectedAction.id) { if (curparentAction.parameters === undefined || curparentAction.parameters === null || curparentAction.parameters.length === 0) { console.log("Parent parameters missing!") break } if (curparentAction.id !== parentAction?.id) { setParentAction(curparentAction) } break } } } }, [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: "Runtime Argument", name: "Runtime Argument", value: "$exec", highlight: "exec", autocomplete: "exec", example: valid.result, }) break } } } } // Add default Runtime Argument if none were added if (newActionList.length === 0) { newActionList.push({ type: "Runtime Argument", name: "Runtime 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 Datastore", 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 === "Runtime Argument" || labels.includes(parentNode.label)) { continue } labels.push(parentNode.label); let exampleData = parentNode.example ?? ""; if (parentNode?.app_name === "http") { exampleData = "" } if (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 (exampleData === "" && apps !== undefined && apps !== null && apps?.length > 0) { // Check apps if it exists, then if it const foundApp = apps?.find(app => app?.id === parentNode?.app_id) if (foundApp !== undefined && foundApp !== null) { if (foundApp?.generated === true || foundApp?.name === "http") { const validationData = validateJson(`{ "status": 200, "body": { "example": "json", "values": "json" }, "url": "https://example.com", "headers": { "Content-Type": "application/json", "Example-Header": "two" }, "cookies": { "example": "session", "__session": "sessionid" }, "success": true }`) if (validationData.valid) { exampleData = validationData.result } } } } 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 = //} return helperText } const errorHelperText = (name, value, error) => { return (
{error}
); } 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 When the key ({actionKey}) is static, but the value is a list ({actionValue}), it will overwrite the list. You may be looking for the { }} style={{ cursor: "pointer", color: "#FF8544", }}>Check Cache Contains action instead. } } 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 Tip: {suggestionText} } // 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 (isAgent || isIntegration) { // Check if actionname uppercase is in the parsedDatatypeImages() dictionary if (isAgent) { wrapperapp.large_image = theme.palette.singulBlackWhite newimage = wrapperapp.large_image } else if (isIntegration) { wrapperapp.large_image = theme.palette.singulGreen newimage = theme.palette.singulGreen } else { const uppercaseimage = parsedDatatypeImages()[actionname.toUpperCase()] if (uppercaseimage !== undefined) { var newimage = uppercaseimage //newimage = 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 (
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) } }} >
{useIcon} {newActionname}
{extraDescription.length > 0 ? {extraDescription} : null}
) } 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 (
{hideExtraTypes === true ? null : (
{ //window.open("/apps/${selectedAction.app_id}", "_blank") }} >

{newAppname}

{ 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.") } } }} > { setAuthenticationModalOpen(true) }} > { if (setAiQueryModalOpen !== undefined) { setAiQueryModalOpen(true) } else { aiSubmit("Fill based on previous values", undefined, undefined, selectedAction) } setAutocompleting(true) setTimeout(() => { setAutocompleting(false) }, 3000) }} > {autoCompleting ? : } {(selectedAction?.generated === true && selectedAction?.app_version === "1.0.0") || (selectedAction?.app_name === "Shuffle Tools" && selectedAction?.app_version !== "1.2.0") ? : null}
{selectedApp.versions !== null && selectedApp.versions !== undefined && selectedApp.versions.length > 1 ? ( ) : null}
Name 0} 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) }} />
{/*!isCloud ? null :*/}
Delay 0} placeholder={selectedAction.execution_delay} value={delay} onChange={(event) => { setDelay(event.target.value) }} />
{/**/}
)} {selectedApp.name !== undefined && selectedAction.authentication !== null && selectedAction.authentication !== undefined && selectedAction.authentication.length === 0 && requiresAuthentication ? (
) : null} {selectedAction.authentication !== undefined && selectedAction.authentication !== null && selectedAction.authentication.length > 0 ? (
Authentication {parentAction?.authentication_id !== undefined && parentAction?.authentication_id !== null && parentAction?.authentication_id !== "" && parentAction?.authentication_id !== selectedAction?.authentication_id ? { selectedAction.authentication_id = parentAction.authentication_id // Check if we can select the parent auth or not console.log("Changing selected auth to parent: ", parentAction, selectedAction.authentication) if (selectedAction.authentication !== undefined && selectedAction.authentication !== null && selectedAction.authentication.length > 0) { var found = false for (var key in selectedAction.authentication) { if (selectedAction.authentication[key].id === parentAction.authentication_id) { selectedAction.selectedAuthentication = selectedAction.authentication[key] found = true break } } if (!found) { toast.error("Couldn't find parent authentication. Is it distributed to this suborg?") } } else { toast.warning("No matching authentication found for this app. Is it distributed to this suborg?") selectedAction.selectedAuthentication = { "id": parentAction.authentication_id, "name": "Parent auth", "label": "Parent auth", } } setSelectedAction(selectedAction) setUpdate(Math.random()) }} /> : null}
0 && Object.getOwnPropertyNames(selectedAction?.selectedAuthentication).length !== 0 ? (
{ changeDistribution(selectedAction?.selectedAuthentication, "auth") }} name="distributeAuth" color="primary" /> } label="Distribute auth to suborgs" />
) : null } placement="left">
{ setAuthenticationModalOpen(true); }} >
) : null} {selectedAction.authentication_id === "authgroups" && (authGroups === undefined || authGroups === null || authGroups.length === 0) ? Create your first Authentication group : null} {/*showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
Environment
) : null*/} {workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
Execution variable (optional)
) : null}
{/*hideExtraTypes ? null :
Actions
*/} {isAgent ? null : setNewSelectedAction !== undefined ? ( 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 (
  • {params.group} {params.children}
  • ) }} 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 ( ); }} 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 ( 0)} /> ); }} /> ) : null} {selectedAction?.app_name === "Shuffle AI" && selectedAction?.name === "run_llm" ? selectedAction?.environment === "Cloud" && isCloud ? ( Info: Cloud Inference processing runs with Shuffle's GPUs in EU, Netherlands, and may be unstable. Your data is NOT stored there. ) : This action is slow without GPU's. Use Shuffle's Cloud Runtime location for faster processing. : null }
    { selectedActionParameters !== undefined && selectedActionParameters !== null && Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0 ?
    {isIntegration || isAgent ? apps !== undefined && apps !== null && apps.length > 0 ?
    { 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()) }}>
    {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 (
    { 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()) }}>
    ) })}
    : null :
    {/* */} } {selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ?
    Select an app you want to use { 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 (
    {useIcon} {newActionname}
    ); }} 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 ( ); }} />
    : null} {selectedAction.description !== undefined && selectedAction.description !== null && selectedAction.description.length > 0 && hiddenParameters === false ? (
    Description {selectedAction.description}
    ) : 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 ( Authentication fields are hidden ); */ } // 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 = (
    { // 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) }} /> { 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) }} />
    ) 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 = ( {tmpitem.charAt(0).toUpperCase() + tmpitem.slice(1)} { setUiBox("closed") }} > Required: {data.required === true || data.configuration === true ? "True" : "False"} Description: {description} Ex. : {data?.example?.length > 0 ? data.example : "No example available"} { data?.configuration === true ? ( Auth: Use "\$" instead of "$" ) : null }
    { e.preventDefault() e.stopPropagation() localStorage.setItem("disabled_ui_box", "true") setUiBox("closed") }}> Don't show again
    ); if (selectedApp.name === "email") { //hideBody = false showButtonField = false hideBodyButtonValue = null } if ((multiline === undefined || multiline === false) && ((data?.autocompleted === true || data?.field_active === true) || data.name.startsWith("${") && data.name.endsWith("}"))) { multiline = true } if (data?.autocompleted === true || data?.field_active === true) { rows = "1" } var datafield = ( { 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); }} /> ), }} multiline={multiline} onClick={() => { /* setExpansionModalOpen(false); */ if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { scrollConfig.selected = clickedFieldId setScrollConfig(scrollConfig) } }} minRows={rows} maxRows={6} 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") } }} /> ) // 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 =
    {tmpheaders.map((inputdata, index) => { const oldkey = inputdata.key const oldval = inputdata.value return (
    { 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) }} /> { 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) }} />
    ) })}
    } //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 = ( { setMenuPosition({ top: event.pageY + 10, left: event.pageX + 10, }); setShowDropdownNumber(count); setShowDropdown(true); setShowAutocomplete(true); }} /> ), }} helperText={returnHelperText(data.name, data.value)} fullWidth multiline={multiline} minRows={3} maxRows={6} 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 = ( ); } 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 ( { handleMenuClose(); }} open={!!menuPosition} style={{ color: "white", marginTop: 2, maxHeight: 650, }} > {actionlist.map((innerdata) => { const icon = innerdata.type === "action" ? ( ) : innerdata.type === "workflow_variable" || innerdata.type === "execution_variable" ? ( ) : ( ); 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 === "Runtime Argument") { handleExecArgumentHover(true); } else if (innerdata.type === "action") { handleActionHover(true, innerdata.id); } }; const handleMouseOut = () => { if (innerdata.type === "Runtime 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 ? ( {icon} {innerdata.name}
    } parentMenuOpen={!!menuPosition} style={{ color: "white", minWidth: 250, maxWidth: 250, maxHeight: 50, overflow: "hidden", }} onClick={() => { console.log(innerdata.example) handleItemClick([innerdata]); }} > { //console.log("HOVER: ", pathdata); }} onClick={() => { handleItemClick([innerdata]); }} > {innerdata.name} {parsedPaths.map((pathdata, index) => { // FIXME: Should be recursive in here // const icon = pathdata.type === "value" ? ( ) : pathdata.type === "list" ? ( ) : ( ); // const indentation_count = (pathdata.name.match(/\./g) || []).length + 1 const baseIndent =
    //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 ( { //console.log("HOVER: ", pathdata); }} onClick={() => { handleItemClick([innerdata, pathdata]); }} >
    {Array(indentation_count).fill().map((subdata, subindex) => { return ( baseIndent ) })} {icon} {newname} {pathdata.type === "list" ? { 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}
    ); })} ) : ( handleMouseover()} onMouseOut={() => { handleMouseOut(); }} onClick={() => { handleItemClick([innerdata]); }} >
    {icon} {innerdata.name}
    ); })} ); } 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 } } var parentParamValue = "" if (parentAction !== undefined && parentAction !== null && parentAction !== "" && parentAction.parameters !== undefined && parentAction.parameters !== null) { const foundParamIndex = parentAction.parameters.findIndex((param) => param.name === data.name) if (foundParamIndex !== -1) { parentParamValue = parentAction.parameters[foundParamIndex].value } } return (
    {isFirstOptional ? : null} {showButtonField === true ? hideBodyButtonValue : null}
    {data.configuration === true ? ( { setAuthenticationModalOpen(true); }} /> ) : null} {hasAutocomplete === true ? data.field_active === true ? : : null} {showCacheConfig === true ? : null}
    {tmpitem} {selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""}
    {parentParamValue !== undefined && parentParamValue !== null && parentParamValue !== "" && parentParamValue !== data.value ? { selectedActionParameters[count].value = parentParamValue selectedAction.parameters[count].value = parentParamValue setSelectedAction(selectedAction) setUpdate(Math.random()) }} /> : null} { 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}&app_name=${selectedAction?.label}`) setEditorData({ "name": data.name, "value": fixExample(parsedvalue), "field_number": count, "actionlist": actionlist, "field_id": clickedFieldId, "example": fixExample(selectedActionParameters[count].example), }) }} />
    {datafield} {/*shufflecode*/} {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length > 0 ? ( Autocomplete ) : null} {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length === 0 ? ( ) : null}
    ); })}
    : null }
    ); }; export default ParsedAction;