Bunch of fixes for MSSP feature buildout
This commit is contained in:
@@ -134,6 +134,9 @@ const AppStats = (defaultprops) => {
|
|||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log("Error getting workflow stats: " + error);
|
||||||
|
return workflow
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
|
|||||||
@@ -428,7 +428,6 @@ const ConfigureWorkflow = (props) => {
|
|||||||
console.log("Found webhook: ", trigger)
|
console.log("Found webhook: ", trigger)
|
||||||
|
|
||||||
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
|
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
|
||||||
console.log("Actions: ", newactions)
|
|
||||||
const findapp = trigger.app_association.name.toLowerCase()
|
const findapp = trigger.app_association.name.toLowerCase()
|
||||||
const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
|
const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
|
||||||
|
|
||||||
@@ -449,9 +448,6 @@ const ConfigureWorkflow = (props) => {
|
|||||||
|
|
||||||
newactions[foundindex].show_steps = true
|
newactions[foundindex].show_steps = true
|
||||||
|
|
||||||
console.log("CHANGED ACTION: ", newactions[foundindex])
|
|
||||||
//console.log("Index: ", newactions[foundindex])
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1321,7 +1317,6 @@ const ConfigureWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (step.type === "authenticate") {
|
if (step.type === "authenticate") {
|
||||||
console.log("AUTH STEP: ", step)
|
|
||||||
if (data.must_authenticate === true ) {
|
if (data.must_authenticate === true ) {
|
||||||
filled = false
|
filled = false
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ const EditWorkflow = (props) => {
|
|||||||
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
||||||
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
|
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
|
||||||
|
|
||||||
console.log("WORKFLOW: ", workflow)
|
|
||||||
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
|
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@@ -235,7 +234,7 @@ const EditWorkflow = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div style={{width: 600, position: "fixed", left: 0, bottom: 0, zIndex: 1002, backgroundColor: "rgba(53,53,53,1)", height: 75, paddingTop: 20, paddingLeft: 75, }}>
|
<div style={{borderTop: "1px solid rgba(255,255,255,0.5)", width: 600, position: "fixed", left: 0, bottom: 0, zIndex: 1002, backgroundColor: "rgba(53,53,53,1)", height: 75, paddingTop: 20, paddingLeft: 75, }}>
|
||||||
{/*
|
{/*
|
||||||
<Button
|
<Button
|
||||||
style={{}}
|
style={{}}
|
||||||
@@ -315,7 +314,7 @@ const EditWorkflow = (props) => {
|
|||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{submitLoading ? <CircularProgress color="secondary" /> : "Done"}
|
{submitLoading ? <CircularProgress color="secondary" /> : "Save Changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -565,14 +564,22 @@ const EditWorkflow = (props) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Typography variant="body2" style={{marginTop: 50, }}>
|
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||||
MSSP Suborg Distribution (beta - contact support@shuffler.io)
|
MSSP Suborg Distribution (beta - contact support@shuffler.io for more info)
|
||||||
</Typography>
|
</Typography>
|
||||||
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
||||||
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
||||||
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ?
|
||||||
You can only distribute to suborgs from a parent org.
|
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||||
</Typography>
|
Your organization does not have any suborgs yet. Please <a href="/admin?tab=suborgs" style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">make one</a>, then try again.
|
||||||
|
</Typography>
|
||||||
|
:
|
||||||
|
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||||
|
{innerWorkflow.parentorg_workflow !== undefined && innerWorkflow.parentorg_workflow !== null && innerWorkflow.parentorg_workflow.length > 0 ? <span>This workflow is distributed from <a href={`/workflows/${innerWorkflow.parentorg_workflow}`} style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">your parent workflow</a> (you may not have access).</span> : null}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You can only distribute to suborgs from a parent org.
|
||||||
|
</Typography>
|
||||||
:
|
:
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
@@ -584,8 +591,6 @@ const EditWorkflow = (props) => {
|
|||||||
newvalue = newvalue.filter(value => value !== "none")
|
newvalue = newvalue.filter(value => value !== "none")
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("NEWVALUE: ", newvalue)
|
|
||||||
|
|
||||||
if (newvalue.includes("none")) {
|
if (newvalue.includes("none")) {
|
||||||
newvalue = ["none"]
|
newvalue = ["none"]
|
||||||
} else if (newvalue.includes("all")) {
|
} else if (newvalue.includes("all")) {
|
||||||
@@ -775,18 +780,172 @@ const EditWorkflow = (props) => {
|
|||||||
>
|
>
|
||||||
<AddIcon style={{}} />
|
<AddIcon style={{}} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
|
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||||
|
Git Backup Repository
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" style={{ textAlign: "left", marginTop: 5, }} color="textSecondary">
|
||||||
|
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides the <a href="/admin?admin_tab=organization" style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">default backup repository</a> your org has chosen. All fields must be filled in for the backup to work.
|
||||||
|
<br />
|
||||||
|
PS: This is a beta feature, and might not work as expected. Credentials are NOT encrypted.
|
||||||
|
</Typography>
|
||||||
|
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Workflow Backup Repository</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="github/com/shuffle/workflowbackup "
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_repo === undefined || innerWorkflow.backup_config.upload_repo === null || innerWorkflow.backup_config.upload_repo === "" ? "" : innerWorkflow.backup_config.upload_repo}
|
||||||
|
onChange={(e) => {
|
||||||
|
//setUploadRepo(e.target.value);
|
||||||
|
innerWorkflow.backup_config.upload_repo = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Branch</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
placeholder="The branch to use"
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_branch === undefined || innerWorkflow.backup_config.upload_branch === null || innerWorkflow.backup_config.upload_branch === "" ? "" : innerWorkflow.backup_config.upload_branch}
|
||||||
|
onChange={(e) => {
|
||||||
|
innerWorkflow.backup_config.upload_branch = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container style={{ }} spacing={2}>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Username</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="The username to use"
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_username === undefined || innerWorkflow.backup_config.upload_username === null || innerWorkflow.backup_config.upload_username === "" ? "" : innerWorkflow.backup_config.upload_username}
|
||||||
|
onChange={(e) => {
|
||||||
|
innerWorkflow.backup_config.upload_username = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Git token/password</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
placeholder="The API token to use"
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_token === undefined || innerWorkflow.backup_config.upload_token === null || innerWorkflow.backup_config.upload_token === "" ? "" : innerWorkflow.backup_config.upload_token}
|
||||||
|
onChange={(e) => {
|
||||||
|
innerWorkflow.backup_config.upload_token = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</span>
|
</span>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
|
|
||||||
<Tooltip color="primary" title={"Add more details"} placement="top">
|
<Tooltip color="primary" title={"Add more details"} placement="top">
|
||||||
<IconButton
|
<Button
|
||||||
style={{ color: "white", margin: "auto", textAlign: "center", width: 50, marginTop: 50, }}
|
style={{ margin: "auto", marginTop: 50, textAlign: "center", }}
|
||||||
|
variant="outlined"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMoreClicked(!showMoreClicked);
|
setShowMoreClicked(!showMoreClicked);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||||
</IconButton>
|
{showMoreClicked ? "Collapse": "Expand"}
|
||||||
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import theme from "../theme.jsx";
|
import theme from "../theme.jsx";
|
||||||
import { BrowserView, MobileView } from "react-device-detect";
|
import { BrowserView, MobileView } from "react-device-detect";
|
||||||
@@ -84,6 +84,15 @@ const Header = (props) => {
|
|||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const topbar = localStorage.getItem("topbar_closed")
|
||||||
|
if (topbar === "true") {
|
||||||
|
setShowTopbar(false)
|
||||||
|
} else {
|
||||||
|
setShowTopbar(true)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setAnchorElAvatar(null);
|
setAnchorElAvatar(null);
|
||||||
@@ -1590,17 +1599,17 @@ const Header = (props) => {
|
|||||||
|
|
||||||
navigate("/training")
|
navigate("/training")
|
||||||
|
|
||||||
//if (window.drift !== undefined) {
|
|
||||||
// window.drift.api.startInteraction({ interactionId: 341911 })
|
|
||||||
//} else {
|
|
||||||
// console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
|
||||||
//}
|
|
||||||
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
|
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
|
||||||
Public Training!
|
Public Training!
|
||||||
</span>
|
</span>
|
||||||
</u>
|
</u>
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => { setShowTopbar(false) }}>
|
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => {
|
||||||
|
setShowTopbar(false)
|
||||||
|
|
||||||
|
// Set storage that it's clicked
|
||||||
|
localStorage.setItem("topbar_closed", "true")
|
||||||
|
}}>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -167,6 +167,7 @@ const ParsedAction = (props) => {
|
|||||||
|
|
||||||
listCache,
|
listCache,
|
||||||
|
|
||||||
|
authGroups,
|
||||||
apps,
|
apps,
|
||||||
setEditorData,
|
setEditorData,
|
||||||
setcodedata,
|
setcodedata,
|
||||||
@@ -1123,6 +1124,65 @@ const ParsedAction = (props) => {
|
|||||||
return helperText
|
return helperText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const analyzeFields = () => {
|
||||||
|
|
||||||
|
if (selectedAction === undefined || selectedAction === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only shuffle tools for now
|
||||||
|
if (selectedAction.app_name !== "Shuffle Tools") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom rules
|
||||||
|
if (selectedAction.name === "set_cache_value") {
|
||||||
|
var actionKey = ""
|
||||||
|
var actionValue = ""
|
||||||
|
for (let [key,keyval] in Object.entries(selectedActionParameters)) {
|
||||||
|
const param = selectedActionParameters[key]
|
||||||
|
if (param.name === "key") {
|
||||||
|
actionKey = param.value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.name === "value") {
|
||||||
|
actionValue = param.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionKey === "" || actionValue === "") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actionKey.includes(".#") && actionValue.includes(".#")) {
|
||||||
|
return <span>When the key ({actionKey}) is static, but the value is a list ({actionValue}), it will overwrite the list. You may be looking for the <span onClick={() => {}} style={{cursor: "pointer", color: "#f85a3e", }}>Check Cache Contains</span> action instead.</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const suggestionInfo = () => {
|
||||||
|
const suggestionText = analyzeFields()
|
||||||
|
if (suggestionText === undefined || suggestionText === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedAction.errors === undefined || selectedAction.errors === null || selectedAction.errors.length === 0) {
|
||||||
|
selectedAction.errors = ["Suggestion: " + suggestionText]
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Paper style={{padding: 10, backgroundColor: theme.palette.surfaceColor, border: "1px solid red",}}>
|
||||||
|
<Typography variant="body" style={{color: "white", }}>
|
||||||
|
<b>Tip:</b> {suggestionText}
|
||||||
|
</Typography>
|
||||||
|
</Paper>
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Issue #40 - selectedActionParameters not reset
|
// FIXME: Issue #40 - selectedActionParameters not reset
|
||||||
if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0) {
|
if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0) {
|
||||||
@@ -1310,6 +1370,7 @@ const ParsedAction = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
{selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ?
|
{selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ?
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
@@ -1441,6 +1502,8 @@ const ParsedAction = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{suggestionInfo()}
|
||||||
|
|
||||||
{selectedActionParameters.map((data, count) => {
|
{selectedActionParameters.map((data, count) => {
|
||||||
if (data.variant === "") {
|
if (data.variant === "") {
|
||||||
data.variant = "STATIC_VALUE";
|
data.variant = "STATIC_VALUE";
|
||||||
@@ -3606,6 +3669,7 @@ const ParsedAction = (props) => {
|
|||||||
}}
|
}}
|
||||||
labelId="select-app-auth"
|
labelId="select-app-auth"
|
||||||
value={
|
value={
|
||||||
|
selectedAction.authentication_id === "authgroups" ? "authgroups" :
|
||||||
Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0
|
Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0
|
||||||
? "No selection"
|
? "No selection"
|
||||||
: selectedAction.selectedAuthentication
|
: selectedAction.selectedAuthentication
|
||||||
@@ -3617,6 +3681,8 @@ const ParsedAction = (props) => {
|
|||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
console.log("AUTH CHANGE: ", e.target.value)
|
||||||
|
|
||||||
if (e.target.value === "No selection") {
|
if (e.target.value === "No selection") {
|
||||||
selectedAction.selectedAuthentication = {};
|
selectedAction.selectedAuthentication = {};
|
||||||
selectedAction.authentication_id = "";
|
selectedAction.authentication_id = "";
|
||||||
@@ -3628,13 +3694,36 @@ const ParsedAction = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setSelectedAction(selectedAction);
|
setSelectedAction(selectedAction);
|
||||||
setUpdate(Math.random());
|
setUpdate(Math.random())
|
||||||
|
|
||||||
|
} else if (e.target.value === "authgroups") {
|
||||||
|
if (authGroups !== undefined && authGroups !== null && authGroups.length === 0) {
|
||||||
|
toast("No auth groups created. Opening window to create one")
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.open("/admin?tab=app_auth", "_blank")
|
||||||
|
}, 2500)
|
||||||
|
} else {
|
||||||
|
selectedAction.selectedAuthentication = {};
|
||||||
|
selectedAction.authentication_id = "authgroups"
|
||||||
|
|
||||||
|
for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
|
||||||
|
//console.log(selectedAction.parameters[key])
|
||||||
|
if (selectedAction.parameters[key].configuration) {
|
||||||
|
selectedAction.parameters[key].value = "authgroup controlled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedAction(selectedAction)
|
||||||
|
setUpdate(Math.random())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
selectedAction.selectedAuthentication = e.target.value;
|
selectedAction.selectedAuthentication = e.target.value;
|
||||||
selectedAction.authentication_id = e.target.value.id;
|
selectedAction.authentication_id = e.target.value.id;
|
||||||
setSelectedAction(selectedAction);
|
setSelectedAction(selectedAction);
|
||||||
setUpdate(Math.random());
|
setUpdate(Math.random());
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.palette.inputColor,
|
backgroundColor: theme.palette.inputColor,
|
||||||
@@ -3653,6 +3742,7 @@ const ParsedAction = (props) => {
|
|||||||
>
|
>
|
||||||
<em>No selection</em>
|
<em>No selection</em>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{selectedAction.authentication.map((data) => {
|
{selectedAction.authentication.map((data) => {
|
||||||
if (data.last_modified === true) {
|
if (data.last_modified === true) {
|
||||||
//console.log("LAST MODIFIED: ", data.label)
|
//console.log("LAST MODIFIED: ", data.label)
|
||||||
@@ -3689,6 +3779,20 @@ const ParsedAction = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|
||||||
|
<Divider style={{marginTop: 10, marginBottom: 10, }}/>
|
||||||
|
|
||||||
|
<MenuItem
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
value="authgroups"
|
||||||
|
>
|
||||||
|
<em>Auth Groups</em>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
|
|||||||
@@ -627,8 +627,8 @@ const CodeEditor = (props) => {
|
|||||||
newMarkers.push({
|
newMarkers.push({
|
||||||
startRow: i,
|
startRow: i,
|
||||||
startCol: startCh,
|
startCol: startCh,
|
||||||
endRow: i+1,
|
endRow: i,
|
||||||
endCol: endCh+1,
|
endCol: endCh,
|
||||||
className: correctVariable ? "good-marker" : "bad-marker",
|
className: correctVariable ? "good-marker" : "bad-marker",
|
||||||
type: "text",
|
type: "text",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -133,6 +133,11 @@ const data = [
|
|||||||
"background-gradient-stop-colors": "data(fillGradient)",
|
"background-gradient-stop-colors": "data(fillGradient)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selector: `node[?parent_controlled]`,
|
||||||
|
css: {
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
selector: `node[app_name="Testing"]`,
|
selector: `node[app_name="Testing"]`,
|
||||||
css: {
|
css: {
|
||||||
|
|||||||
+296
-163
@@ -166,7 +166,9 @@ const Admin = (props) => {
|
|||||||
//console.log("Selected: ", selectedOrganization)
|
//console.log("Selected: ", selectedOrganization)
|
||||||
const [appAuthenticationGroupModalOpen , setAppAuthenticationGroupModalOpen] = React.useState(false);
|
const [appAuthenticationGroupModalOpen , setAppAuthenticationGroupModalOpen] = React.useState(false);
|
||||||
const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]);
|
const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]);
|
||||||
|
const [appAuthenticationGroupId, setAppAuthenticationGroupId] = React.useState("");
|
||||||
const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState("");
|
const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState("");
|
||||||
|
const [appAuthenticationGroupEnvironment, setAppAuthenticationGroupEnvironment] = React.useState("");
|
||||||
const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState("");
|
const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState("");
|
||||||
const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]);
|
const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]);
|
||||||
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
||||||
@@ -431,23 +433,62 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAppAuthenticationGroup = (name, description, appAuthIds) => {
|
const deleteAppAuthenticationGroup = (appAuthGroupId) => {
|
||||||
|
const url = `${globalUrl}/api/v1/authentication/group/${appAuthGroupId}`
|
||||||
|
fetch(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for deleting app auth group");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
toast("Failed to delete app authentication group");
|
||||||
|
} else {
|
||||||
|
toast("App authentication group deleted")
|
||||||
|
getAppAuthenticationGroups()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
toast(error.toString())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const createAppAuthenticationGroup = (name, environment, description, appAuthIds) => {
|
||||||
|
// Makes list of ids into a full-on list of auth, but just with the ID
|
||||||
|
// The backend fills in the rest
|
||||||
|
console.log("INput auth: ", appAuthIds)
|
||||||
let app_auths = appAuthIds.map((appAuthId) => {
|
let app_auths = appAuthIds.map((appAuthId) => {
|
||||||
return { id: appAuthId };
|
return { id: appAuthId };
|
||||||
})
|
})
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/apps/authentication/group", {
|
var parsedAppGroup = {
|
||||||
|
label: name,
|
||||||
|
environment: environment,
|
||||||
|
description: description,
|
||||||
|
app_auths: app_auths
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appAuthenticationGroupId !== undefined && appAuthenticationGroupId !== null && appAuthenticationGroupId !== "") {
|
||||||
|
parsedAppGroup.id = appAuthenticationGroupId
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(globalUrl + "/api/v1/authentication/group", {
|
||||||
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({
|
body: JSON.stringify(parsedAppGroup),
|
||||||
label: name,
|
|
||||||
description: description,
|
|
||||||
app_auths: app_auths
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
@@ -457,8 +498,15 @@ const Admin = (props) => {
|
|||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
// getAppAuthenticationGroups();
|
if (responseJson.success === false) {
|
||||||
toast("App authentication group created");
|
toast("Failed to create. Please try again, or contact support@shuffler.io")
|
||||||
|
} else {
|
||||||
|
// Close the modal
|
||||||
|
setAppAuthenticationGroupModalOpen(false)
|
||||||
|
|
||||||
|
toast("App authentication group created")
|
||||||
|
getAppAuthenticationGroups()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
toast(error.toString());
|
toast(error.toString());
|
||||||
@@ -805,7 +853,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
setWebHooks(responseJson.webhooks || []); // Handling the case where the result is null or undefined
|
setWebHooks(responseJson.webhooks || []); // Handling the case where the result is null or undefined
|
||||||
setAllSchedules(responseJson.schedules || []);
|
setAllSchedules(responseJson.schedules || []);
|
||||||
setPipelines(responseJson.pipelines || []);
|
// setPipelines(responseJson.pipelines || []);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
// toast(error.toString());
|
// toast(error.toString());
|
||||||
@@ -990,13 +1038,11 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
command: pipeline.command,
|
|
||||||
name: pipeline.name,
|
name: pipeline.name,
|
||||||
type: state,
|
type: state,
|
||||||
environment: pipeline.environment,
|
environment: pipeline.environment,
|
||||||
workflow_id: pipeline.workflow_id,
|
workflow_id: pipeline.workflow_id,
|
||||||
trigger_id: pipeline.trigger_id,
|
trigger_id: pipeline.trigger_id,
|
||||||
start_node: pipeline.start_node,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state === "start") toast("starting the pipeline");
|
if (state === "start") toast("starting the pipeline");
|
||||||
@@ -1027,7 +1073,6 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
if (state === "start") toast("Successfully created pipeline");
|
if (state === "start") toast("Successfully created pipeline");
|
||||||
else toast("Sucessfully stopped the pipeline");
|
else toast("Sucessfully stopped the pipeline");
|
||||||
}
|
}
|
||||||
setTimeout(handleGetAllTriggers, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
//toast(error.toString());
|
//toast(error.toString());
|
||||||
@@ -2093,10 +2138,10 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAppAuthenticationGroups = () => {
|
const getAppAuthenticationGroups = () => {
|
||||||
console.log("DEBUG: Skipping app auth group loading")
|
//console.log("DEBUG: Skipping app auth group loading")
|
||||||
return
|
//return
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/apps/authentication/group", {
|
fetch(globalUrl + "/api/v1/authentication/group", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -4941,7 +4986,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
|
|
||||||
<ListItemText>
|
<ListItemText>
|
||||||
<Button
|
<Button
|
||||||
style={{ marginLeft: "140px" }}
|
style={{ marginLeft: "18%" }}
|
||||||
variant={
|
variant={
|
||||||
webhook.status === "running" ? "contained" : "outlined"
|
webhook.status === "running" ? "contained" : "outlined"
|
||||||
}
|
}
|
||||||
@@ -4963,10 +5008,10 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
{/* <div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||||
<h2 style={{ display: "inline" }}>Tenzir Pipelines</h2>
|
<h2 style={{ display: "inline" }}>Tenzir Pipelines</h2>
|
||||||
<span style={{ marginLeft: 25 }}>
|
<span style={{ marginLeft: 25 }}>
|
||||||
Controls the Tenzir pipeline operations.{" "}
|
Controls a pipeline to run things.{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -5013,7 +5058,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
primary="Workflow"
|
primary="Workflow"
|
||||||
style={{ maxWidth: 315, minWidth: 315 }}
|
style={{ maxWidth: 315, minWidth: 315 }}
|
||||||
/>
|
/>
|
||||||
<ListItemText primary="Actions" style={{ marginLeft: '120px' }} />
|
<ListItemText primary="Actions" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{pipelines.map((pipeline, index) => {
|
{pipelines.map((pipeline, index) => {
|
||||||
var bgColor = "#27292d";
|
var bgColor = "#27292d";
|
||||||
@@ -5066,7 +5111,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}*/}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
@@ -5170,31 +5215,63 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
|
|
||||||
|
|
||||||
const handleAppAuthGroupCheckbox = (data) => {
|
const handleAppAuthGroupCheckbox = (data) => {
|
||||||
let appOrginal = data.app
|
//let groupApp = data.app.id
|
||||||
if (appsForAppAuthGroup.includes(appOrginal.id)) {
|
var newappauth = appsForAppAuthGroup
|
||||||
return;
|
if (appsForAppAuthGroup.includes(data.app.id)) {
|
||||||
}
|
newappauth = newappauth.filter((item) => item !== data.app.id)
|
||||||
|
}
|
||||||
|
|
||||||
setAppsForAppAuthGroup([...appsForAppAuthGroup, data.id]);
|
if (appsForAppAuthGroup.includes(data.id)) {
|
||||||
console.log("Apps for app auth group: ", appsForAppAuthGroup);
|
// Remove app from app auth group
|
||||||
};
|
newappauth = newappauth.filter((item) => item !== data.id)
|
||||||
|
setAppsForAppAuthGroup(newappauth)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < authentication.length; i++) {
|
||||||
|
if (authentication[i].id === data.id) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appsForAppAuthGroup.includes(authentication[i].id)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authentication[i].app.id === data.app.id) {
|
||||||
|
// Remove app from app auth group
|
||||||
|
newappauth = newappauth.filter((item) => item !== authentication[i].id)
|
||||||
|
toast(`App ${data.app.name} is already in this group`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAppsForAppAuthGroup(newappauth.concat(data.id))
|
||||||
|
}
|
||||||
|
|
||||||
const authenticationView =
|
const authenticationView =
|
||||||
curTab === 2 ? (
|
curTab === 2 ? (
|
||||||
<>
|
|
||||||
|
<div>
|
||||||
{/* (appAuthenticationGroupModalOpen : { */}
|
{/* (appAuthenticationGroupModalOpen : { */}
|
||||||
{appAuthenticationGroupModalOpen && (
|
{appAuthenticationGroupModalOpen && (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={appAuthenticationGroupModalOpen}
|
open={appAuthenticationGroupModalOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setAppAuthenticationGroupModalOpen(false);
|
setAppAuthenticationGroupModalOpen(false);
|
||||||
|
|
||||||
|
setAppAuthenticationGroupId("")
|
||||||
|
setAppAuthenticationGroupName("")
|
||||||
|
setAppAuthenticationGroupEnvironment("")
|
||||||
|
setAppAuthenticationGroupDescription("")
|
||||||
|
setAppsForAppAuthGroup([])
|
||||||
}}
|
}}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
minWidth: "1200px",
|
minWidth: "1000px",
|
||||||
minHeight: "320px",
|
minHeight: "320px",
|
||||||
|
padding: 25,
|
||||||
|
paddingLeft: 50,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -5202,106 +5279,130 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
<span style={{ color: "white" }}>App Authentication Groups</span>
|
<span style={{ color: "white" }}>App Authentication Groups</span>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent style={{marginLeft: 0, paddingLeft: 0, }}>
|
||||||
<div>
|
<div style={{display: "flex", position: "sticky", top: 0, zIndex: 1, backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette.borderRadius, padding: 20, marginBottom: 10, }}>
|
||||||
<TextField
|
<div style={{marginRight: 50, minWidth: 250, }}>
|
||||||
color="primary"
|
<Typography style={{marginTop: 10, }}>
|
||||||
style={{ backgroundColor: theme.palette.inputColor }}
|
Name
|
||||||
autoFocus
|
</Typography>
|
||||||
InputProps={{
|
<TextField
|
||||||
style: {
|
color="primary"
|
||||||
height: "50px",
|
label="Name"
|
||||||
color: "white",
|
style={{ backgroundColor: theme.palette.inputColor }}
|
||||||
fontSize: "1em",
|
autoFocus
|
||||||
},
|
InputProps={{
|
||||||
}}
|
style: {
|
||||||
required
|
height: "50px",
|
||||||
fullWidth={true}
|
color: "white",
|
||||||
placeholder="Name"
|
fontSize: "1em",
|
||||||
id="namefield"
|
},
|
||||||
margin="normal"
|
}}
|
||||||
variant="outlined"
|
required
|
||||||
onChange={(event) => {
|
fullWidth={true}
|
||||||
setAppAuthenticationGroupName(event.target.value);
|
placeholder="Name"
|
||||||
}}
|
id="namefield"
|
||||||
/>
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
defaultValue={appAuthenticationGroupName}
|
||||||
|
onChange={(event) => {
|
||||||
|
setAppAuthenticationGroupName(event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{marginRight: 50, }}>
|
||||||
|
<Typography style={{marginTop: 10, marginBottom: 10}}>
|
||||||
|
Evironment
|
||||||
|
</Typography>
|
||||||
|
{environments !== undefined && environments !== null && environments.length > 0 ?
|
||||||
|
<Select
|
||||||
|
defaultValue={appAuthenticationGroupEnvironment === "" ? environments[0].Name : appAuthenticationGroupEnvironment}
|
||||||
|
onChange={(e) => {
|
||||||
|
setAppAuthenticationGroupEnvironment(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{environments.map((env, index) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={env.Name}>
|
||||||
|
{env.Name}
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
:
|
||||||
|
<Typography>
|
||||||
|
Environments failed to load. Please try again
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style={{marginTop: 65, }}>
|
||||||
<TextField
|
|
||||||
color="primary"
|
|
||||||
style={{ backgroundColor: theme.palette.inputColor }}
|
|
||||||
autoFocus
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
height: "50px",
|
|
||||||
color: "white",
|
|
||||||
fontSize: "1em",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
fullWidth={true}
|
|
||||||
placeholder="Description"
|
|
||||||
id="descriptionfield"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
onChange={(event) => {
|
|
||||||
setAppAuthenticationGroupDescription(event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{/* Show a check box list of all app authentications to add to the auth group */}
|
|
||||||
<div>
|
|
||||||
{authentication.map((data, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Tooltip
|
|
||||||
title={data.app.name}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '10px', marginLeft: '5px' }}>
|
|
||||||
<img
|
|
||||||
src={data.app.large_image ? data.app.large_image : '/images/no_image.png'}
|
|
||||||
alt=""
|
|
||||||
style={{ width: '50px', height: '50px', marginRight: '10px' }}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
checked={data.checked}
|
|
||||||
onChange={(event) => {
|
|
||||||
handleAppAuthGroupCheckbox(data)
|
|
||||||
}}
|
|
||||||
name={data.label}
|
|
||||||
disabled={data.app.id in appsForAppAuthGroup}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
label={data.label}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Button
|
<Button
|
||||||
style={{}}
|
style={{}}
|
||||||
|
disabled={appAuthenticationGroupName === "" || appAuthenticationGroupEnvironment === "" || appsForAppAuthGroup.length === 0}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
createAppAuthenticationGroup(
|
createAppAuthenticationGroup(
|
||||||
appAuthenticationGroupName,
|
appAuthenticationGroupName,
|
||||||
|
appAuthenticationGroupEnvironment,
|
||||||
appAuthenticationGroupDescription,
|
appAuthenticationGroupDescription,
|
||||||
appsForAppAuthGroup
|
appsForAppAuthGroup,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Create
|
Set Group
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Divider style={{marginTop: 10, marginBottom: 10, }}/>
|
||||||
|
|
||||||
|
<div style={{marginLeft: 25, }}>
|
||||||
|
{/* Show a check box list of all app authentications to add to the auth group */}
|
||||||
|
<div>
|
||||||
|
{authentication.map((data, index) => {
|
||||||
|
var checked = data.checked
|
||||||
|
if (checked === undefined || checked === null) {
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appsForAppAuthGroup.includes(data.id)) {
|
||||||
|
checked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Tooltip
|
||||||
|
title={data.app.name}
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '10px', marginLeft: '5px', }}>
|
||||||
|
<img
|
||||||
|
src={data.app.large_image ? data.app.large_image : '/images/no_image.png'}
|
||||||
|
alt=""
|
||||||
|
style={{ borderRadius: theme.palette.borderRadius, width: 50, height: 50, marginRight: 10 }}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
checked={checked}
|
||||||
|
onChange={(event) => {
|
||||||
|
handleAppAuthGroupCheckbox(data)
|
||||||
|
}}
|
||||||
|
name={data.label}
|
||||||
|
disabled={data.app.id in appsForAppAuthGroup}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
label={data.label}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
@@ -5311,7 +5412,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||||
<h2 style={{ display: "inline" }}>App Authentication</h2>
|
<h2 style={{ display: "inline" }}>App Authentication</h2>
|
||||||
<span style={{ marginLeft: 25 }}>
|
<span style={{ marginLeft: 25 }}>
|
||||||
Control the authentication options for individual apps.
|
Control the authentication options for individual apps. App Groups are farther down on this page.
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
@@ -5587,10 +5688,18 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div>
|
<Divider
|
||||||
|
style={{
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div style={{marginTop: 50, }}>
|
||||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||||
<h2 style={{ display: "inline" }}>App Authentication Groups</h2>
|
<h2 style={{ }}>App Authentication Groups</h2>
|
||||||
<span style={{ marginLeft: 25 }}>
|
<span style={{ marginLeft: 0 }}>
|
||||||
Groups of authentication options for subflows.{" "}
|
Groups of authentication options for subflows.{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -5602,29 +5711,39 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Divider
|
<br />
|
||||||
style={{
|
<Button
|
||||||
marginTop: 20,
|
style={{ marginTop: 20 }}
|
||||||
marginBottom: 20,
|
variant="contained"
|
||||||
backgroundColor: theme.palette.inputColor,
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
|
||||||
|
if (environments !== undefined && environments !== null && environments.length > 0) {
|
||||||
|
setAppAuthenticationGroupEnvironment(environments[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setAppAuthenticationGroupModalOpen(true)
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<List>
|
Add Group
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<List style={{marginTop: 25, }}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Label"
|
primary="Label"
|
||||||
|
style={{ minWidth: 250, maxWidth: 250}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Environment"
|
||||||
style={{ minWidth: 150, maxWidth: 150 }}
|
style={{ minWidth: 150, maxWidth: 150 }}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Description"
|
primary="App Auth"
|
||||||
style={{ minWidth: 250, maxWidth: 250 }}
|
style={{ minWidth: 250, maxWidth: 250 }}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Apps"
|
primary="Created At"
|
||||||
style={{ minWidth: 250, maxWidth: 250 }}
|
|
||||||
/>
|
|
||||||
<ListItemText
|
|
||||||
primary="CreatedAt"
|
|
||||||
style={{ minWidth: 150, maxWidth: 150 }}
|
style={{ minWidth: 150, maxWidth: 150 }}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
@@ -5638,31 +5757,49 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
if (index % 2 === 0) {
|
if (index % 2 === 0) {
|
||||||
bgColor = "#1f2023";
|
bgColor = "#1f2023";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.app_auths === undefined || data.app_auths === null) {
|
||||||
|
data.app_auths = []
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem key={index} style={{ backgroundColor: bgColor }}>
|
<ListItem key={index} style={{ backgroundColor: bgColor }}>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={data.label}
|
primary={data.label}
|
||||||
style={{ minWidth: 150, maxWidth: 150 }}
|
style={{ minWidth: 250, maxWidth: 250, }}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={data.description}
|
primary={data.environment}
|
||||||
style={{ minWidth: 250, maxWidth: 250 }}
|
style={{ minWidth: 150, maxWidth: 150, }}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<div style={{ display: 'flex' }}>
|
<div style={{ display: 'flex' }}>
|
||||||
{data.app_auths.map((appAuth, index) => (
|
{data.app_auths.map((appAuth, index) => {
|
||||||
<Tooltip
|
if (appAuth.app.large_image === undefined || appAuth.app.large_image === null || appAuth.app.large_image === "") {
|
||||||
title={appAuth.app.name}
|
const foundImage = authentication.find((auth) => auth.app.id === appAuth.app.id)
|
||||||
>
|
if (foundImage !== undefined) {
|
||||||
<img
|
appAuth.app.large_image = foundImage.app.large_image
|
||||||
key={index}
|
|
||||||
src={appAuth.app.large_image}
|
appAuth.app.name = foundImage.app.name
|
||||||
alt={appAuth.app.name}
|
}
|
||||||
style={{ width: '24px', height: '24px', marginRight: '5px' }}
|
}
|
||||||
/>
|
|
||||||
</Tooltip>
|
const tooltip = `${appAuth.app.name.replaceAll("_", " ")} (authname: ${appAuth.label})`
|
||||||
))}
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={tooltip}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
key={index}
|
||||||
|
src={appAuth.app.large_image}
|
||||||
|
alt={appAuth.app.name}
|
||||||
|
style={{ width: 30, height: 30, marginRight: 5 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
style={{ minWidth: 250, maxWidth: 250 }}
|
style={{ minWidth: 250, maxWidth: 250 }}
|
||||||
@@ -5676,16 +5813,22 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
<div style={{ display: 'flex' }}>
|
<div style={{ display: 'flex' }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setAppAuthenticationGroupId(data.id)
|
||||||
|
|
||||||
|
setAppAuthenticationGroupName(data.label)
|
||||||
|
setAppAuthenticationGroupDescription(data.description)
|
||||||
|
|
||||||
|
setAppsForAppAuthGroup(data.app_auths.map((appAuth) => appAuth.id))
|
||||||
|
setAppAuthenticationGroupEnvironment(data.environment)
|
||||||
|
setAppAuthenticationGroupModalOpen(true)
|
||||||
}}
|
}}
|
||||||
disabled={true}
|
|
||||||
>
|
>
|
||||||
<EditIcon />
|
<EditIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// deleteAppAuthenticationGroup(data);
|
deleteAppAuthenticationGroup(data.id)
|
||||||
}}
|
}}
|
||||||
disabled={true}
|
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -5700,20 +5843,10 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<Button
|
|
||||||
style={{ marginLeft: 10 }}
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => {
|
|
||||||
setAppAuthenticationGroupModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add Group
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
</div>
|
||||||
</>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const getLogs = async (ip, userId) => {
|
const getLogs = async (ip, userId) => {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,7 @@ const Body = {
|
|||||||
height: "100%",
|
height: "100%",
|
||||||
color: "white",
|
color: "white",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
paddingTop: 40,
|
||||||
//textAlign: "center",
|
//textAlign: "center",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import {
|
|||||||
ArrowLeft as ArrowLeftIcon,
|
ArrowLeft as ArrowLeftIcon,
|
||||||
ArrowRight as ArrowRightIcon,
|
ArrowRight as ArrowRightIcon,
|
||||||
QueryStats as QueryStatsIcon,
|
QueryStats as QueryStatsIcon,
|
||||||
|
Visibility as VisibilityIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||||
@@ -614,6 +615,7 @@ const Workflows = (props) => {
|
|||||||
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
||||||
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
||||||
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
|
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
|
||||||
|
const [highlightIds, setHighlightIds] = React.useState([])
|
||||||
|
|
||||||
const [apps, setApps] = React.useState([]);
|
const [apps, setApps] = React.useState([]);
|
||||||
|
|
||||||
@@ -1213,7 +1215,24 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFirstLoad(false)
|
setFirstLoad(false)
|
||||||
}, 100)
|
}, 250)
|
||||||
|
|
||||||
|
/*
|
||||||
|
setTimeout(() => {
|
||||||
|
var timeout = 0
|
||||||
|
for (var key in newarray) {
|
||||||
|
const wf = newarray[key]
|
||||||
|
if (wf.actions === undefined || wf.actions === null || wf.actions.length === 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
sideloadWorkflow(wf.id, false)
|
||||||
|
}, timeout)
|
||||||
|
|
||||||
|
timeout += 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
*/
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
@@ -1637,20 +1656,24 @@ const Workflows = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyWorkflow = (data) => {
|
const duplicateWorkflow = (data) => {
|
||||||
data = JSON.parse(JSON.stringify(data));
|
//data = JSON.parse(JSON.stringify(data));
|
||||||
toast("Copying workflow " + data.name);
|
toast("Copying workflow '" + data.name + "'. The new workflow will load in and be highlighted.");
|
||||||
data.id = "";
|
//data.id = "";
|
||||||
data.name = data.name + "_copy";
|
//data.name = data.name + "_copy";
|
||||||
data = deduplicateIds(data, true);
|
//data = deduplicateIds(data, true);
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/workflows", {
|
const duplicateData = {
|
||||||
|
name: data.name + "_copy",
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`${globalUrl}/api/v1/workflows/${data.id}/duplicate`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(duplicateData),
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@@ -1660,7 +1683,20 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
if (responseJson.reason !== undefined) {
|
||||||
|
toast("Failed copying workflow: " + responseJson.reason)
|
||||||
|
} else {
|
||||||
|
toast("Failed copying workflow")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseJson.id !== undefined) {
|
||||||
|
setHighlightIds([responseJson.id])
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getAvailableWorkflows();
|
getAvailableWorkflows();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -1689,15 +1725,68 @@ const Workflows = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const sideloadWorkflow = (id, openEdit) => {
|
const exportSingleWorkflow = (data, setOpen) => {
|
||||||
|
setExportModalOpen(true)
|
||||||
|
|
||||||
|
if (data.triggers !== null && data.triggers !== undefined) {
|
||||||
|
var newSubflows = [];
|
||||||
|
for (var key in data.triggers) {
|
||||||
|
const trigger = data.triggers[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
trigger.parameters !== null &&
|
||||||
|
trigger.parameters !== undefined
|
||||||
|
) {
|
||||||
|
for (var subkey in trigger.parameters) {
|
||||||
|
const param = trigger.parameters[subkey];
|
||||||
|
if (
|
||||||
|
param.name === "workflow" &&
|
||||||
|
param.value !== data.id &&
|
||||||
|
!newSubflows.includes(param.value)
|
||||||
|
) {
|
||||||
|
newSubflows.push(param.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedworkflows = [];
|
||||||
|
for (var key in newSubflows) {
|
||||||
|
const foundWorkflow = workflows.find(
|
||||||
|
(workflow) => workflow.id === newSubflows[key]
|
||||||
|
);
|
||||||
|
if (foundWorkflow !== undefined && foundWorkflow !== null) {
|
||||||
|
parsedworkflows.push(foundWorkflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedworkflows.length > 0) {
|
||||||
|
console.log(
|
||||||
|
"Appending subflows during export: ",
|
||||||
|
parsedworkflows.length
|
||||||
|
);
|
||||||
|
data.subflows = parsedworkflows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setExportData(data)
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sideloadWorkflow = (id, action, setOpen) => {
|
||||||
|
|
||||||
const storagewf = localStorage.getItem("workflows")
|
const storagewf = localStorage.getItem("workflows")
|
||||||
const storageWorkflows = JSON.parse(storagewf)
|
const storageWorkflows = JSON.parse(storagewf)
|
||||||
if (storageWorkflows === null || storageWorkflows === undefined || storageWorkflows.length === 0) {
|
if (storageWorkflows === null || storageWorkflows === undefined || storageWorkflows.length === 0) {
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < storageWorkflows.length; i++) {
|
for (var i = 0; i < storageWorkflows.length; i++) {
|
||||||
if (storageWorkflows[i].id === id) {
|
if (storageWorkflows[i].id === id) {
|
||||||
if (storageWorkflows[i].image !== "") {
|
if (storageWorkflows[i].image !== "" && storageWorkflows[i].image !== undefined && storageWorkflows[i].image !== null) {
|
||||||
return
|
|
||||||
|
if (action === undefined || action === null || action === "") {
|
||||||
|
console.log("RETURNING")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1718,8 +1807,16 @@ const Workflows = (props) => {
|
|||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (openEdit) {
|
if (responseJson.success !== false && responseJson.id !== undefined) {
|
||||||
setEditing(responseJson)
|
if (action === "edit") {
|
||||||
|
setEditing(responseJson)
|
||||||
|
} else if (action === "publish") {
|
||||||
|
setPublishModalOpen(true)
|
||||||
|
setSelectedWorkflow(responseJson)
|
||||||
|
} else if (action === "export") {
|
||||||
|
exportSingleWorkflow(responseJson, setOpen)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < storageWorkflows.length; i++) {
|
for (var i = 0; i < storageWorkflows.length; i++) {
|
||||||
@@ -1730,8 +1827,9 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setWorkflows(storageWorkflows)
|
//setWorkflows(storageWorkflows)
|
||||||
//setFilteredWorkflows(storageWorkflows)
|
setFilteredWorkflows(storageWorkflows)
|
||||||
|
//setUpdate(Math.random())
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error.toString())
|
console.log(error.toString())
|
||||||
@@ -1878,7 +1976,9 @@ const Workflows = (props) => {
|
|||||||
const appGroup = getWorkflowAppgroup(data)
|
const appGroup = getWorkflowAppgroup(data)
|
||||||
const [triggers, subflows] = getWorkflowMeta(data)
|
const [triggers, subflows] = getWorkflowMeta(data)
|
||||||
|
|
||||||
const isDistributed = data.suborg_distribution !== undefined && data.suborg_distribution !== null && data.suborg_distribution.includes(userdata.active_org.id)
|
const hasSuborgs = data.suborg_distribution !== undefined && data.suborg_distribution !== null && data.suborg_distribution.length > 0
|
||||||
|
const isDistributed = (data.parentorg_workflow !== undefined && data.parentorg_workflow !== null && data.parentorg_workflow.length > 0) //|| (data.org_id !== userdata.active_org.id && data.org_id !== undefined && data.org_id !== null && data.org_id.length > 0)
|
||||||
|
|
||||||
const workflowMenuButtons = (
|
const workflowMenuButtons = (
|
||||||
<Menu
|
<Menu
|
||||||
id="long-menu"
|
id="long-menu"
|
||||||
@@ -1890,8 +1990,20 @@ const Workflows = (props) => {
|
|||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{isDistributed ?
|
||||||
|
<MenuItem
|
||||||
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/workflows/${data.id}`)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VisibilityIcon style={{ marginLeft: 0, marginRight: 8 }} />
|
||||||
|
Explore Workflow
|
||||||
|
</MenuItem>
|
||||||
|
: null}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0 && data.image !== "") {
|
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0 && data.image !== "") {
|
||||||
@@ -1899,7 +2011,9 @@ const Workflows = (props) => {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
//toast("Need to side-load workflow to be edited properly")
|
//toast("Need to side-load workflow to be edited properly")
|
||||||
sideloadWorkflow(data.id, true)
|
sideloadWorkflow(data.id, "edit")
|
||||||
|
|
||||||
|
toast.info("Loading full workflow for editing. Please wait...")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
key={"change"}
|
key={"change"}
|
||||||
@@ -1909,9 +2023,11 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedWorkflow(data);
|
sideloadWorkflow(data.id, "publish")
|
||||||
setPublishModalOpen(true);
|
|
||||||
|
toast.info("Loading full workflow for publishing. Please wait...")
|
||||||
}}
|
}}
|
||||||
key={"publish"}
|
key={"publish"}
|
||||||
>
|
>
|
||||||
@@ -1920,9 +2036,10 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyWorkflow(data);
|
duplicateWorkflow(data)
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
key={"duplicate"}
|
key={"duplicate"}
|
||||||
>
|
>
|
||||||
@@ -1931,52 +2048,11 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setExportModalOpen(true);
|
sideloadWorkflow(data.id, "export", setOpen)
|
||||||
|
|
||||||
if (data.triggers !== null && data.triggers !== undefined) {
|
toast.info("Loading full workflow to be exported. Please wait...")
|
||||||
var newSubflows = [];
|
|
||||||
for (var key in data.triggers) {
|
|
||||||
const trigger = data.triggers[key];
|
|
||||||
|
|
||||||
if (
|
|
||||||
trigger.parameters !== null &&
|
|
||||||
trigger.parameters !== undefined
|
|
||||||
) {
|
|
||||||
for (var subkey in trigger.parameters) {
|
|
||||||
const param = trigger.parameters[subkey];
|
|
||||||
if (
|
|
||||||
param.name === "workflow" &&
|
|
||||||
param.value !== data.id &&
|
|
||||||
!newSubflows.includes(param.value)
|
|
||||||
) {
|
|
||||||
newSubflows.push(param.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsedworkflows = [];
|
|
||||||
for (var key in newSubflows) {
|
|
||||||
const foundWorkflow = workflows.find(
|
|
||||||
(workflow) => workflow.id === newSubflows[key]
|
|
||||||
);
|
|
||||||
if (foundWorkflow !== undefined && foundWorkflow !== null) {
|
|
||||||
parsedworkflows.push(foundWorkflow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedworkflows.length > 0) {
|
|
||||||
console.log(
|
|
||||||
"Appending subflows during export: ",
|
|
||||||
parsedworkflows.length
|
|
||||||
);
|
|
||||||
data.subflows = parsedworkflows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setExportData(data);
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
}}
|
||||||
key={"export"}
|
key={"export"}
|
||||||
>
|
>
|
||||||
@@ -1985,6 +2061,7 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDeleteModalOpen(true);
|
setDeleteModalOpen(true);
|
||||||
setSelectedWorkflowId(data.id);
|
setSelectedWorkflowId(data.id);
|
||||||
@@ -2076,7 +2153,7 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{width: "100%", minWidth: 321, position: "relative", border: isDistributed ? "2px solid #40E0D0" : "inherit", borderRadius: theme.palette.borderRadius, }}>
|
<div style={{width: "100%", minWidth: 321, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "2px solid #40E0D0" : "inherit", borderRadius: theme.palette.borderRadius, }}>
|
||||||
<Paper square style={paperAppStyle}>
|
<Paper square style={paperAppStyle}>
|
||||||
{selectedCategory !== "" ?
|
{selectedCategory !== "" ?
|
||||||
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
|
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
|
||||||
@@ -2462,6 +2539,8 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
var workflowids = []
|
||||||
|
|
||||||
// Waits for the read
|
// Waits for the read
|
||||||
reader.addEventListener("load", (event) => {
|
reader.addEventListener("load", (event) => {
|
||||||
var data = reader.result;
|
var data = reader.result;
|
||||||
@@ -2497,7 +2576,8 @@ const Workflows = (props) => {
|
|||||||
data.org_id = userdata.active_org.id
|
data.org_id = userdata.active_org.id
|
||||||
data.org = []
|
data.org = []
|
||||||
data.execution_org = {}
|
data.execution_org = {}
|
||||||
|
|
||||||
|
workflowids.push(data.id)
|
||||||
|
|
||||||
// Actually create it
|
// Actually create it
|
||||||
setNewWorkflow(
|
setNewWorkflow(
|
||||||
@@ -2528,6 +2608,10 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoadWorkflowsModalOpen(false);
|
setLoadWorkflowsModalOpen(false);
|
||||||
|
|
||||||
|
if (workflowids.length > 0) {
|
||||||
|
setHighlightIds(workflowids)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWorkflowMeta = (data) => {
|
const getWorkflowMeta = (data) => {
|
||||||
@@ -3682,7 +3766,7 @@ const Workflows = (props) => {
|
|||||||
workflowDelay += 75
|
workflowDelay += 75
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Grid key={index} item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}>
|
<Grid key={index} item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px", }}>
|
||||||
<WorkflowPaper key={index} data={data} />
|
<WorkflowPaper key={index} data={data} />
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1897,87 +1897,87 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
||||||
if request.Body == nil {
|
if request.Body == nil {
|
||||||
log.Printf("[WARNING] (2) No body in request for workflowqueue")
|
log.Printf("[WARNING] (2) No body in request for workflowqueue")
|
||||||
resp.WriteHeader(http.StatusBadRequest)
|
resp.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
defer request.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(request.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[WARNING] (3) Failed reading body for workflowqueue")
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var actionResult shuffle.ActionResult
|
|
||||||
err = json.Unmarshal(body, &actionResult)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] Failed shuffle.ActionResult unmarshaling (2): %s", err)
|
|
||||||
//resp.WriteHeader(401)
|
|
||||||
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
||||||
//return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(actionResult.ExecutionId) == 0 {
|
|
||||||
log.Printf("[ERROR] No workflow execution id in action result. Data: %s", string(body))
|
|
||||||
resp.WriteHeader(400)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Get the shuffle.WorkflowExecution(ExecutionId) from the database
|
|
||||||
// 2. if shuffle.ActionResult.Authentication != shuffle.WorkflowExecution.Authentication -> exit
|
|
||||||
// 3. Add to and update actionResult in workflowExecution
|
|
||||||
// 4. Push to db
|
|
||||||
// IF FAIL: Set executionstatus: abort or cancel
|
|
||||||
ctx := context.Background()
|
|
||||||
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err)
|
|
||||||
resp.WriteHeader(500)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workflowExecution.Authorization != actionResult.Authorization {
|
|
||||||
log.Printf("[ERROR][%s] Bad authorization key when updating node (workflowQueue). Want: %s, Have: %s", actionResult.ExecutionId, workflowExecution.Authorization, actionResult.Authorization)
|
|
||||||
resp.WriteHeader(403)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workflowExecution.Status == "FINISHED" {
|
|
||||||
log.Printf("[DEBUG][%s] Workflowexecution is already FINISHED. No further action can be taken", workflowExecution.ExecutionId)
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is already finished because it has status %s. Lastnode: %s"}`, workflowExecution.Status, workflowExecution.LastNode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
|
|
||||||
log.Printf("[WARNING][%s] Workflowexecution already has status %s. No further action can be taken", workflowExecution.ExecutionId, workflowExecution.Status)
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
retries := 0
|
|
||||||
retry, retriesok := request.URL.Query()["retries"]
|
|
||||||
if retriesok && len(retry) > 0 {
|
|
||||||
val, err := strconv.Atoi(retry[0])
|
|
||||||
if err == nil {
|
|
||||||
retries = val
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[DEBUG][%s] Action: Received, Label: '%s', Action: '%s', Status: %s, Run status: %s, Extra=Retry:%d", workflowExecution.ExecutionId, actionResult.Action.Label, actionResult.Action.AppName, actionResult.Status, workflowExecution.Status, retries)
|
defer request.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] (3) Failed reading body for workflowqueue")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//results = append(results, actionResult)
|
var actionResult shuffle.ActionResult
|
||||||
//log.Printf("[INFO][%s] Time to execute %s (%s) with app %s:%s, function %s, env %s with %d parameters.", workflowExecution.ExecutionId, action.ID, action.Label, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters))
|
err = json.Unmarshal(body, &actionResult)
|
||||||
//log.Printf("[DEBUG][%s] In workflowQueue with transaction", workflowExecution.ExecutionId)
|
if err != nil {
|
||||||
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
log.Printf("[ERROR] Failed shuffle.ActionResult unmarshaling (2): %s", err)
|
||||||
|
//resp.WriteHeader(401)
|
||||||
|
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
//return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actionResult.ExecutionId) == 0 {
|
||||||
|
log.Printf("[ERROR] No workflow execution id in action result. Data: %s", string(body))
|
||||||
|
resp.WriteHeader(400)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Get the shuffle.WorkflowExecution(ExecutionId) from the database
|
||||||
|
// 2. if shuffle.ActionResult.Authentication != shuffle.WorkflowExecution.Authentication -> exit
|
||||||
|
// 3. Add to and update actionResult in workflowExecution
|
||||||
|
// 4. Push to db
|
||||||
|
// IF FAIL: Set executionstatus: abort or cancel
|
||||||
|
ctx := context.Background()
|
||||||
|
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err)
|
||||||
|
resp.WriteHeader(500)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowExecution.Authorization != actionResult.Authorization {
|
||||||
|
log.Printf("[ERROR][%s] Bad authorization key when updating node (workflowQueue). Want: %s, Have: %s", actionResult.ExecutionId, workflowExecution.Authorization, actionResult.Authorization)
|
||||||
|
resp.WriteHeader(403)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowExecution.Status == "FINISHED" {
|
||||||
|
log.Printf("[DEBUG][%s] Workflowexecution is already FINISHED. No further action can be taken", workflowExecution.ExecutionId)
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is already finished because it has status %s. Lastnode: %s"}`, workflowExecution.Status, workflowExecution.LastNode)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
|
||||||
|
log.Printf("[WARNING][%s] Workflowexecution already has status %s. No further action can be taken", workflowExecution.ExecutionId, workflowExecution.Status)
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
retries := 0
|
||||||
|
retry, retriesok := request.URL.Query()["retries"]
|
||||||
|
if retriesok && len(retry) > 0 {
|
||||||
|
val, err := strconv.Atoi(retry[0])
|
||||||
|
if err == nil {
|
||||||
|
retries = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG][%s] Action: Received, Label: '%s', Action: '%s', Status: %s, Run status: %s, Extra=Retry:%d", workflowExecution.ExecutionId, actionResult.Action.Label, actionResult.Action.AppName, actionResult.Status, workflowExecution.Status, retries)
|
||||||
|
|
||||||
|
//results = append(results, actionResult)
|
||||||
|
//log.Printf("[INFO][%s] Time to execute %s (%s) with app %s:%s, function %s, env %s with %d parameters.", workflowExecution.ExecutionId, action.ID, action.Label, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters))
|
||||||
|
//log.Printf("[DEBUG][%s] In workflowQueue with transaction", workflowExecution.ExecutionId)
|
||||||
|
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times
|
// Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times
|
||||||
@@ -2090,7 +2090,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
|
if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
|
||||||
log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions))
|
log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions))
|
||||||
//result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results))
|
//result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results))
|
||||||
|
|||||||
Reference in New Issue
Block a user