Added PoC autocomplete feature based on app return example
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
{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>
|
||||
<InputLabel id="action-autocompleter" style={{marginLeft: 10, color: "white"}}><ArrowUpwardIcon />Autocomplete</InputLabel>
|
||||
<Select
|
||||
@@ -2920,6 +2978,9 @@ const AngularWorkflow = (props) => {
|
||||
<AppActionArguments key={selectedAction.id} selectedAction={selectedAction} />
|
||||
|
||||
</div>
|
||||
<div style={{}}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: null
|
||||
@@ -3288,7 +3349,7 @@ const AngularWorkflow = (props) => {
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
<Button
|
||||
style={{borderRadius: "0px"}}
|
||||
onClick={() => {
|
||||
setSelectedEdge({})
|
||||
|
||||
@@ -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) => {
|
||||
:
|
||||
<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"), {
|
||||
var baseInfo = newAppname.length > 0 ?
|
||||
<div>
|
||||
@@ -454,6 +555,7 @@ const Apps = (props) => {
|
||||
{selectedAction.description}
|
||||
</div>
|
||||
: null}
|
||||
<GetAppExample />
|
||||
</div>
|
||||
:
|
||||
null
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
Reference in New Issue
Block a user