diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 7f40ec0f..b7b2baae 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -142,6 +142,7 @@ type WorkflowAppAction struct { } `json:"execution_variable" datastore:"execution_variables"` Returns struct { Description string `json:"description" datastore:"returns" yaml:"description,omitempty"` + Example string `json:"example" datastore:"example" yaml:"example"` ID string `json:"id" datastore:"id" yaml:"id,omitempty"` Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"` } `json:"returns" datastore:"returns"` @@ -4205,6 +4206,16 @@ func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra strin } } + /* + if workflowapp.Name == "thehive" { + for _, action := range workflowapp.Actions { + if len(action.Returns.Example) > 0 { + log.Printf("ACTION: %#v", action) + } + } + } + */ + if skip { continue } diff --git a/frontend/src/AngularWorkflow.js b/frontend/src/AngularWorkflow.js index 5eaad198..d3d567ee 100644 --- a/frontend/src/AngularWorkflow.js +++ b/frontend/src/AngularWorkflow.js @@ -67,6 +67,7 @@ import cxtmenu from 'cytoscape-cxtmenu'; import { w3cwebsocket as W3CWebSocket } from "websocket"; import { useAlert } from "react-alert"; +import { GetParsedPaths } from "./Apps"; const surfaceColor = "#27292D" const inputColor = "#383B40" @@ -2256,6 +2257,7 @@ const AngularWorkflow = (props) => { const [showDropdown, setShowDropdown] = React.useState(false) const [showDropdownNumber, setShowDropdownNumber] = React.useState(0) const [actionlist, setActionlist] = React.useState([]) + const [jsonList, setJsonList] = React.useState([]) useEffect(() => { if (selectedActionParameters !== null && selectedActionParameters.length === 0) { @@ -2317,6 +2319,20 @@ const AngularWorkflow = (props) => { } } + if (event.target.value[event.target.value.length-1] === ".") { + console.log("GET THE LAST ARGUMENT FOR !") + //const [jsonList, getJsonList] = React.useState([]) + const inputdata = {"data": "1.2.3.4", "dataType": "4.5.6.6"} + const returnJson = GetParsedPaths(inputdata, "") + console.log(jsonList) + setJsonList(returnJson) + + if (!showDropdown) { + setShowDropdown(true) + setShowDropdownNumber(count) + } + } + selectedActionParameters[count].value = event.target.value selectedAction.parameters[count].value = event.target.value setSelectedAction(selectedAction) @@ -2605,7 +2621,49 @@ const AngularWorkflow = (props) => { {datafield} - {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" ? + {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length > 0 ? + + Autocomplete + { + //parsedValues.push({"name": basekeyname, "autocomplete": `${basekey}.${key}`}) + console.log("CHANGE!") + 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 + console.log("TARGET: ", selectedActionParameters) + setSelectedAction(selectedAction) + setUpdate("action"+e.target.value.name) + + setShowDropdown(false) + }} + style={{border: `2px solid #f85a3e`, color: "white", height: "50px"}} + > + {jsonList.map(data => { + return ( + {}}> + + + {data.name} + + + + ) + })} + + + : null} + {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length === 0 ? Autocomplete { + + + : null @@ -3288,7 +3349,7 @@ const AngularWorkflow = (props) => { - { setSelectedEdge({}) diff --git a/frontend/src/Apps.js b/frontend/src/Apps.js index 3e48b8e6..140fdf7d 100644 --- a/frontend/src/Apps.js +++ b/frontend/src/Apps.js @@ -19,6 +19,7 @@ import Input from '@material-ui/core/Input'; import YAML from 'yaml' import {Link} from 'react-router-dom'; import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import ReactJson from 'react-json-view' import CachedIcon from '@material-ui/icons/Cached'; import CloudDownloadIcon from '@material-ui/icons/CloudDownload'; @@ -39,6 +40,51 @@ import CircularProgress from '@material-ui/core/CircularProgress'; const surfaceColor = "#27292D" const inputColor = "#383B40" +// Parses JSON data into keys that can be used everywhere :) +export const GetParsedPaths = (inputdata, basekey) => { + const splitkey = " => " + var parsedValues = [] + for (const [key, value] of Object.entries(inputdata)) { + + // Check if loop or JSON + const extra = basekey.length > 0 ? splitkey : "" + const basekeyname = `${basekey.slice(1,basekey.length).split(".").join(splitkey)}${extra}${key}` + if (typeof(value) === 'object') { + if (Array.isArray(value)) { + // Check if each item is object + parsedValues.push({"name": basekeyname, "autocomplete": `${basekey}.${key}`}) + parsedValues.push({"name": `${basekeyname}${splitkey}list`, "autocomplete": `${basekey}.${key}.#`}) + + // Only check the first. This would be probably be dumb otherwise. + for (var subkey in value) { + if (typeof(value) === 'object') { + const returnValues = GetParsedPaths(value[subkey], `${basekey}.${key}.#`) + for (var subkey in returnValues) { + parsedValues.push(returnValues[subkey]) + } + } + + // Don't need else as # (all items) is already defined before the loop + + break + } + //console.log(key+" is array") + } else { + parsedValues.push({"name": basekeyname, "autocomplete": `${basekey}.${key}`}) + const returnValues = GetParsedPaths(value, `${basekey}.${key}`) + for (var subkey in returnValues) { + parsedValues.push(returnValues[subkey]) + } + } + } else { + parsedValues.push({"name": basekeyname, "autocomplete": `${basekey}.${key}`}) + } + } + + return parsedValues +} + + const Apps = (props) => { const { globalUrl, isLoggedIn, isLoaded } = props; @@ -367,6 +413,61 @@ const Apps = (props) => { : + const GetAppExample = () => { + if (selectedAction.returns === undefined) { + return null + } + + var showResult = selectedAction.returns.example + if (showResult === undefined || showResult === null || showResult.length === 0) { + return null + } + + var jsonvalid = true + try { + const tmp = String(JSON.parse(showResult)) + if (!tmp.includes("{") && !tmp.includes("[")) { + jsonvalid = false + } + } catch (e) { + jsonvalid = false + } + + + // FIXME: In here -> parse the values into a list or something + if (jsonvalid) { + const paths = GetParsedPaths(JSON.parse(showResult), "") + console.log("PATHS: ", paths) + + return ( + + {paths.map(data => { + const circleSize = 10 + return ( + console.log(data.autocomplete)}> + {data.name} + + ) + })} + + + ) + } + + return ( + + Example return + {selectedAction.returns.example} + + ) + } + //fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"), { var baseInfo = newAppname.length > 0 ? @@ -454,6 +555,7 @@ const Apps = (props) => { {selectedAction.description} : null} + : null diff --git a/frontend/src/Workflows.js b/frontend/src/Workflows.js index e3dccbec..0bd40953 100644 --- a/frontend/src/Workflows.js +++ b/frontend/src/Workflows.js @@ -185,15 +185,15 @@ const Workflows = (props) => { return response.json() }) .then((responseJson) => { - if (responseJson.success === true) { - setWorkflowExecutions(responseJson) + if (responseJson.success === false) { + alert.error("Failed getting executions") + } else { if (responseJson.length > 0) { setSelectedExecution(responseJson[0]) + setWorkflowExecutions(responseJson) + } else { + alert.info("Couldn't find executions for the workflow") } - } else if (!responseJson.success && responseJson.reason !== undefined) { - alert.error("Failed loading executions: "+responseJson.reason) - } else { - alert.error("Failed loading executions for workflow") } }) .catch(error => {