Fixed a bunch of minor issues with notification workflows and the like

This commit is contained in:
Frikky
2023-09-22 01:01:20 +02:00
parent fc9bc33b30
commit ec36639fa7
7 changed files with 227 additions and 46 deletions
+8 -9
View File
@@ -22,8 +22,7 @@ import dateutil
import threading import threading
import concurrent.futures import concurrent.futures
from io import StringIO as StringBuffer from io import StringIO as StringBuffer, BytesIO
from io import BytesIO
from liquid import Liquid, defaults from liquid import Liquid, defaults
runtime = os.getenv("SHUFFLE_SWARM_CONFIG", "") runtime = os.getenv("SHUFFLE_SWARM_CONFIG", "")
@@ -1296,7 +1295,7 @@ class AppBase:
def delete_cache(self, key): def delete_cache(self, key):
org_id = self.full_execution["workflow"]["execution_org"]["id"] org_id = self.full_execution["workflow"]["execution_org"]["id"]
url = "%s/api/v1/orgs/%s/delete_cache" % (self.url, org_id, key) url = "%s/api/v1/orgs/%s/delete_cache" % (self.url, org_id)
data = { data = {
"workflow_id": self.full_execution["workflow"]["id"], "workflow_id": self.full_execution["workflow"]["id"],
@@ -1309,11 +1308,11 @@ class AppBase:
response = requests.post(url, json=data, verify=False) response = requests.post(url, json=data, verify=False)
try: try:
allvalues = response.json() allvalues = response.json()
return allvalues return json.dumps(allvalues)
except Exception as e: except Exception as e:
self.logger.info("[ERROR} Failed to parse response from delete_cache: %s" % e) self.logger.info("[ERROR} Failed to parse response from delete_cache: %s" % e)
#return response.json() #return response.json()
return {"success": False, "reason": f"Failed to delete cache for key {key}"} return json.dumps({"success": False, "reason": f"Failed to delete cache for key '{key}'"})
def set_cache(self, key, value): def set_cache(self, key, value):
org_id = self.full_execution["workflow"]["execution_org"]["id"] org_id = self.full_execution["workflow"]["execution_org"]["id"]
@@ -2301,13 +2300,13 @@ class AppBase:
#if len(template) > 100: #if len(template) > 100:
# self.logger.info("[DEBUG] Running liquid with data of length %d" % len(template)) # self.logger.info("[DEBUG] Running liquid with data of length %d" % len(template))
#self.logger.info(f"[DEBUG] Data: {template}") #self.logger.info(f"[DEBUG] Data: {template}")
run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters)
# Can't handle self yet (?)
all_globals = globals() all_globals = globals()
all_globals["self"] = self all_globals["self"] = self
run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters, globals=all_globals)
ret = run.render(**all_globals) # Add locals that are missing to globals
ret = run.render()
return ret return ret
except jinja2.exceptions.TemplateNotFound as e: except jinja2.exceptions.TemplateNotFound as e:
self.logger.info(f"[ERROR] Liquid Template error: {e}") self.logger.info(f"[ERROR] Liquid Template error: {e}")
+1 -1
View File
@@ -1,7 +1,7 @@
urllib3==1.26.5 urllib3==1.26.5
requests==2.25.1 requests==2.25.1
MarkupSafe==2.0.1 MarkupSafe==2.0.1
liquidpy==0.7.6 liquidpy==0.8.1
flask[async]==2.0.2 flask[async]==2.0.2
waitress==2.1.0 waitress==2.1.0
#flask==1.1.2 #flask==1.1.2
+15
View File
@@ -39,6 +39,9 @@ import AlertTemplate from "./components/AlertTemplate";
import { useAlert, positions, Provider } from "react-alert"; import { useAlert, positions, Provider } from "react-alert";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Drift from "react-driftjs"; import Drift from "react-driftjs";
// Production - backend proxy forwarding in nginx // Production - backend proxy forwarding in nginx
@@ -561,6 +564,18 @@ const App = (message, props) => {
{includedData} {includedData}
</Provider> </Provider>
</BrowserRouter> </BrowserRouter>
<ToastContainer
position="bottom-center"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
theme="dark"
/>
</CookiesProvider> </CookiesProvider>
</ThemeProvider> </ThemeProvider>
); );
-1
View File
@@ -38,7 +38,6 @@ const OrgHeader = (props) => {
handleEditOrg, handleEditOrg,
} = props; } = props;
//const alert = useAlert();
const classes = useStyles(); const classes = useStyles();
var upload = ""; var upload = "";
+201 -33
View File
@@ -1,8 +1,8 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
import { useAlert } from "react-alert";
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { toast } from "react-toastify"
import { import {
FormControl, FormControl,
@@ -24,6 +24,7 @@ import {
Tab, Tab,
Grid, Grid,
IconButton, IconButton,
Autocomplete,
} from "@mui/material"; } from "@mui/material";
import { import {
@@ -48,7 +49,6 @@ const OrgHeaderexpanded = (props) => {
adminTab, adminTab,
} = props; } = props;
const alert = useAlert();
const classes = useStyles(); const classes = useStyles();
const defaultBranch = "master"; const defaultBranch = "master";
@@ -155,6 +155,47 @@ const OrgHeaderexpanded = (props) => {
: selectedOrganization.sso_config.openid_token : selectedOrganization.sso_config.openid_token
) )
const [workflows, setWorkflows] = React.useState([])
const [workflow, setWorkflow] = React.useState({})
const getAvailableWorkflows = (trigger_index) => {
fetch(globalUrl + "/api/v1/workflows", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson !== undefined) {
setWorkflows(responseJson)
if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) {
const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow)
if (workflow !== undefined && workflow !== null) {
setWorkflow(workflow)
}
}
}
})
.catch((error) => {
console.log("Error getting workflows: " + error);
})
}
useEffect(() => {
getAvailableWorkflows()
}, [])
const handleEditOrg = ( const handleEditOrg = (
name, name,
description, description,
@@ -188,17 +229,29 @@ const OrgHeaderexpanded = (props) => {
.then((response) => .then((response) =>
response.json().then((responseJson) => { response.json().then((responseJson) => {
if (responseJson["success"] === false) { if (responseJson["success"] === false) {
alert.error("Failed updating org: ", responseJson.reason); toast("Failed updating org: ", responseJson.reason);
} else { } else {
alert.success("Successfully edited org!"); toast("Successfully edited org!");
} }
}) })
) )
.catch((error) => { .catch((error) => {
alert.error("Err: " + error.toString()); toast("Err: " + error.toString());
}); });
}; };
const handleWorkflowSelectionUpdate = (e, isUserinput) => {
if (e.target.value === undefined || e.target.value === null || e.target.value.id === undefined) {
console.log("Returning as there's no id")
return null
}
setWorkflow(e.target.value)
setNotificationWorkflow(e.target.value.id)
toast("Updated notification workflow. Don't forget to save!")
}
const orgSaveButton = ( const orgSaveButton = (
<Tooltip title="Save any unsaved data" placement="bottom"> <Tooltip title="Save any unsaved data" placement="bottom">
<div> <div>
@@ -247,34 +300,149 @@ const OrgHeaderexpanded = (props) => {
<Grid container spacing={3} style={{ textAlign: "left" }}> <Grid container spacing={3} style={{ textAlign: "left" }}>
<Grid item xs={12} style={{}}> <Grid item xs={12} style={{}}>
<span> <span>
<Typography>Notification Workflow ID</Typography> <Typography>Notification Workflow</Typography>
<TextField {/*
required <Typography variant="body2" color="textSecondary">
style={{ Add a Workflow that receives notifications from Shuffle when an error occurs in one of your workflows
flex: "1", </Typography>
marginTop: "5px", */}
marginRight: "15px", <div style={{display: "flex", flexDirection: "row", alignItems: "center"}}>
backgroundColor: theme.palette.inputColor, {workflows !== undefined && workflows !== null && workflows.length > 0 ?
}} <Autocomplete
fullWidth={true} id="notification_workflow_search"
type="name" autoHighlight
id="outlined-with-placeholder" freeSolo
margin="normal" //autoSelect
variant="outlined" value={workflow}
placeholder="ID of the workflow to receive notifications" classes={{ inputRoot: classes.inputRoot }}
value={notificationWorkflow} ListboxProps={{
onChange={(e) => { style: {
setNotificationWorkflow(e.target.value); backgroundColor: theme.palette.inputColor,
}} color: "white",
InputProps={{ },
classes: { }}
notchedOutline: classes.notchedOutline, getOptionLabel={(option) => {
}, if (
style: { option === undefined ||
color: "white", option === null ||
}, option.name === undefined ||
}} option.name === null
/> ) {
return "No Workflow Selected";
}
const newname = (
option.name.charAt(0).toUpperCase() + option.name.substring(1)
).replaceAll("_", " ");
return newname;
}}
options={workflows}
fullWidth
style={{
backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette.borderRadius,
}}
onChange={(event, newValue) => {
console.log("Found value: ", newValue)
var parsedinput = { target: { value: newValue } }
// For variables
if (typeof newValue === 'string' && newValue.startsWith("$")) {
parsedinput = {
target: {
value: {
"name": newValue,
"id": newValue,
"actions": [],
"triggers": [],
}
}
}
}
handleWorkflowSelectionUpdate(parsedinput)
}}
renderOption={(props, data, state) => {
if (data.id === workflow.id) {
data = workflow;
}
return (
<Tooltip arrow placement="left" 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: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
: null}
<Typography>
Choose {data.name}
</Typography>
</span>
} placement="bottom">
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
color: data.id === workflow.id ? "red" : "white",
}}
value={data}
onClick={(e) => {
var parsedinput = { target: { value: data } }
handleWorkflowSelectionUpdate(parsedinput)
}}
>
{data.name}
</MenuItem>
</Tooltip>
)
}}
renderInput={(params) => {
return (
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
}}
{...params}
label="Find a notification workflow"
variant="outlined"
/>
);
}}
/>
:
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="ID of the workflow to receive notifications"
value={notificationWorkflow}
onChange={(e) => {
setNotificationWorkflow(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
}
<div style={{minWidth: 150, maxWidth: 150, marginTop: 5, marginLeft: 10, }}>
{orgSaveButton}
</div>
</div>
</span> </span>
</Grid> </Grid>
<Grid item xs={12} style={{}}> <Grid item xs={12} style={{}}>
+1 -1
View File
@@ -1131,7 +1131,7 @@ const ParsedAction = (props) => {
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
console.log("SELECT: ", event, newValue) console.log("SELECT: ", event, newValue)
// Workaround with event lol // Workaround with event lol
//if (newValue !== undefined && newValue !== null) { //if (newValue !== undefined && newValue !== null) {
// setNewSelectedAction({ target: { value: newValue.name } }); // setNewSelectedAction({ target: { value: newValue.name } });
+1 -1
View File
@@ -2718,7 +2718,7 @@ const Apps = (props) => {
</DialogTitle> </DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}> <DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<Typography variant="body1"> <Typography variant="body1">
Paste in a URL, and we will make it into an app for you. This may take multiple minutes based on the size of the documentation. {isCloud ? "" : "Uses to Shuffle Cloud (https://shuffler.io) for processing."} Paste in a URL, and we will make it into an app for you. This may take multiple minutes based on the size of the documentation. <b>{isCloud ? "" : "Uses Shuffle Cloud (https://shuffler.io) for processing (for now)."}</b>
</Typography> </Typography>
<TextField <TextField
style={{ backgroundColor: inputColor }} style={{ backgroundColor: inputColor }}