Added usecase editing & app framework handling in any place
This commit is contained in:
@@ -2,7 +2,7 @@ module main
|
|||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
|
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
|
||||||
|
|
||||||
//replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi
|
//replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi
|
||||||
//replace github.com/frikky/go-elasticsearch => ../../../../git/go-elasticsearch
|
//replace github.com/frikky/go-elasticsearch => ../../../../git/go-elasticsearch
|
||||||
@@ -24,7 +24,7 @@ require (
|
|||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da // indirect
|
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da // indirect
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shuffle/shuffle-shared v0.2.7
|
github.com/shuffle/shuffle-shared v0.2.9
|
||||||
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce
|
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce
|
||||||
google.golang.org/api v0.65.0
|
google.golang.org/api v0.65.0
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
services:
|
services:
|
||||||
frontend:
|
frontend:
|
||||||
#build: ./frontend
|
build: ./frontend
|
||||||
image: ghcr.io/frikky/shuffle-frontend:nightly
|
image: ghcr.io/frikky/shuffle-frontend:nightly
|
||||||
container_name: shuffle-frontend
|
container_name: shuffle-frontend
|
||||||
hostname: shuffle-frontend
|
hostname: shuffle-frontend
|
||||||
@@ -16,7 +16,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
backend:
|
backend:
|
||||||
#build: ./backend
|
build: ./backend
|
||||||
image: ghcr.io/frikky/shuffle-backend:nightly
|
image: ghcr.io/frikky/shuffle-backend:nightly
|
||||||
container_name: shuffle-backend
|
container_name: shuffle-backend
|
||||||
hostname: ${BACKEND_HOSTNAME}
|
hostname: ${BACKEND_HOSTNAME}
|
||||||
@@ -68,7 +68,7 @@ services:
|
|||||||
container_name: shuffle-opensearch
|
container_name: shuffle-opensearch
|
||||||
environment:
|
environment:
|
||||||
- bootstrap.memory_lock=true
|
- bootstrap.memory_lock=true
|
||||||
- "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
|
- "OPENSEARCH_JAVA_OPTS=-Xms1024m -Xmx1024m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
|
||||||
- cluster.routing.allocation.disk.threshold_enabled=false
|
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||||
- cluster.name=shuffle-cluster
|
- cluster.name=shuffle-cluster
|
||||||
- node.name=shuffle-opensearch
|
- node.name=shuffle-opensearch
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { securityFramework } from "./LandingpageUsecases.jsx";
|
|||||||
import CytoscapeComponent from 'react-cytoscapejs';
|
import CytoscapeComponent from 'react-cytoscapejs';
|
||||||
import frameworkStyle from '../frameworkStyle.jsx';
|
import frameworkStyle from '../frameworkStyle.jsx';
|
||||||
import WorkflowSearch from './Workflowsearch.jsx';
|
import WorkflowSearch from './Workflowsearch.jsx';
|
||||||
import uuid from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
|
|
||||||
@@ -518,7 +518,7 @@ export const usecases = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Framework = (props) => {
|
const Framework = (props) => {
|
||||||
const {globalUrl, isLoaded, showOptions, selectedOption, rolling, frameworkData, size } = props;
|
const {globalUrl, isLoaded, showOptions, selectedOption, rolling, frameworkData, size, inputUsecase, isLoggedIn, } = props;
|
||||||
const [cy, setCy] = React.useState()
|
const [cy, setCy] = React.useState()
|
||||||
const [edgesStarted, setEdgesStarted] = React.useState(false)
|
const [edgesStarted, setEdgesStarted] = React.useState(false)
|
||||||
const [graphDone, setGraphDone] = React.useState(false)
|
const [graphDone, setGraphDone] = React.useState(false)
|
||||||
@@ -532,6 +532,50 @@ const Framework = (props) => {
|
|||||||
|
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
|
|
||||||
|
const setUsecaseItem = (inputUsecase) => {
|
||||||
|
var parsedUsecase = inputUsecase
|
||||||
|
const edges = cy.edges().jsons()
|
||||||
|
|
||||||
|
parsedUsecase.process = []
|
||||||
|
for (var key in edges) {
|
||||||
|
const edge = edges[key]
|
||||||
|
console.log("Edge: ", edge)
|
||||||
|
parsedUsecase.process.push(edge.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(parsedUsecase),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for framework!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
if (responseJson.reason !== undefined) {
|
||||||
|
alert.error("Failed updating: " + responseJson.reason)
|
||||||
|
} else {
|
||||||
|
alert.error("Failed to update framework for your org.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert.info("Updated usecase.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
alert.error(error.toString());
|
||||||
|
//setFrameworkLoaded(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const setFrameworkItem = (data) => {
|
const setFrameworkItem = (data) => {
|
||||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -586,17 +630,18 @@ const Framework = (props) => {
|
|||||||
const foundelement = cy.getElementById(discoveryData.id)
|
const foundelement = cy.getElementById(discoveryData.id)
|
||||||
if (foundelement !== undefined && foundelement !== null) {
|
if (foundelement !== undefined && foundelement !== null) {
|
||||||
foundelement.data("large_image", newSelectedApp.image_url)
|
foundelement.data("large_image", newSelectedApp.image_url)
|
||||||
foundelement.data("text_margin_y", "60px")
|
|
||||||
foundelement.data("margin_x", "0px")
|
foundelement.data("margin_x", "0px")
|
||||||
foundelement.data("margin_y", "50px")
|
foundelement.data("margin_y", "0px")
|
||||||
foundelement.data("width", "85px")
|
foundelement.data("text_margin_y", `${60*scale}px`)
|
||||||
foundelement.data("height", "85px")
|
foundelement.data("width", `${85*scale}px`)
|
||||||
|
foundelement.data("height", `${85*scale}px`)
|
||||||
}
|
}
|
||||||
|
|
||||||
setFrameworkItem(submitValue)
|
setFrameworkItem(submitValue)
|
||||||
|
|
||||||
}, [newSelectedApp])
|
}, [newSelectedApp])
|
||||||
|
|
||||||
|
|
||||||
const isCloud =
|
const isCloud =
|
||||||
window.location.host === "localhost:3002" ||
|
window.location.host === "localhost:3002" ||
|
||||||
window.location.host === "shuffler.io";
|
window.location.host === "shuffler.io";
|
||||||
@@ -649,7 +694,6 @@ const Framework = (props) => {
|
|||||||
const [usecaseType, setUsecaseType] = React.useState(0)
|
const [usecaseType, setUsecaseType] = React.useState(0)
|
||||||
const [selectedUsecase, setSelectedUsecase] = React.useState(selectedOption !== undefined ? selectedOption : "Phishing")
|
const [selectedUsecase, setSelectedUsecase] = React.useState(selectedOption !== undefined ? selectedOption : "Phishing")
|
||||||
|
|
||||||
|
|
||||||
const elements = []
|
const elements = []
|
||||||
const surfaceColor = "#27292D"
|
const surfaceColor = "#27292D"
|
||||||
|
|
||||||
@@ -680,12 +724,15 @@ const Framework = (props) => {
|
|||||||
|
|
||||||
setSelectedUsecase(value)
|
setSelectedUsecase(value)
|
||||||
|
|
||||||
const allEdges = cy.edges().jsons()
|
const edges = cy.edges()
|
||||||
for (var key in allEdges) {
|
if (edges !== undefined) {
|
||||||
const newedge = allEdges[key]
|
const allEdges = edges.jsons()
|
||||||
const foundelement = cy.getElementById(newedge.data.id)
|
for (var key in allEdges) {
|
||||||
if (foundelement !== undefined && foundelement !== null) {
|
const newedge = allEdges[key]
|
||||||
foundelement.remove()
|
const foundelement = cy.getElementById(newedge.data.id)
|
||||||
|
if (foundelement !== undefined && foundelement !== null) {
|
||||||
|
foundelement.remove()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,7 +766,6 @@ const Framework = (props) => {
|
|||||||
if (cy !== undefined && cyDone && !animationStarted) {
|
if (cy !== undefined && cyDone && !animationStarted) {
|
||||||
const foundelement = cy.getElementById("CASES")
|
const foundelement = cy.getElementById("CASES")
|
||||||
|
|
||||||
console.log("FOUND: ", foundelement)
|
|
||||||
if (foundelement !== undefined && foundelement !== null) {
|
if (foundelement !== undefined && foundelement !== null) {
|
||||||
setAnimationStarted(true)
|
setAnimationStarted(true)
|
||||||
|
|
||||||
@@ -819,6 +865,7 @@ const Framework = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const onNodeSelect = (event) => {
|
const onNodeSelect = (event) => {
|
||||||
const data = event.target.data();
|
const data = event.target.data();
|
||||||
console.log("Node: ", data)
|
console.log("Node: ", data)
|
||||||
@@ -841,7 +888,7 @@ const Framework = (props) => {
|
|||||||
const onEdgeSelect = (event) => {
|
const onEdgeSelect = (event) => {
|
||||||
console.log(event)
|
console.log(event)
|
||||||
|
|
||||||
if (event.target.data("human") === undefined) {
|
if (event.target.data("human") === undefined || event.target.data("human") === false) {
|
||||||
event.target.data("human", true)
|
event.target.data("human", true)
|
||||||
} else if (event.target.data("human") === true) {
|
} else if (event.target.data("human") === true) {
|
||||||
event.target.remove()
|
event.target.remove()
|
||||||
@@ -854,9 +901,6 @@ const Framework = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (graphDone && cyDone === false) {
|
if (graphDone && cyDone === false) {
|
||||||
//const [cyDone, setCyDone] = React.useState(false)
|
|
||||||
console.log("IN CYDONe")
|
|
||||||
|
|
||||||
cy.fit(null, 200);
|
cy.fit(null, 200);
|
||||||
cy.on("select", "edge", (e) => onEdgeSelect(e));
|
cy.on("select", "edge", (e) => onEdgeSelect(e));
|
||||||
cy.on("add", "edge", (e) => onEdgeAdded(e));
|
cy.on("add", "edge", (e) => onEdgeAdded(e));
|
||||||
@@ -878,9 +922,9 @@ const Framework = (props) => {
|
|||||||
const baselocationX = 285*scale
|
const baselocationX = 285*scale
|
||||||
const baselocationY = 50*scale
|
const baselocationY = 50*scale
|
||||||
const shiftmodifier = 3*scale
|
const shiftmodifier = 3*scale
|
||||||
|
|
||||||
const svgSize = `${40*scale}px`
|
const svgSize = `${40*scale}px`
|
||||||
|
|
||||||
|
|
||||||
console.log("Framework: ", parsedFrameworkData)
|
console.log("Framework: ", parsedFrameworkData)
|
||||||
const fontSize = `${12*scale}px`
|
const fontSize = `${12*scale}px`
|
||||||
const defaultSize = `${85*scale}px`
|
const defaultSize = `${85*scale}px`
|
||||||
@@ -1217,6 +1261,27 @@ const Framework = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeUsecase(selectedUsecase, usecaseType)
|
changeUsecase(selectedUsecase, usecaseType)
|
||||||
|
|
||||||
|
if (inputUsecase !== undefined && inputUsecase !== null) {
|
||||||
|
console.log("Got usecase: ", inputUsecase)
|
||||||
|
|
||||||
|
for (var key in inputUsecase.process) {
|
||||||
|
if (inputUsecase.process[key].source === "" || inputUsecase.process[key].target === "") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Edge: ", inputUsecase.process[key])
|
||||||
|
inputUsecase.process[key].label = parseInt(key)+1
|
||||||
|
inputUsecase.process[key].id = uuidv4();
|
||||||
|
|
||||||
|
cy.add({
|
||||||
|
group: "edges",
|
||||||
|
data: inputUsecase.process[key],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//changeUsecase(inputUsecase, 0)
|
||||||
|
//setSelectedUsecase(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedOption !== undefined && selectedUsecase !== selectedOption) {
|
if (selectedOption !== undefined && selectedUsecase !== selectedOption) {
|
||||||
@@ -1571,6 +1636,13 @@ const Framework = (props) => {
|
|||||||
setCy(incy)
|
setCy(incy)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{isCloud && inputUsecase !== undefined && inputUsecase !== null && isLoggedIn === true && Object.keys(inputUsecase).length > 0 ?
|
||||||
|
<Button color="secondary" variant={"outlined"} style={{position: "absolute", bottom: -10, right: 0,}} onClick={() => {
|
||||||
|
setUsecaseItem(inputUsecase)
|
||||||
|
}}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ const ParsedAction = (props) => {
|
|||||||
actionDelayChange,
|
actionDelayChange,
|
||||||
getParents,
|
getParents,
|
||||||
isCloud,
|
isCloud,
|
||||||
|
lastSaved,
|
||||||
|
setLastSaved,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
//const theme = useTheme();
|
//const theme = useTheme();
|
||||||
@@ -1487,6 +1489,9 @@ const ParsedAction = (props) => {
|
|||||||
}
|
}
|
||||||
onBlur={(event) => {
|
onBlur={(event) => {
|
||||||
baseHelperText = calculateHelpertext(event.target.value)
|
baseHelperText = calculateHelpertext(event.target.value)
|
||||||
|
if (setLastSaved !== undefined) {
|
||||||
|
setLastSaved(false)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -2201,35 +2206,6 @@ const ParsedAction = (props) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const expansionModal = (
|
|
||||||
<Dialog
|
|
||||||
disableEnforceFocus={false}
|
|
||||||
hideBackdrop={true}
|
|
||||||
open={expansionModalOpen}
|
|
||||||
onClose={() => {
|
|
||||||
setExpansionModalOpen(false);
|
|
||||||
}}
|
|
||||||
PaperProps={{
|
|
||||||
style: {
|
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
|
||||||
color: "white",
|
|
||||||
minWidth: 600,
|
|
||||||
padding: 50,
|
|
||||||
pointerEvents: "auto",
|
|
||||||
maxHeight: 550,
|
|
||||||
overflowY: "auto",
|
|
||||||
overflowX: "hidden",
|
|
||||||
zIndex: 10012,
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogTitle>
|
|
||||||
<span style={{ color: "white" }}>Workflow Variable</span>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>Hello</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
//const CustomPopper = function (props) {
|
//const CustomPopper = function (props) {
|
||||||
// const classes = useStyles()
|
// const classes = useStyles()
|
||||||
// return <Popper {...props} className={classes.root} placement="bottom" />
|
// return <Popper {...props} className={classes.root} placement="bottom" />
|
||||||
@@ -2239,7 +2215,6 @@ const ParsedAction = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div style={appApiViewStyle} id="parsed_action_view">
|
<div style={appApiViewStyle} id="parsed_action_view">
|
||||||
|
|
||||||
{expansionModal}
|
|
||||||
{hideExtraTypes === true ? null : (
|
{hideExtraTypes === true ? null : (
|
||||||
<span>
|
<span>
|
||||||
<div style={{ display: "flex", minHeight: 40, marginBottom: 30 }}>
|
<div style={{ display: "flex", minHeight: 40, marginBottom: 30 }}>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
|
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
import { orange } from '@mui/material/colors';
|
import { orange } from '@mui/material/colors';
|
||||||
|
import { isMobile } from "react-device-detect"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FullscreenExit as FullscreenExitIcon,
|
FullscreenExit as FullscreenExitIcon,
|
||||||
@@ -86,7 +87,7 @@ const CodeEditor = (props) => {
|
|||||||
var popup = false
|
var popup = false
|
||||||
|
|
||||||
for(var ch=0; ch<code_line.length; ch++){
|
for(var ch=0; ch<code_line.length; ch++){
|
||||||
if(code_line[ch] == '$'){
|
if(code_line[ch] === '$'){
|
||||||
dollar_occurences.push(ch)
|
dollar_occurences.push(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +119,7 @@ const CodeEditor = (props) => {
|
|||||||
for(var occ = 0; occ<variable_ranges.length; occ++){
|
for(var occ = 0; occ<variable_ranges.length; occ++){
|
||||||
for(var occ1 = 0; occ1<variable_ranges[occ].length; occ1++){
|
for(var occ1 = 0; occ1<variable_ranges[occ].length; occ1++){
|
||||||
// console.log(variable_ranges[occ][occ1])
|
// console.log(variable_ranges[occ][occ1])
|
||||||
if(loc == variable_ranges[occ][occ1]){
|
if(loc === variable_ranges[occ][occ1]){
|
||||||
popup = true
|
popup = true
|
||||||
setCurrentLocation([line, dollar_occurences[occ]])
|
setCurrentLocation([line, dollar_occurences[occ]])
|
||||||
console.log("Current Location : "+dollar_occurences[occ])
|
console.log("Current Location : "+dollar_occurences[occ])
|
||||||
@@ -172,7 +173,7 @@ const CodeEditor = (props) => {
|
|||||||
// console.log()
|
// console.log()
|
||||||
|
|
||||||
// for (var j = 0; j < actionlist.length; j++) {
|
// for (var j = 0; j < actionlist.length; j++) {
|
||||||
// if(found[i].slice(1,).toLowerCase() == actionlist[j].autocomplete.toLowerCase()){
|
// if(found[i].slice(1,).toLowerCase() === actionlist[j].autocomplete.toLowerCase()){
|
||||||
// input = input.replace(found[i], JSON.stringify(actionlist[j].example));
|
// input = input.replace(found[i], JSON.stringify(actionlist[j].example));
|
||||||
// console.log(input)
|
// console.log(input)
|
||||||
// console.log(actionlist[j].example)
|
// console.log(actionlist[j].example)
|
||||||
@@ -182,7 +183,7 @@ const CodeEditor = (props) => {
|
|||||||
|
|
||||||
var dollar_occurence = []
|
var dollar_occurence = []
|
||||||
for(var ch=0; ch<current_code_line.length; ch++){
|
for(var ch=0; ch<current_code_line.length; ch++){
|
||||||
if(current_code_line[ch] == '$'){
|
if(current_code_line[ch] === '$'){
|
||||||
dollar_occurence.push(ch)
|
dollar_occurence.push(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,11 +201,11 @@ const CodeEditor = (props) => {
|
|||||||
for (var occ = 0; occ<variable_occurence.length; occ++){
|
for (var occ = 0; occ<variable_occurence.length; occ++){
|
||||||
// value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: #8b8e26; border-radius: 4px; color: white"})
|
// value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: #8b8e26; border-radius: 4px; color: white"})
|
||||||
var correctVariable = actionlist.find(action => action.autocomplete.toLowerCase() === variable_occurence[occ].slice(1,).toLowerCase())
|
var correctVariable = actionlist.find(action => action.autocomplete.toLowerCase() === variable_occurence[occ].slice(1,).toLowerCase())
|
||||||
if(correctVariable == undefined) {
|
if(correctVariable === undefined) {
|
||||||
value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: red; border-radius: 4px; color: white"})
|
value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: #f86a3e; padding-top: 2px; padding-bottom: 2px; color: white"})
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: #8b8e26; border-radius: 4px; color: white"})
|
value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: #8b8e26; padding-top: 2px; padding-bottom: 2px; color: white"})
|
||||||
}
|
}
|
||||||
// console.log(correctVariables)
|
// console.log(correctVariables)
|
||||||
}
|
}
|
||||||
@@ -237,7 +238,13 @@ const CodeEditor = (props) => {
|
|||||||
// console.log(currentLocation)
|
// console.log(currentLocation)
|
||||||
|
|
||||||
var code_lines = localcodedata.split('\n')
|
var code_lines = localcodedata.split('\n')
|
||||||
code_lines[currentLine] = code_lines[currentLine].slice(0,currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1]+currentVariable.length,)
|
var parsedVariable = currentVariable
|
||||||
|
if (currentVariable === undefined || currentVariable === null) {
|
||||||
|
console.log("Location: ", currentLocation)
|
||||||
|
parsedVariable= "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
code_lines[currentLine] = code_lines[currentLine].slice(0,currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1]+parsedVariable.length,)
|
||||||
// console.log(code_lines)
|
// console.log(code_lines)
|
||||||
var updatedCode = code_lines.join('\n')
|
var updatedCode = code_lines.join('\n')
|
||||||
// console.log(updatedCode)
|
// console.log(updatedCode)
|
||||||
@@ -255,7 +262,7 @@ const CodeEditor = (props) => {
|
|||||||
// console.log(found[i]);
|
// console.log(found[i]);
|
||||||
|
|
||||||
for (var j = 0; j < actionlist.length; j++) {
|
for (var j = 0; j < actionlist.length; j++) {
|
||||||
if(found[i].slice(1,).toLowerCase() == actionlist[j].autocomplete.toLowerCase()){
|
if(found[i].slice(1,).toLowerCase() === actionlist[j].autocomplete.toLowerCase()){
|
||||||
input = input.replace(found[i], JSON.stringify(actionlist[j].example));
|
input = input.replace(found[i], JSON.stringify(actionlist[j].example));
|
||||||
// console.log(input)
|
// console.log(input)
|
||||||
// console.log(actionlist[j].example)
|
// console.log(actionlist[j].example)
|
||||||
@@ -293,8 +300,8 @@ const CodeEditor = (props) => {
|
|||||||
style: {
|
style: {
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
minWidth: 600,
|
minWidth: isMobile ? "100%" : 600,
|
||||||
padding: 25,
|
padding: isMobile ? "25px 10px 25px 10px" : 25,
|
||||||
border: theme.palette.defaultBorder,
|
border: theme.palette.defaultBorder,
|
||||||
zIndex: 10012,
|
zIndex: 10012,
|
||||||
},
|
},
|
||||||
@@ -318,7 +325,7 @@ const CodeEditor = (props) => {
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<IconButton
|
<IconButton
|
||||||
style={{
|
style={{
|
||||||
marginLeft: 400,
|
marginLeft: isMobile ? "80%" : 400,
|
||||||
height: 50,
|
height: 50,
|
||||||
width: 50,
|
width: 50,
|
||||||
}}
|
}}
|
||||||
@@ -393,7 +400,7 @@ const CodeEditor = (props) => {
|
|||||||
style={{
|
style={{
|
||||||
margin: 10,
|
margin: 10,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
width: 250,
|
width: isMobile ? "100%" : 250,
|
||||||
// textOverflow: 'ellipsis'
|
// textOverflow: 'ellipsis'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -478,10 +485,10 @@ const CodeEditor = (props) => {
|
|||||||
Dark Theme
|
Dark Theme
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (codeTheme == "gruvbox-dark") {
|
if (codeTheme === "gruvbox-dark") {
|
||||||
setcodeTheme("duotone-light")
|
setcodeTheme("duotone-light")
|
||||||
}
|
}
|
||||||
if (codeTheme == "duotone-light"){
|
if (codeTheme === "duotone-light"){
|
||||||
setcodeTheme("gruvbox-dark")
|
setcodeTheme("gruvbox-dark")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -500,68 +507,70 @@ const CodeEditor = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<DialogTitle
|
{isMobile ? null :
|
||||||
style={{
|
<DialogTitle
|
||||||
paddingTop: 30,
|
|
||||||
paddingLeft: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style={{
|
style={{
|
||||||
color: "white"
|
paddingTop: 30,
|
||||||
|
paddingLeft: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Output
|
<span
|
||||||
</span>
|
style={{
|
||||||
</DialogTitle>
|
color: "white"
|
||||||
{validation === true ?
|
}}
|
||||||
<ReactJson
|
>
|
||||||
src={expOutput}
|
Output
|
||||||
theme={theme.palette.jsonTheme}
|
</span>
|
||||||
style={{
|
</DialogTitle>
|
||||||
borderRadius: 5,
|
}
|
||||||
border: `2px solid ${theme.palette.inputColor}`,
|
{isMobile ? null :
|
||||||
padding: 10,
|
validation === true ?
|
||||||
maxHeight: 250,
|
<ReactJson
|
||||||
minheight: 250,
|
src={expOutput}
|
||||||
overflow: "auto",
|
theme={theme.palette.jsonTheme}
|
||||||
}}
|
style={{
|
||||||
collapsed={false}
|
borderRadius: 5,
|
||||||
enableClipboard={(copy) => {
|
border: `2px solid ${theme.palette.inputColor}`,
|
||||||
//handleReactJsonClipboard(copy);
|
padding: 10,
|
||||||
}}
|
maxHeight: 250,
|
||||||
displayDataTypes={false}
|
minheight: 250,
|
||||||
onSelect={(select) => {
|
overflow: "auto",
|
||||||
//HandleJsonCopy(validate.result, select, "exec");
|
}}
|
||||||
}}
|
collapsed={false}
|
||||||
name={"JSON autocompletion"}
|
enableClipboard={(copy) => {
|
||||||
/>
|
//handleReactJsonClipboard(copy);
|
||||||
:
|
}}
|
||||||
<p
|
displayDataTypes={false}
|
||||||
id='expOutput'
|
onSelect={(select) => {
|
||||||
style={{
|
//HandleJsonCopy(validate.result, select, "exec");
|
||||||
whiteSpace: "pre-wrap",
|
}}
|
||||||
color: "#f85a3e",
|
name={"JSON autocompletion"}
|
||||||
fontFamily: "monospace",
|
/>
|
||||||
backgroundColor: "#282828",
|
:
|
||||||
padding: 20,
|
<p
|
||||||
marginTop: -2,
|
id='expOutput'
|
||||||
border: `2px solid ${theme.palette.inputColor}`,
|
style={{
|
||||||
borderRadius: theme.palette.borderRadius,
|
whiteSpace: "pre-wrap",
|
||||||
maxHeight: 250,
|
color: "#f85a3e",
|
||||||
overflow: "auto",
|
fontFamily: "monospace",
|
||||||
}}
|
backgroundColor: "#282828",
|
||||||
>
|
padding: 20,
|
||||||
{expOutput}
|
marginTop: -2,
|
||||||
</p>
|
border: `2px solid ${theme.palette.inputColor}`,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
maxHeight: 250,
|
||||||
|
overflow: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{expOutput}
|
||||||
|
</p>
|
||||||
}
|
}
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
color: "white",
|
color: "white",
|
||||||
fontFamily: "monospace",
|
fontFamily: "monospace",
|
||||||
padding: 20,
|
margin: 20,
|
||||||
paddingLeft: 10,
|
marginTop: 30,
|
||||||
marginTop: -15,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
JSON Validation: {validation ? "Correct" : "Incorrect"}
|
JSON Validation: {validation ? "Correct" : "Incorrect"}
|
||||||
@@ -573,48 +582,44 @@ const CodeEditor = (props) => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<button
|
||||||
<button
|
style={{
|
||||||
style={{
|
color: "white",
|
||||||
color: "white",
|
background: "#383b49",
|
||||||
background: "#383b49",
|
border: "none",
|
||||||
border: "none",
|
height: 35,
|
||||||
height: 35,
|
flex: 1,
|
||||||
width: 290,
|
marginLeft: 5,
|
||||||
marginLeft: 5,
|
marginTop: 20,
|
||||||
marginTop: 20,
|
cursor: "pointer"
|
||||||
cursor: "pointer"
|
}}
|
||||||
}}
|
onClick={() => {
|
||||||
onClick={() => {
|
setExpansionModalOpen(false)
|
||||||
setExpansionModalOpen(false)
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Cancel
|
||||||
Cancel
|
</button>
|
||||||
</button>
|
<button
|
||||||
</div>
|
style={{
|
||||||
<div>
|
color: "white",
|
||||||
<button
|
background: "#f85a3e",
|
||||||
style={{
|
border: "none",
|
||||||
color: "white",
|
height: 35,
|
||||||
background: "#f85a3e",
|
flex: 1,
|
||||||
border: "none",
|
marginLeft: 10,
|
||||||
height: 35,
|
marginTop: 20,
|
||||||
width: 290,
|
cursor: "pointer"
|
||||||
marginLeft: 10,
|
}}
|
||||||
marginTop: 20,
|
onClick={(event) => {
|
||||||
cursor: "pointer"
|
// console.log(codedata)
|
||||||
}}
|
// console.log(fieldCount)
|
||||||
onClick={(event) => {
|
changeActionParameterCodeMirror(event, fieldCount, localcodedata)
|
||||||
// console.log(codedata)
|
setExpansionModalOpen(false)
|
||||||
// console.log(fieldCount)
|
setcodedata(localcodedata)
|
||||||
changeActionParameterCodeMirror(event, fieldCount, localcodedata)
|
}}
|
||||||
setExpansionModalOpen(false)
|
>
|
||||||
setcodedata(localcodedata)
|
Done
|
||||||
}}
|
</button>
|
||||||
>
|
|
||||||
Done
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog>)
|
</Dialog>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ import {
|
|||||||
ExpandMore as ExpandMoreIcon,
|
ExpandMore as ExpandMoreIcon,
|
||||||
VpnKey as VpnKeyIcon,
|
VpnKey as VpnKeyIcon,
|
||||||
AddComment as AddCommentIcon,
|
AddComment as AddCommentIcon,
|
||||||
|
Edit as EditIcon,
|
||||||
} from "@material-ui/icons";
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -251,6 +252,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
const [triggerFolders, setTriggerFolders] = React.useState([]);
|
const [triggerFolders, setTriggerFolders] = React.useState([]);
|
||||||
const [workflows, setWorkflows] = React.useState([]);
|
const [workflows, setWorkflows] = React.useState([]);
|
||||||
const [showEnvironment, setShowEnvironment] = React.useState(false);
|
const [showEnvironment, setShowEnvironment] = React.useState(false);
|
||||||
|
const [editWorkflowDetails, setEditWorkflowDetails] = React.useState(false);
|
||||||
|
|
||||||
const [workflow, setWorkflow] = React.useState({});
|
const [workflow, setWorkflow] = React.useState({});
|
||||||
const [userSettings, setUserSettings] = React.useState({});
|
const [userSettings, setUserSettings] = React.useState({});
|
||||||
@@ -1257,98 +1259,100 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const executeWorkflow = (executionArgument, startNode, hasSaved) => {
|
const executeWorkflow = (executionArgument, startNode, hasSaved) => {
|
||||||
if (hasSaved === false) {
|
ReactDOM.unstable_batchedUpdates(() => {
|
||||||
setExecutionRequestStarted(true);
|
if (hasSaved === false) {
|
||||||
saveWorkflow(workflow, executionArgument, startNode);
|
setExecutionRequestStarted(true);
|
||||||
console.log("FIXME: Might have forgotten to save before executing.");
|
saveWorkflow(workflow, executionArgument, startNode);
|
||||||
return;
|
console.log("FIXME: Might have forgotten to save before executing.");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (workflow.public) {
|
if (workflow.public) {
|
||||||
alert.info("Save it to get a new version");
|
alert.info("Save it to get a new version");
|
||||||
}
|
}
|
||||||
|
|
||||||
var returncheck = monitorUpdates();
|
var returncheck = monitorUpdates();
|
||||||
if (!returncheck) {
|
if (!returncheck) {
|
||||||
alert.error("No startnode set.");
|
alert.error("No startnode set.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisited([]);
|
setVisited([]);
|
||||||
setExecutionRequest({});
|
setExecutionRequest({});
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
var curelements = cy.elements();
|
var curelements = cy.elements();
|
||||||
for (var i = 0; i < curelements.length; i++) {
|
for (var i = 0; i < curelements.length; i++) {
|
||||||
curelements[i].addClass("not-executing-highlight");
|
curelements[i].addClass("not-executing-highlight");
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = { execution_argument: executionArgument, start: startNode };
|
const data = { execution_argument: executionArgument, start: startNode };
|
||||||
fetch(
|
fetch(
|
||||||
globalUrl + "/api/v1/workflows/" + props.match.params.key + "/execute",
|
globalUrl + "/api/v1/workflows/" + props.match.params.key + "/execute",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (!responseJson.success) {
|
if (!responseJson.success) {
|
||||||
alert.error("Failed to start: " + responseJson.reason);
|
alert.error("Failed to start: " + responseJson.reason);
|
||||||
setExecutionRunning(false);
|
setExecutionRunning(false);
|
||||||
setExecutionRequestStarted(false);
|
setExecutionRequestStarted(false);
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
for (var i = 0; i < curelements.length; i++) {
|
for (var i = 0; i < curelements.length; i++) {
|
||||||
curelements[i].removeClass("not-executing-highlight");
|
curelements[i].removeClass("not-executing-highlight");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
setExecutionRunning(true);
|
setExecutionRunning(true);
|
||||||
setExecutionRequestStarted(false);
|
setExecutionRequestStarted(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
responseJson.execution_id === "" ||
|
responseJson.execution_id === "" ||
|
||||||
responseJson.execution_id === undefined ||
|
responseJson.execution_id === undefined ||
|
||||||
responseJson.authorization === "" ||
|
responseJson.authorization === "" ||
|
||||||
responseJson.authorization === undefined
|
responseJson.authorization === undefined
|
||||||
) {
|
) {
|
||||||
alert.error("Something went wrong during execution startup");
|
alert.error("Something went wrong during execution startup");
|
||||||
console.log("BAD RESPONSE FOR EXECUTION: ", responseJson);
|
console.log("BAD RESPONSE FOR EXECUTION: ", responseJson);
|
||||||
setExecutionRunning(false);
|
setExecutionRunning(false);
|
||||||
setExecutionRequestStarted(false);
|
setExecutionRequestStarted(false);
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
for (var i = 0; i < curelements.length; i++) {
|
for (var i = 0; i < curelements.length; i++) {
|
||||||
curelements[i].removeClass("not-executing-highlight");
|
curelements[i].removeClass("not-executing-highlight");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setExecutionRequest({
|
setExecutionRequest({
|
||||||
execution_id: responseJson.execution_id,
|
execution_id: responseJson.execution_id,
|
||||||
authorization: responseJson.authorization,
|
authorization: responseJson.authorization,
|
||||||
});
|
});
|
||||||
setExecutionData({});
|
setExecutionData({});
|
||||||
setExecutionModalOpen(true);
|
setExecutionModalOpen(true);
|
||||||
setExecutionModalView(1);
|
setExecutionModalView(1);
|
||||||
start();
|
start();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert.error(error.toString());
|
alert.error(error.toString());
|
||||||
});
|
});
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// This can be used to only show prioritzed ones later
|
// This can be used to only show prioritzed ones later
|
||||||
@@ -1542,6 +1546,35 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Searhc by username, userId, workflow, appId should all work
|
||||||
|
const getUserProfile = (username) => {
|
||||||
|
fetch(`${globalUrl}/api/v1/users/creators/${username}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success !== false) {
|
||||||
|
console.log("Found creator: ", responseJson)
|
||||||
|
setCreatorProfile(responseJson)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getWorkflow = (workflow_id, sourcenode) => {
|
const getWorkflow = (workflow_id, sourcenode) => {
|
||||||
console.log(
|
console.log(
|
||||||
//`Getting workflow ${workflow_id} with append value ${sourcenode}`
|
//`Getting workflow ${workflow_id} with append value ${sourcenode}`
|
||||||
@@ -1582,9 +1615,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (responseJson.public) {
|
if (responseJson.public) {
|
||||||
alert.info(
|
alert.info("This workflow is public. Save the workflow to start using it.");
|
||||||
"This workflow is public. Save the workflow to "
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("RESP: ", responseJson)
|
console.log("RESP: ", responseJson)
|
||||||
if (Object.getOwnPropertyNames(creatorProfile).length === 0) {
|
if (Object.getOwnPropertyNames(creatorProfile).length === 0) {
|
||||||
@@ -8686,13 +8717,13 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
workflow.triggers[selectedTriggerIndex].parameters[2] = {"name": "auth_headers", "value": ""}
|
workflow.triggers[selectedTriggerIndex].parameters[2] = {"name": "auth_headers", "value": ""}
|
||||||
setWorkflow(workflow)
|
setWorkflow(workflow)
|
||||||
} else {
|
} else {
|
||||||
// Always update
|
if (selectedTrigger.environment !== "cloud") {
|
||||||
const newUrl = referenceUrl+"webhook_"+selectedTrigger.id
|
const newUrl = referenceUrl+"webhook_"+selectedTrigger.id
|
||||||
//console.log("Validating webhook url: ", newUrl)
|
if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) {
|
||||||
if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) {
|
console.log("Url is wrong - should update. This functionality is temporarily disabled.")
|
||||||
console.log("Url is wrong - updating")
|
//workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl
|
//setWorkflow(workflow)
|
||||||
setWorkflow(workflow)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8898,13 +8929,13 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
// Always update
|
// Always update
|
||||||
const newUrl = referenceUrl + "webhook_" + selectedTrigger.id;
|
const newUrl = referenceUrl + "webhook_" + selectedTrigger.id;
|
||||||
//console.log("Validating webhook url: ", newUrl);
|
//console.log("Validating webhook url: ", newUrl);
|
||||||
if (
|
if (selectedTrigger.environment !== "cloud") {
|
||||||
newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value
|
if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) {
|
||||||
) {
|
console.log("Url is wrong. NOT updating because of hybrid.");
|
||||||
console.log("Url is wrong - updating");
|
//workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl;
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl;
|
//setWorkflow(workflow);
|
||||||
setWorkflow(workflow);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const trigger_header_auth =
|
const trigger_header_auth =
|
||||||
@@ -8976,28 +9007,21 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
selectedTrigger.environment = e.target.value;
|
selectedTrigger.environment = e.target.value;
|
||||||
setSelectedTrigger(selectedTrigger);
|
|
||||||
if (e.target.value === "cloud") {
|
if (e.target.value === "cloud") {
|
||||||
const tmpvalue =
|
const tmpvalue = workflow.triggers[selectedTriggerIndex].parameters[0].value.split("/");
|
||||||
workflow.triggers[
|
|
||||||
selectedTriggerIndex
|
|
||||||
].parameters[0].value.split("/");
|
|
||||||
const urlpath = tmpvalue.slice(3, tmpvalue.length);
|
const urlpath = tmpvalue.slice(3, tmpvalue.length);
|
||||||
const newurl = "https://shuffler.io/" + urlpath.join("/");
|
const newurl = "https://shuffler.io/" + urlpath.join("/");
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0].value =
|
workflow.triggers[selectedTriggerIndex].parameters[0].value = newurl;
|
||||||
newurl;
|
|
||||||
} else {
|
} else {
|
||||||
const tmpvalue =
|
const tmpvalue = workflow.triggers[selectedTriggerIndex].parameters[0].value.split("/");
|
||||||
workflow.triggers[
|
|
||||||
selectedTriggerIndex
|
|
||||||
].parameters[0].value.split("/");
|
|
||||||
const urlpath = tmpvalue.slice(3, tmpvalue.length);
|
const urlpath = tmpvalue.slice(3, tmpvalue.length);
|
||||||
const newurl =
|
const newurl = window.location.origin + "/" + urlpath.join("/");
|
||||||
window.location.origin + "/" + urlpath.join("/");
|
workflow.triggers[selectedTriggerIndex].parameters[0].value = newurl;
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0].value =
|
|
||||||
newurl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("New value: ", workflow.triggers[selectedTriggerIndex].parameters[0])
|
||||||
|
selectedTrigger.parameters[0] = workflow.triggers[selectedTriggerIndex].parameters[0]
|
||||||
|
setSelectedTrigger(selectedTrigger);
|
||||||
setWorkflow(workflow);
|
setWorkflow(workflow);
|
||||||
setUpdate(Math.random());
|
setUpdate(Math.random());
|
||||||
}}
|
}}
|
||||||
@@ -9086,10 +9110,8 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
helperText={
|
helperText={
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0]
|
workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined &&
|
||||||
.value !== undefined &&
|
workflow.triggers[selectedTriggerIndex].parameters[0].value !== null &&
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0]
|
|
||||||
.value !== null &&
|
|
||||||
(workflow.triggers[
|
(workflow.triggers[
|
||||||
selectedTriggerIndex
|
selectedTriggerIndex
|
||||||
].parameters[0].value.includes("localhost") ||
|
].parameters[0].value.includes("localhost") ||
|
||||||
@@ -9115,7 +9137,7 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled
|
disabled
|
||||||
defaultValue={
|
value={
|
||||||
workflow.triggers[selectedTriggerIndex].parameters[0].value
|
workflow.triggers[selectedTriggerIndex].parameters[0].value
|
||||||
}
|
}
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -10515,31 +10537,33 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip color="primary" title="Save (ctrl+s)" placement="top">
|
{userdata.avatar === creatorProfile.github_avatar ? null :
|
||||||
<span>
|
<Tooltip color="primary" title="Save (ctrl+s)" placement="top">
|
||||||
<Button
|
<span>
|
||||||
disabled={savingState !== 0}
|
<Button
|
||||||
color="primary"
|
disabled={savingState !== 0}
|
||||||
style={{
|
color="primary"
|
||||||
height: workflow.public ? 100 : 50,
|
style={{
|
||||||
width: workflow.public ? 100 : 64,
|
height: workflow.public ? 100 : 50,
|
||||||
marginLeft: 10,
|
width: workflow.public ? 100 : 64,
|
||||||
}}
|
marginLeft: 10,
|
||||||
variant={
|
}}
|
||||||
lastSaved && !workflow.public ? "outlined" : "contained"
|
variant={
|
||||||
}
|
lastSaved && !workflow.public ? "outlined" : "contained"
|
||||||
onClick={() => saveWorkflow()}
|
}
|
||||||
>
|
onClick={() => saveWorkflow()}
|
||||||
{savingState === 2 ? (
|
>
|
||||||
<CircularProgress style={{ height: 35, width: 35 }} />
|
{savingState === 2 ? (
|
||||||
) : savingState === 1 ? (
|
<CircularProgress style={{ height: 35, width: 35 }} />
|
||||||
<DoneIcon style={{ color: green }} />
|
) : savingState === 1 ? (
|
||||||
) : (
|
<DoneIcon style={{ color: green }} />
|
||||||
<SaveIcon />
|
) : (
|
||||||
)}
|
<SaveIcon />
|
||||||
</Button>
|
)}
|
||||||
</span>
|
</Button>
|
||||||
</Tooltip>
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
{workflow.public ? (
|
{workflow.public ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -10774,6 +10798,8 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
globalUrl={globalUrl}
|
globalUrl={globalUrl}
|
||||||
setSelectedActionEnvironment={setSelectedActionEnvironment}
|
setSelectedActionEnvironment={setSelectedActionEnvironment}
|
||||||
requiresAuthentication={requiresAuthentication}
|
requiresAuthentication={requiresAuthentication}
|
||||||
|
setLastSaved={setLastSaved}
|
||||||
|
lastSaved={lastSaved}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
} else if (Object.getOwnPropertyNames(selectedComment).length > 0) {
|
} else if (Object.getOwnPropertyNames(selectedComment).length > 0) {
|
||||||
@@ -10849,34 +10875,6 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
//}}>Execute websocket</Button>
|
//}}>Execute websocket</Button>
|
||||||
//
|
//
|
||||||
|
|
||||||
// Searhc by username, userId, workflow, appId should all work
|
|
||||||
const getUserProfile = (username) => {
|
|
||||||
fetch(`${globalUrl}/api/v1/users/creators/${username}`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Accept: "application/json",
|
|
||||||
},
|
|
||||||
credentials: "include",
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((responseJson) => {
|
|
||||||
console.log("Found creator: ", responseJson)
|
|
||||||
if (responseJson.success !== false) {
|
|
||||||
setCreatorProfile(responseJson)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const leftView = workflow.public === true ?
|
const leftView = workflow.public === true ?
|
||||||
<div style={{minHeight: "80vh", height: "100%", minWidth: leftBarSize-70, maxWidth: leftBarSize-70, zIndex: 0, padding: 35, borderRight: "1px solid rgba(91,96,100,1)",}}>
|
<div style={{minHeight: "80vh", height: "100%", minWidth: leftBarSize-70, maxWidth: leftBarSize-70, zIndex: 0, padding: 35, borderRight: "1px solid rgba(91,96,100,1)",}}>
|
||||||
@@ -10929,14 +10927,6 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
: null }
|
: null }
|
||||||
<div style={{display: "flex", marginTop: 10, }}>
|
|
||||||
<Typography variant="body1">
|
|
||||||
Mitre Att&ck:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1" color="textSecondary">
|
|
||||||
TBD
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
{appGroup.length > 0 ?
|
{appGroup.length > 0 ?
|
||||||
<div style={{display: "flex", marginTop: 10, }}>
|
<div style={{display: "flex", marginTop: 10, }}>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
@@ -10967,6 +10957,15 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
</AvatarGroup>
|
</AvatarGroup>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
|
<div style={{display: "flex", marginTop: 10, }}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Mitre Att&ck:
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" color="textSecondary">
|
||||||
|
TBD
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
<div style={{display: "flex", marginTop: 10, }}>
|
<div style={{display: "flex", marginTop: 10, }}>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
Related Workflows:
|
Related Workflows:
|
||||||
@@ -10985,6 +10984,19 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
{userdata.avatar === creatorProfile.github_avatar ?
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
style={{marginTop: 15, }}
|
||||||
|
onClick={() => {
|
||||||
|
setEditWorkflowDetails(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit Details
|
||||||
|
</Button>
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
: isMobile && leftViewOpen ?
|
: isMobile && leftViewOpen ?
|
||||||
<div
|
<div
|
||||||
@@ -12311,23 +12323,35 @@ const parsedExecutionArgument = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{maxWidth: 600, overflowX: "hidden", }}>
|
<div style={{maxWidth: 600, overflowX: "hidden", }}>
|
||||||
<IconButton
|
{data.value.length > 60 ?
|
||||||
style={{
|
<IconButton
|
||||||
marginBottom: 0, marginTop: 5, cursor: "pointer",
|
style={{
|
||||||
}}
|
marginBottom: 0, marginTop: 5, cursor: "pointer",
|
||||||
onClick={() => {
|
padding: 3,
|
||||||
if (!showVariable) {
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
setOpen(!open)
|
borderRadius: theme.palette.borderRadius,
|
||||||
}
|
}}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
if (!showVariable) {
|
||||||
|
setOpen(!open)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
style={{}}
|
||||||
|
>
|
||||||
|
<b>{data.name}</b> {showVariable ? data.value : null}
|
||||||
|
</Typography>
|
||||||
|
</IconButton>
|
||||||
|
:
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body1"
|
||||||
style={{}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<b>{data.name}</b>: {showVariable ? data.value : null}
|
<b>{data.name}</b>: {showVariable ? data.value : null}
|
||||||
</Typography>
|
</Typography>
|
||||||
</IconButton>
|
}
|
||||||
{open ?
|
{open ?
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
@@ -12697,6 +12721,32 @@ const parsedExecutionArgument = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const editWorkflowModal =
|
||||||
|
<Dialog
|
||||||
|
open={editWorkflowDetails}
|
||||||
|
PaperComponent={PaperComponent}
|
||||||
|
hideBackdrop={true}
|
||||||
|
disableEnforceFocus={true}
|
||||||
|
disableBackdropClick={true}
|
||||||
|
style={{ pointerEvents: "none" }}
|
||||||
|
aria-labelledby="draggable-dialog-title"
|
||||||
|
onClose={() => {
|
||||||
|
setEditWorkflowDetails(false)
|
||||||
|
}}
|
||||||
|
PaperProps={{
|
||||||
|
style: {
|
||||||
|
pointerEvents: "auto",
|
||||||
|
backgroundColor: surfaceColor,
|
||||||
|
color: "white",
|
||||||
|
border: theme.palette.defaultBorder,
|
||||||
|
maxWidth: "100%",
|
||||||
|
padding: 50,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit workflow!
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
const ExecutionVariableModal = (props) => {
|
const ExecutionVariableModal = (props) => {
|
||||||
const { variableInfo } = props
|
const { variableInfo } = props
|
||||||
|
|
||||||
@@ -13604,6 +13654,7 @@ const parsedExecutionArgument = () => {
|
|||||||
{authenticationModal}
|
{authenticationModal}
|
||||||
{codePopoutModal}
|
{codePopoutModal}
|
||||||
{configureWorkflowModal}
|
{configureWorkflowModal}
|
||||||
|
{editWorkflowModal}
|
||||||
<TextField
|
<TextField
|
||||||
id="copy_element_shuffle"
|
id="copy_element_shuffle"
|
||||||
value={to_be_copied}
|
value={to_be_copied}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import DetectionFramework from "../components/DetectionFramework.jsx";
|
|||||||
// nodejs library that concatenates classes
|
// nodejs library that concatenates classes
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
|
|
||||||
// react plugin used to create charts
|
// react plugin used to create charts
|
||||||
//import { Line, Bar } from "react-chartjs-2";
|
//import { Line, Bar } from "react-chartjs-2";
|
||||||
@@ -11,7 +12,9 @@ import { useAlert } from "react-alert";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
TextField,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Button,
|
||||||
Typography,
|
Typography,
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
Paper,
|
||||||
@@ -23,6 +26,7 @@ import {
|
|||||||
DoneAll as DoneAllIcon,
|
DoneAll as DoneAllIcon,
|
||||||
Description as DescriptionIcon,
|
Description as DescriptionIcon,
|
||||||
PlayArrow as PlayArrowIcon,
|
PlayArrow as PlayArrowIcon,
|
||||||
|
Edit as EditIcon,
|
||||||
} from "@material-ui/icons";
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
||||||
@@ -53,13 +57,114 @@ import {
|
|||||||
TreeMapRect,
|
TreeMapRect,
|
||||||
} from 'reaviz';
|
} from 'reaviz';
|
||||||
|
|
||||||
const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn}) => {
|
||||||
const [expandedIndex, setExpandedIndex] = useState(-1);
|
const [expandedIndex, setExpandedIndex] = useState(-1);
|
||||||
const [expandedItem, setExpandedItem] = useState(-1);
|
const [expandedItem, setExpandedItem] = useState(-1);
|
||||||
|
const [inputUsecase, setInputUsecase] = useState({});
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
|
const [video, setVideo] = useState("");
|
||||||
|
const [blogpost, setBlogpost] = useState("");
|
||||||
|
|
||||||
|
const [mitreTags, setMitreTags] = useState([]);
|
||||||
|
let navigate = useNavigate();
|
||||||
if (keys === undefined || keys === null || keys.length === 0) {
|
if (keys === undefined || keys === null || keys.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getUsecase = (name, index, subindex) => {
|
||||||
|
fetch(globalUrl + "/api/v1/workflows/usecases/"+name.replaceAll(" ", "_"), {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for framework!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
setInputUsecase({
|
||||||
|
"name": name,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setInputUsecase(responseJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
setExpandedIndex(index)
|
||||||
|
setExpandedItem(subindex)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
//alert.error(error.toString());
|
||||||
|
setInputUsecase({})
|
||||||
|
setExpandedIndex(index)
|
||||||
|
setExpandedItem(subindex)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setUsecaseItem = (inputUsecase) => {
|
||||||
|
var parsedUsecase = inputUsecase
|
||||||
|
|
||||||
|
if (blogpost !== inputUsecase.blogpost) {
|
||||||
|
inputUsecase.blogpost = blogpost
|
||||||
|
parsedUsecase.blogpost = blogpost
|
||||||
|
}
|
||||||
|
|
||||||
|
if (video !== inputUsecase.video) {
|
||||||
|
inputUsecase.video = video
|
||||||
|
parsedUsecase.video = video
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description !== inputUsecase.description) {
|
||||||
|
inputUsecase.description = description
|
||||||
|
parsedUsecase.description = description
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mitreTags !== inputUsecase.mitre) {
|
||||||
|
inputUsecase.mitre = mitreTags
|
||||||
|
parsedUsecase.mitre = mitreTags
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(parsedUsecase),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for framework!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
if (responseJson.reason !== undefined) {
|
||||||
|
//alert.error("Failed updating: " + responseJson.reason)
|
||||||
|
} else {
|
||||||
|
//alert.error("Failed to update framework for your org.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//alert.info("Updated usecase.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
//alert.error(error.toString());
|
||||||
|
//setFrameworkLoaded(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{marginTop: 25, minHeight: 1000,}}>
|
<div style={{marginTop: 25, minHeight: 1000,}}>
|
||||||
<Typography variant="h1">
|
<Typography variant="h1">
|
||||||
@@ -81,6 +186,23 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectedItem = subindex === expandedItem && index === expandedIndex
|
const selectedItem = subindex === expandedItem && index === expandedIndex
|
||||||
|
if (selectedItem && subcase.name !== undefined && inputUsecase.name !== undefined) {
|
||||||
|
if (subcase.name.toLowerCase().replaceAll(" ", "_") === inputUsecase.name.toLowerCase().replaceAll(" ", "_")) {
|
||||||
|
console.log("Input: ", inputUsecase)
|
||||||
|
if (inputUsecase.description !== undefined && inputUsecase.description !== null) {
|
||||||
|
subcase.description = inputUsecase.description
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputUsecase.blogpost !== undefined && inputUsecase.blogpost !== null) {
|
||||||
|
subcase.blogpost = inputUsecase.blogpost
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputUsecase.video !== undefined && inputUsecase.video !== null) {
|
||||||
|
subcase.video = inputUsecase.video
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const finished = subcase.matches.length > 0
|
const finished = subcase.matches.length > 0
|
||||||
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
|
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
|
||||||
const backgroundColor = "inherit"
|
const backgroundColor = "inherit"
|
||||||
@@ -90,14 +212,12 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
|||||||
<Grid item xs={selectedItem ? 12 : 4} key={subindex} style={{minHeight: 110,}} onClick={() => {
|
<Grid item xs={selectedItem ? 12 : 4} key={subindex} style={{minHeight: 110,}} onClick={() => {
|
||||||
if (selectedItem) {
|
if (selectedItem) {
|
||||||
} else {
|
} else {
|
||||||
if (subcase.description !== undefined && subcase.description !== null && subcase.description.length > 0) {
|
//if (subcase.description !== undefined && subcase.description !== null && subcase.description.length > 0) {
|
||||||
setExpandedIndex(index)
|
getUsecase(subcase.name, index, subindex)
|
||||||
setExpandedItem(subindex)
|
//}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Paper style={{padding: "30px 30px 30px 30px", minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
<Paper style={{padding: "30px 30px 30px 30px", minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||||
console.log("Clicked: ", subcase)
|
|
||||||
}}>
|
}}>
|
||||||
{!selectedItem ?
|
{!selectedItem ?
|
||||||
<div style={{textAlign: "left", position: "relative",}}>
|
<div style={{textAlign: "left", position: "relative",}}>
|
||||||
@@ -131,7 +251,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
|||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
style={{paddingTop: 5, }}
|
style={{marginTop: finished ? 5 : 40, }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -168,10 +288,37 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
|||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
<b>{subcase.name}</b>
|
<b>{subcase.name}</b>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
|
||||||
Description: {subcase.description}
|
|
||||||
</Typography>
|
|
||||||
<div style={{ position: "absolute", top: 0, right: 0 }}>
|
<div style={{ position: "absolute", top: 0, right: 0 }}>
|
||||||
|
{isLoggedIn === true ?
|
||||||
|
<Tooltip
|
||||||
|
title="Click to edit"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
style={{paddingTop: 5, }}
|
||||||
|
onClick={(e) => {
|
||||||
|
setEditing(true)
|
||||||
|
if (subcase.description !== undefined && subcase.description !== null) {
|
||||||
|
setDescription(subcase.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcase.blogpost !== undefined && subcase.blogpost !== null) {
|
||||||
|
setBlogpost(subcase.blogpost)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcase.video !== undefined && subcase.video !== null) {
|
||||||
|
setVideo(subcase.video)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcase.mitre !== undefined && subcase.mitre !== null) {
|
||||||
|
setMitreTags(subcase.mitre)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditIcon style={{ color: usecase.color }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
: null}
|
||||||
{subcase.blogpost !== null && subcase.blogpost !== undefined && subcase.blogpost.length > 0 ?
|
{subcase.blogpost !== null && subcase.blogpost !== undefined && subcase.blogpost.length > 0 ?
|
||||||
<a
|
<a
|
||||||
href={subcase.blogpost}
|
href={subcase.blogpost}
|
||||||
@@ -224,6 +371,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setExpandedItem(-1)
|
setExpandedItem(-1)
|
||||||
setExpandedIndex(-1)
|
setExpandedIndex(-1)
|
||||||
|
setEditing(false)
|
||||||
|
setInputUsecase({})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CloseIcon style={{ color: "white" }} />
|
<CloseIcon style={{ color: "white" }} />
|
||||||
@@ -231,65 +380,168 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div style={{marginTop: 25, display: "flex", minHeight: 400, maxHeight: 400, }}>
|
<div style={{marginTop: 25, display: "flex", minHeight: 400, maxHeight: 400, }}>
|
||||||
{/*
|
{editing ?
|
||||||
<img
|
<div style={{flex: 1, marginRight: 50, }}>
|
||||||
alt={subcase.name}
|
<Typography variant="h6">
|
||||||
src={"/images/detectionframework.png"}
|
Editing!
|
||||||
style={{
|
</Typography>
|
||||||
flex: 1,
|
<TextField
|
||||||
height: 400,
|
style={{
|
||||||
|
marginTop: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
color="primary"
|
||||||
|
label="Description"
|
||||||
|
placeholder={"Description"}
|
||||||
|
value={description}
|
||||||
|
onChange={(event) => {
|
||||||
|
setDescription(event.target.value)
|
||||||
|
}}
|
||||||
|
id="descriptionEditng"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
style={{
|
||||||
|
marginTop: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
color="primary"
|
||||||
|
label="Blogpost"
|
||||||
|
placeholder={"Blogpost"}
|
||||||
|
value={blogpost}
|
||||||
|
onChange={(event) => {
|
||||||
|
setBlogpost(event.target.value)
|
||||||
|
}}
|
||||||
|
id="blogpostEditing"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
style={{
|
||||||
|
marginTop: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
color="primary"
|
||||||
|
label="Video"
|
||||||
|
placeholder={"Video"}
|
||||||
|
value={video}
|
||||||
|
onChange={(event) => {
|
||||||
|
setVideo(event.target.value)
|
||||||
|
}}
|
||||||
|
id="videoEditing"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
color: "white",
|
||||||
|
background: "#383b49",
|
||||||
|
border: "none",
|
||||||
|
height: 35,
|
||||||
|
flex: 1,
|
||||||
|
marginLeft: 5,
|
||||||
|
marginTop: 20,
|
||||||
|
cursor: "pointer"
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setDescription("")
|
||||||
|
setVideo("")
|
||||||
|
setBlogpost("")
|
||||||
|
setEditing(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
color: "white",
|
||||||
|
background: "#f85a3e",
|
||||||
|
border: "none",
|
||||||
|
height: 35,
|
||||||
|
flex: 1,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginTop: 20,
|
||||||
|
cursor: "pointer"
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
setEditing(false)
|
||||||
|
setUsecaseItem(inputUsecase)
|
||||||
|
setDescription("")
|
||||||
|
setVideo("")
|
||||||
|
setBlogpost("")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div style={{flex: 1, textAlign: "left",}}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{subcase.description}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h6" style={{marginTop: 15, }}>
|
||||||
|
Your workflow{subcase.matches.length === 1 ? "" : "s"} ({subcase.matches.length})
|
||||||
|
</Typography>
|
||||||
|
{subcase.matches.length > 0 ?
|
||||||
|
<Grid container xs={3} style={{maxWidth: 325, marginTop: 10, }}>
|
||||||
|
{subcase.matches.map((workflow, workflowindex) => {
|
||||||
|
return (
|
||||||
|
<Grid key={workflowindex} item index={workflowindex} xs={12}>
|
||||||
|
<WorkflowPaper key={workflowindex} data={workflow} />
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<Typography variant="body1" color="textSecondary">
|
||||||
|
No workflow selected yet.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{isCloud !== false ?
|
||||||
|
<div>
|
||||||
|
<Typography variant="h6" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {
|
||||||
|
navigate("/search?tab=workflows&q="+subcase.name)
|
||||||
|
}}>
|
||||||
|
Public workflows
|
||||||
|
</Typography>
|
||||||
|
{/*
|
||||||
|
<div>
|
||||||
|
<Typography variant="body1" color="textSecondary">
|
||||||
|
No workflows yet.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
*/}
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div style={{
|
||||||
|
height: 400,
|
||||||
width: 400,
|
width: 400,
|
||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
border: "1px solid rgba(255,255,255,0.3)",
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
}}
|
}}>
|
||||||
/>
|
<DetectionFramework
|
||||||
*/}
|
inputUsecase={inputUsecase}
|
||||||
|
frameworkData={frameworkData}
|
||||||
<div style={{
|
selectedOption={"Draw"}
|
||||||
height: 400,
|
showOptions={false}
|
||||||
width: 400,
|
isLoaded={true}
|
||||||
borderRadius: theme.palette.borderRadius,
|
isLoggedIn={true}
|
||||||
border: "1px solid rgba(255,255,255,0.3)",
|
globalUrl={globalUrl}
|
||||||
}}>
|
size={0.7}
|
||||||
<DetectionFramework
|
/>
|
||||||
frameworkData={undefined}
|
</div>
|
||||||
selectedOption={"Draw"}
|
|
||||||
showOptions={false}
|
|
||||||
isLoaded={true}
|
|
||||||
isLoggedIn={true}
|
|
||||||
globalUrl={globalUrl}
|
|
||||||
size={0.7}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{flex: 1, marginLeft: 10, textAlign: "center",}}>
|
|
||||||
<Typography variant="h6">
|
|
||||||
Your workflow{subcase.matches.length === 1 ? "" : "s"} ({subcase.matches.length})
|
|
||||||
</Typography>
|
|
||||||
{subcase.matches.length > 0 ?
|
|
||||||
<Grid container xs={3} style={{maxWidth: 325, margin: "auto", marginTop: 10, itemAlign: "center", }}>
|
|
||||||
{subcase.matches.map((workflow, workflowindex) => {
|
|
||||||
return (
|
|
||||||
<Grid key={workflowindex} item index={workflowindex} xs={12}>
|
|
||||||
<WorkflowPaper key={workflowindex} data={workflow} />
|
|
||||||
</Grid>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Grid>
|
|
||||||
:
|
|
||||||
<div>
|
|
||||||
<Typography variant="body1" color="textSecondary">
|
|
||||||
No workflow selected yet.
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{isCloud !== false ?
|
|
||||||
<Typography variant="h6">
|
|
||||||
Public workflows
|
|
||||||
</Typography>
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -449,11 +701,44 @@ const Dashboard = (props) => {
|
|||||||
const [selectedUsecases, setSelectedUsecases] = useState([]);
|
const [selectedUsecases, setSelectedUsecases] = useState([]);
|
||||||
const [usecases, setUsecases] = useState([]);
|
const [usecases, setUsecases] = useState([]);
|
||||||
const [workflows, setWorkflows] = useState([]);
|
const [workflows, setWorkflows] = useState([]);
|
||||||
|
const [frameworkData, setFrameworkData] = useState(undefined);
|
||||||
|
|
||||||
const isCloud =
|
const isCloud =
|
||||||
window.location.host === "localhost:3002" ||
|
window.location.host === "localhost:3002" ||
|
||||||
window.location.host === "shuffler.io";
|
window.location.host === "shuffler.io";
|
||||||
|
|
||||||
|
const getFramework = () => {
|
||||||
|
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for framework!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
if (responseJson.reason !== undefined) {
|
||||||
|
//alert.error("Failed loading: " + responseJson.reason)
|
||||||
|
} else {
|
||||||
|
//alert.error("Failed to load framework for your org.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFrameworkData(responseJson)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
alert.error(error.toString());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getAvailableWorkflows = () => {
|
const getAvailableWorkflows = () => {
|
||||||
fetch(globalUrl + "/api/v1/workflows", {
|
fetch(globalUrl + "/api/v1/workflows", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -605,6 +890,7 @@ const Dashboard = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAvailableWorkflows()
|
getAvailableWorkflows()
|
||||||
|
getFramework()
|
||||||
//fetchUsecases()
|
//fetchUsecases()
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -865,7 +1151,7 @@ const Dashboard = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||||
<div style={{ display: "flex", marginLeft: 120,}}>
|
<div style={{ display: "flex", marginLeft: 180, }}>
|
||||||
{usecases.map((usecase, index) => {
|
{usecases.map((usecase, index) => {
|
||||||
return (
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
@@ -898,7 +1184,13 @@ const Dashboard = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
<UsecaseListComponent keys={selectedUsecases} isCloud={isCloud} globalUrl={globalUrl} />
|
<UsecaseListComponent
|
||||||
|
isLoggedIn={isLoggedIn}
|
||||||
|
frameworkData={frameworkData}
|
||||||
|
keys={selectedUsecases}
|
||||||
|
isCloud={isCloud}
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
/>
|
||||||
|
|
||||||
{treeKeys.length > 0 ?
|
{treeKeys.length > 0 ?
|
||||||
<TreeChart keys={treeKeys} />
|
<TreeChart keys={treeKeys} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import ReactDOM from "react-dom"
|
||||||
|
|
||||||
import DetectionFramework from "../components/DetectionFramework.jsx";
|
import DetectionFramework from "../components/DetectionFramework.jsx";
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
@@ -39,14 +40,18 @@ const Framework = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
alert.error("Failed to load framework for your org.")
|
alert.error("Failed to load framework for your org.")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setFrameworkLoaded(true)
|
setFrameworkLoaded(true)
|
||||||
setFrameworkData(responseJson)
|
} else {
|
||||||
|
ReactDOM.unstable_batchedUpdates(() => {
|
||||||
|
setFrameworkData(responseJson)
|
||||||
|
setFrameworkLoaded(true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert.error(error.toString());
|
|
||||||
setFrameworkLoaded(true)
|
setFrameworkLoaded(true)
|
||||||
|
alert.error(error.toString());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +68,7 @@ const Framework = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{frameworkLoaded === true ?
|
{frameworkLoaded === true && isLoaded ?
|
||||||
<DetectionFramework
|
<DetectionFramework
|
||||||
frameworkData={frameworkData}
|
frameworkData={frameworkData}
|
||||||
selectedOption={"Draw"}
|
selectedOption={"Draw"}
|
||||||
|
|||||||
@@ -509,6 +509,8 @@ const Workflows = (props) => {
|
|||||||
const [newWorkflowTags, setNewWorkflowTags] = React.useState([]);
|
const [newWorkflowTags, setNewWorkflowTags] = React.useState([]);
|
||||||
|
|
||||||
const [defaultReturnValue, setDefaultReturnValue] = React.useState("");
|
const [defaultReturnValue, setDefaultReturnValue] = React.useState("");
|
||||||
|
const [blogpost, setBlogpost] = React.useState("");
|
||||||
|
const [status, setStatus] = React.useState("test");
|
||||||
|
|
||||||
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
|
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
|
||||||
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
||||||
@@ -816,7 +818,10 @@ const Workflows = (props) => {
|
|||||||
data.tags,
|
data.tags,
|
||||||
data.default_return_value,
|
data.default_return_value,
|
||||||
{},
|
{},
|
||||||
false
|
false,
|
||||||
|
[],
|
||||||
|
"",
|
||||||
|
data.status,
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response !== undefined) {
|
if (response !== undefined) {
|
||||||
@@ -830,7 +835,10 @@ const Workflows = (props) => {
|
|||||||
data.tags,
|
data.tags,
|
||||||
data.default_return_value,
|
data.default_return_value,
|
||||||
data,
|
data,
|
||||||
false
|
false,
|
||||||
|
[],
|
||||||
|
"",
|
||||||
|
data.status
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
if (response !== undefined) {
|
if (response !== undefined) {
|
||||||
alert.success(`Successfully imported ${data.name}`);
|
alert.success(`Successfully imported ${data.name}`);
|
||||||
@@ -883,12 +891,18 @@ const Workflows = (props) => {
|
|||||||
if (responseJson !== undefined) {
|
if (responseJson !== undefined) {
|
||||||
setWorkflows(responseJson);
|
setWorkflows(responseJson);
|
||||||
fetchUsecases(responseJson)
|
fetchUsecases(responseJson)
|
||||||
|
|
||||||
|
var setProdFilter = false
|
||||||
|
|
||||||
if (responseJson !== undefined) {
|
if (responseJson !== undefined) {
|
||||||
|
|
||||||
var actionnamelist = [];
|
var actionnamelist = [];
|
||||||
var parsedactionlist = [];
|
var parsedactionlist = [];
|
||||||
for (var key in responseJson) {
|
for (var key in responseJson) {
|
||||||
|
const workflow = responseJson[key]
|
||||||
|
if (workflow.status === "production") {
|
||||||
|
setProdFilter = true
|
||||||
|
}
|
||||||
|
|
||||||
for (var actionkey in responseJson[key].actions) {
|
for (var actionkey in responseJson[key].actions) {
|
||||||
const action = responseJson[key].actions[actionkey];
|
const action = responseJson[key].actions[actionkey];
|
||||||
//console.log("Action: ", action)
|
//console.log("Action: ", action)
|
||||||
@@ -905,10 +919,22 @@ const Workflows = (props) => {
|
|||||||
setActionImageList(parsedactionlist);
|
setActionImageList(parsedactionlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilteredWorkflows(responseJson);
|
|
||||||
setWorkflowDone(true);
|
if (setProdFilter === true) {
|
||||||
|
setFilters(["status:production"]);
|
||||||
|
const newWorkflows = responseJson.filter(workflow => workflow.status === "production")
|
||||||
|
console.log(newWorkflows)
|
||||||
|
if (newWorkflows !== undefined && newWorkflows !== null) {
|
||||||
|
setFilteredWorkflows(newWorkflows);
|
||||||
|
} else {
|
||||||
|
setFilteredWorkflows(responseJson);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFilteredWorkflows(responseJson);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures the zooming happens only once per load
|
// Ensures the zooming happens only once per load
|
||||||
|
setWorkflowDone(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setFirstLoad(false)
|
setFirstLoad(false)
|
||||||
}, 100)
|
}, 100)
|
||||||
@@ -946,7 +972,7 @@ const Workflows = (props) => {
|
|||||||
if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) {
|
if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) {
|
||||||
for (var usecasekey in workflow.usecase_ids) {
|
for (var usecasekey in workflow.usecase_ids) {
|
||||||
if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) {
|
if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) {
|
||||||
console.log("Got match: ", workflow.usecase_ids[usecasekey])
|
//console.log("Got match: ", workflow.usecase_ids[usecasekey])
|
||||||
|
|
||||||
category.matches.push({
|
category.matches.push({
|
||||||
"workflow": workflow.id,
|
"workflow": workflow.id,
|
||||||
@@ -1247,7 +1273,8 @@ const Workflows = (props) => {
|
|||||||
|
|
||||||
// Add correct ID's for triggers
|
// Add correct ID's for triggers
|
||||||
// Add mag
|
// Add mag
|
||||||
|
|
||||||
|
data.status = "test"
|
||||||
let dataStr = JSON.stringify(data);
|
let dataStr = JSON.stringify(data);
|
||||||
let dataUri =
|
let dataUri =
|
||||||
"data:application/json;charset=utf-8," + encodeURIComponent(dataStr);
|
"data:application/json;charset=utf-8," + encodeURIComponent(dataStr);
|
||||||
@@ -1638,6 +1665,7 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("data: ", data)
|
||||||
return (
|
return (
|
||||||
<div style={{width: "100%", position: "relative",}}>
|
<div style={{width: "100%", position: "relative",}}>
|
||||||
<Paper square style={paperAppStyle}>
|
<Paper square style={paperAppStyle}>
|
||||||
@@ -1667,7 +1695,16 @@ const Workflows = (props) => {
|
|||||||
{image}
|
{image}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={`Edit ${data.name}`} placement="bottom">
|
<Tooltip arrow title={
|
||||||
|
<span style={{}}>
|
||||||
|
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||||
|
<img src={data.image} alt={data.name} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 250, minHeigth: 250, borderRadius: theme.palette.borderRadius, }} />
|
||||||
|
: null}
|
||||||
|
<Typography>
|
||||||
|
Edit {data.name}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
} placement="bottom">
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body1"
|
||||||
style={{
|
style={{
|
||||||
@@ -1732,7 +1769,7 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
color="primary"
|
color="primary"
|
||||||
title="Trigger amount"
|
title="Amount of triggers"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -1884,6 +1921,8 @@ const Workflows = (props) => {
|
|||||||
editingWorkflow,
|
editingWorkflow,
|
||||||
redirect,
|
redirect,
|
||||||
currentUsecases,
|
currentUsecases,
|
||||||
|
inputblogpost,
|
||||||
|
inputstatus,
|
||||||
) => {
|
) => {
|
||||||
var method = "POST";
|
var method = "POST";
|
||||||
var extraData = "";
|
var extraData = "";
|
||||||
@@ -1905,6 +1944,8 @@ const Workflows = (props) => {
|
|||||||
if (tags !== undefined) {
|
if (tags !== undefined) {
|
||||||
workflowdata["tags"] = tags;
|
workflowdata["tags"] = tags;
|
||||||
}
|
}
|
||||||
|
workflowdata["blogpost"] = inputblogpost
|
||||||
|
workflowdata["status"] = inputstatus
|
||||||
|
|
||||||
if (defaultReturnValue !== undefined) {
|
if (defaultReturnValue !== undefined) {
|
||||||
workflowdata["default_return_value"] = defaultReturnValue;
|
workflowdata["default_return_value"] = defaultReturnValue;
|
||||||
@@ -2005,7 +2046,10 @@ const Workflows = (props) => {
|
|||||||
data.tags,
|
data.tags,
|
||||||
data.default_return_value,
|
data.default_return_value,
|
||||||
{},
|
{},
|
||||||
false
|
false,
|
||||||
|
[],
|
||||||
|
"",
|
||||||
|
data.status,
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response !== undefined) {
|
if (response !== undefined) {
|
||||||
@@ -2022,7 +2066,10 @@ const Workflows = (props) => {
|
|||||||
data.tags,
|
data.tags,
|
||||||
data.default_return_value,
|
data.default_return_value,
|
||||||
data,
|
data,
|
||||||
false
|
false,
|
||||||
|
[],
|
||||||
|
"",
|
||||||
|
data.status,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
if (response !== undefined) {
|
if (response !== undefined) {
|
||||||
alert.success("Successfully imported " + data.name);
|
alert.success("Successfully imported " + data.name);
|
||||||
@@ -2212,7 +2259,7 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
color="primary"
|
color="primary"
|
||||||
title="Trigger amount"
|
title="Amount of triggers"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -2535,7 +2582,41 @@ const Workflows = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showMoreClicked ?
|
{showMoreClicked ?
|
||||||
<span>
|
<span style={{marginTop: 25, }}>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
if (event.target.value.toLowerCase() === "test") {
|
||||||
|
setStatus("test")
|
||||||
|
} else if (event.target.value.toLowerCase() === "production") {
|
||||||
|
setStatus("production")
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={status}
|
||||||
|
placeholder="The status of the workflow. Can be test or production."
|
||||||
|
rows="1"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => setBlogpost(event.target.value)}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={blogpost}
|
||||||
|
placeholder="A blogpost or other reference for how this work workflow was built, and what it's for."
|
||||||
|
rows="1"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
onBlur={(event) => setDefaultReturnValue(event.target.value)}
|
onBlur={(event) => setDefaultReturnValue(event.target.value)}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
@@ -2596,6 +2677,8 @@ const Workflows = (props) => {
|
|||||||
editingWorkflow,
|
editingWorkflow,
|
||||||
false,
|
false,
|
||||||
selectedUsecases,
|
selectedUsecases,
|
||||||
|
blogpost,
|
||||||
|
status,
|
||||||
);
|
);
|
||||||
|
|
||||||
setNewWorkflowName("");
|
setNewWorkflowName("");
|
||||||
@@ -2612,6 +2695,8 @@ const Workflows = (props) => {
|
|||||||
{},
|
{},
|
||||||
true,
|
true,
|
||||||
selectedUsecases,
|
selectedUsecases,
|
||||||
|
blogpost,
|
||||||
|
status,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2999,7 +3084,6 @@ const Workflows = (props) => {
|
|||||||
backgroundColor: filters.includes(usecase.name.toLowerCase()) ? usecase.color : theme.palette.surfaceColor,
|
backgroundColor: filters.includes(usecase.name.toLowerCase()) ? usecase.color : theme.palette.surfaceColor,
|
||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
marginRight: index === usecases.length-1 ? 0 : 10,
|
marginRight: index === usecases.length-1 ? 0 : 10,
|
||||||
height: 60,
|
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
border: `2px solid ${usecase.color}`,
|
border: `2px solid ${usecase.color}`,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
|
|||||||
Reference in New Issue
Block a user