Added PoC autocomplete feature based on app return example

This commit is contained in:
frikky
2020-07-09 11:42:36 +02:00
parent 82f08e3c6b
commit 65d4035cfd
4 changed files with 182 additions and 8 deletions
+11
View File
@@ -142,6 +142,7 @@ type WorkflowAppAction struct {
} `json:"execution_variable" datastore:"execution_variables"` } `json:"execution_variable" datastore:"execution_variables"`
Returns struct { Returns struct {
Description string `json:"description" datastore:"returns" yaml:"description,omitempty"` 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"` ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"` Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
} `json:"returns" datastore:"returns"` } `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 { if skip {
continue continue
} }
+63 -2
View File
@@ -67,6 +67,7 @@ import cxtmenu from 'cytoscape-cxtmenu';
import { w3cwebsocket as W3CWebSocket } from "websocket"; import { w3cwebsocket as W3CWebSocket } from "websocket";
import { useAlert } from "react-alert"; import { useAlert } from "react-alert";
import { GetParsedPaths } from "./Apps";
const surfaceColor = "#27292D" const surfaceColor = "#27292D"
const inputColor = "#383B40" const inputColor = "#383B40"
@@ -2256,6 +2257,7 @@ const AngularWorkflow = (props) => {
const [showDropdown, setShowDropdown] = React.useState(false) const [showDropdown, setShowDropdown] = React.useState(false)
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0) const [showDropdownNumber, setShowDropdownNumber] = React.useState(0)
const [actionlist, setActionlist] = React.useState([]) const [actionlist, setActionlist] = React.useState([])
const [jsonList, setJsonList] = React.useState([])
useEffect(() => { useEffect(() => {
if (selectedActionParameters !== null && selectedActionParameters.length === 0) { 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 selectedActionParameters[count].value = event.target.value
selectedAction.parameters[count].value = event.target.value selectedAction.parameters[count].value = event.target.value
setSelectedAction(selectedAction) setSelectedAction(selectedAction)
@@ -2605,7 +2621,49 @@ const AngularWorkflow = (props) => {
</Tooltip> </Tooltip>
</div> </div>
{datafield} {datafield}
{showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" ? {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length > 0 ?
<FormControl fullWidth>
<InputLabel id="action-autocompleter" style={{marginLeft: 10, color: "white"}}><ArrowUpwardIcon />Autocomplete</InputLabel>
<Select
labelId="action-autocompleter"
SelectDisplayProps={{
style: {
marginLeft: 10,
}
}}
fullWidth
onChange={(e) => {
//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 (
<MenuItem key={data.name} style={{backgroundColor: inputColor, color: "white"}} value={data} onMouseOver={() => {}}>
<Tooltip color="primary" title={`Value: ${data.value}`} placement="left">
<div style={{display: "flex"}}>
{data.name}
</div>
</Tooltip>
</MenuItem>
)
})}
</Select>
</FormControl>
: null}
{showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length === 0 ?
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel id="action-autocompleter" style={{marginLeft: 10, color: "white"}}><ArrowUpwardIcon />Autocomplete</InputLabel> <InputLabel id="action-autocompleter" style={{marginLeft: 10, color: "white"}}><ArrowUpwardIcon />Autocomplete</InputLabel>
<Select <Select
@@ -2920,6 +2978,9 @@ const AngularWorkflow = (props) => {
<AppActionArguments key={selectedAction.id} selectedAction={selectedAction} /> <AppActionArguments key={selectedAction.id} selectedAction={selectedAction} />
</div> </div>
<div style={{}}>
</div>
</div> </div>
</div> </div>
: null : null
@@ -3288,7 +3349,7 @@ const AngularWorkflow = (props) => {
</div> </div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
style={{borderRadius: "0px"}} style={{borderRadius: "0px"}}
onClick={() => { onClick={() => {
setSelectedEdge({}) setSelectedEdge({})
+102
View File
@@ -19,6 +19,7 @@ import Input from '@material-ui/core/Input';
import YAML from 'yaml' import YAML from 'yaml'
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import Breadcrumbs from '@material-ui/core/Breadcrumbs'; import Breadcrumbs from '@material-ui/core/Breadcrumbs';
import ReactJson from 'react-json-view'
import CachedIcon from '@material-ui/icons/Cached'; import CachedIcon from '@material-ui/icons/Cached';
import CloudDownloadIcon from '@material-ui/icons/CloudDownload'; import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
@@ -39,6 +40,51 @@ import CircularProgress from '@material-ui/core/CircularProgress';
const surfaceColor = "#27292D" const surfaceColor = "#27292D"
const inputColor = "#383B40" 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 Apps = (props) => {
const { globalUrl, isLoggedIn, isLoaded } = props; const { globalUrl, isLoggedIn, isLoaded } = props;
@@ -367,6 +413,61 @@ const Apps = (props) => {
: :
<img alt={selectedApp.title} src={selectedApp.large_image} style={{width: 100, height: 100, maxWidth: "100%"}} /> <img alt={selectedApp.title} src={selectedApp.large_image} style={{width: 100, height: 100, maxWidth: "100%"}} />
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 (
<div>
{paths.map(data => {
const circleSize = 10
return (
<MenuItem style={{backgroundColor: inputColor, color: "white"}} value={data} onClick={() => console.log(data.autocomplete)}>
{data.name}
</MenuItem>
)
})}
<ReactJson
src={JSON.parse(showResult)}
theme="solarized"
collapsed={false}
displayDataTypes={true}
name={"Example return value"}
/>
</div>
)
}
return (
<div>
<b>Example return</b><div/>
{selectedAction.returns.example}
</div>
)
}
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"), { //fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"), {
var baseInfo = newAppname.length > 0 ? var baseInfo = newAppname.length > 0 ?
<div> <div>
@@ -454,6 +555,7 @@ const Apps = (props) => {
{selectedAction.description} {selectedAction.description}
</div> </div>
: null} : null}
<GetAppExample />
</div> </div>
: :
null null
+6 -6
View File
@@ -185,15 +185,15 @@ const Workflows = (props) => {
return response.json() return response.json()
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === true) { if (responseJson.success === false) {
setWorkflowExecutions(responseJson) alert.error("Failed getting executions")
} else {
if (responseJson.length > 0) { if (responseJson.length > 0) {
setSelectedExecution(responseJson[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 => { .catch(error => {