Added statistics to both backend and frontend + cloud sync

This commit is contained in:
Frikky
2023-10-17 02:40:32 +02:00
parent 0579f60b58
commit 595c217a7c
32 changed files with 4035 additions and 1753 deletions
+2 -2
View File
@@ -71,8 +71,8 @@ SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default
# the container to the docker0 # the container to the docker0
SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0 SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0
# Used for auto-cleanup of containers. REALLY important at scale. # Used for auto-cleanup of containers. REALLY important at scale. Set to false to see all container info.
SHUFFLE_CONTAINER_AUTO_CLEANUP=false SHUFFLE_CONTAINER_AUTO_CLEANUP=true
SHUFFLE_ELASTIC=true SHUFFLE_ELASTIC=true
SHUFFLE_LOGS_DISABLED=false SHUFFLE_LOGS_DISABLED=false
SHUFFLE_CHAT_DISABLED=false # Controls support chat SHUFFLE_CHAT_DISABLED=false # Controls support chat
+1 -1
View File
@@ -1,6 +1,6 @@
module shuffle-shared module shuffle-shared
//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
go 1.19 go 1.19
-58
View File
@@ -700,64 +700,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
} }
} }
/*
// Removed as UserInput is now handled as an app
if actionResult.Status == "WAITING" && actionResult.Action.AppName == "User Input" {
log.Printf("[INFO] SHOULD WAIT A BIT AND RUN USER INPUT! WAITING!")
var trigger shuffle.Trigger
err = json.Unmarshal([]byte(actionResult.Result), &trigger)
if err != nil {
log.Printf("[WARNING] Failed unmarshaling actionresult for user input: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
orgId := workflowExecution.ExecutionOrg
if len(workflowExecution.OrgId) == 0 && len(workflowExecution.Workflow.OrgId) > 0 {
orgId = workflowExecution.Workflow.OrgId
}
err := handleUserInput(trigger, orgId, workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
if err != nil {
log.Printf("[WARNING] Failed userinput handler: %s", err)
actionResult.Result = fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)
workflowExecution.Results = append(workflowExecution.Results, actionResult)
workflowExecution.Status = "ABORTED"
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
if err != nil {
log.Printf("[WARNING] Failed to set execution during wait: %s", err)
} else {
log.Printf("[INFO] Successfully set the execution %s to waiting.", workflowExecution.ExecutionId)
}
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error: %s"}`, err)))
return
} else {
log.Printf("[INFO] Successful userinput handler")
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "CLOUD IS DONE"}`)))
actionResult.Result = `{"success": True, "reason": "Waiting for user feedback based on configuration"}`
workflowExecution.Results = append(workflowExecution.Results, actionResult)
workflowExecution.Status = actionResult.Status
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
if err != nil {
log.Printf("[WARNING] Failed setting userinput: %s", err)
} else {
log.Printf("[DEBUG] Successfully set the execution to waiting.")
}
}
return
}
*/
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp) runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
} }
-2
View File
@@ -1856,8 +1856,6 @@ const AppFramework = (props) => {
//autounselectify={true} //autounselectify={true}
var usecasediff = -100 var usecasediff = -100
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
console.log("Background: ", bgColor)
return ( return (
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}> <div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
<div style={{position: "absolute"}}> <div style={{position: "absolute"}}>
+1 -1
View File
@@ -200,7 +200,7 @@ const Appsearch = props => {
const CustomHits = connectHits(InputHits) const CustomHits = connectHits(InputHits)
return ( return (
<div style={{width: "100%", textAlign: "center", position: "relative", height: "100%",}}> <div style={{width: 287, height: 295, padding: "16px 16px 267px 16px", alignItems: "center", gap: 138,}}>
<InstantSearch searchClient={searchClient} indexName="appsearch"> <InstantSearch searchClient={searchClient} indexName="appsearch">
{/* showSearch === false ? null : {/* showSearch === false ? null :
<div style={{maxWidth: 450, margin: "auto", }}> <div style={{maxWidth: 450, margin: "auto", }}>
@@ -18,7 +18,6 @@ import {
Grid, Grid,
Paper, Paper,
Typography, Typography,
TextField,
Zoom, Zoom,
} from "@mui/material"; } from "@mui/material";
@@ -294,9 +294,6 @@ const AuthenticationData = (props) => {
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em", fontSize: "1em",
}, },
disableUnderline: true, disableUnderline: true,
@@ -386,7 +383,6 @@ const AuthenticationData = (props) => {
PaperProps={{ PaperProps={{
style: { style: {
pointerEvents: "auto", pointerEvents: "auto",
backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
minWidth: 600, minWidth: 600,
minHeight: 600, minHeight: 600,
@@ -441,9 +437,6 @@ const AuthenticationData = (props) => {
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em", fontSize: "1em",
}, },
}} }}
File diff suppressed because it is too large Load Diff
+280
View File
@@ -0,0 +1,280 @@
import React, { useState, useEffect } from 'react';
import classNames from "classnames";
import theme from '../theme.jsx';
import {
Tooltip,
TextField,
IconButton,
Button,
Typography,
Grid,
Paper,
Chip,
Checkbox,
} from "@mui/material";
import {
BarChart,
RadialBarChart,
RadialAreaChart,
RadialAxis,
StackedBarSeries,
TooltipArea,
ChartTooltip,
TooltipTemplate,
RadialAreaSeries,
RadialPointSeries,
RadialArea,
RadialLine,
TreeMap,
TreeMapSeries,
TreeMapLabel,
TreeMapRect,
Line,
LineChart,
LineSeries,
LinearYAxis,
LinearXAxis,
LinearYAxisTickSeries,
LinearXAxisTickSeries,
Area,
AreaChart,
AreaSeries,
AreaSparklineChart,
PointSeries,
GridlineSeries,
Gridline,
Stripes,
Gradient,
GradientStop,
LinearXAxisTickLabel,
} from 'reaviz';
const LineChartWrapper = ({keys, inputname, height, width}) => {
const [hovered, setHovered] = useState("");
const inputdata = keys.data === undefined ? keys : keys.data
return (
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, }}>
<Typography variant="h6" style={{marginBotton: 15, }}>
{inputname}
</Typography>
<BarChart
width={"100%"}
height={height}
data={inputdata}
gridlines={
<GridlineSeries line={<Gridline direction="all" />} />
}
/>
</div>
)
}
const AppStats = (defaultprops) => {
const { globalUrl, selectedOrganization, userdata, } = defaultprops;
const [keys, setKeys] = useState([])
const [searches, setSearches] = useState([]);
const [clickData, setClickData] = useState(undefined);
const [conversionData, setConversionData] = useState(undefined);
const [statistics, setStatistics] = useState(undefined);
const [appRuns, setAppruns] = useState(undefined);
const [workflowRuns, setWorkflowRuns] = useState(undefined);
const [subflowRuns, setSubflowRuns] = useState(undefined);
const handleDataSetting = (inputdata, grouping) => {
if (inputdata === undefined || inputdata === null) {
return
}
const dailyStats = inputdata.daily_statistics
if (dailyStats === undefined || dailyStats === null) {
return
}
console.log("Looking at daily data: ", inputdata)
var appRuns = {
"key": "App Runs",
"data": []
}
var workflowRuns = {
"key": "Workflow Runs (includes subflows)",
"data": []
}
var subflowRuns = {
"key": "Subflow Runs",
"data": []
}
for (let key in dailyStats) {
// Always skips first one as it has accumulated data in it
if (key === 0) {
continue
}
const item = dailyStats[key]
if (item["date"] === undefined) {
console.log("No date: ", item)
continue
}
// Check if app_executions key in item
if (item["app_executions"] !== undefined && item["app_executions"] !== null) {
appRuns["data"].push({
key: new Date(item["date"]),
data: item["app_executions"]
})
}
// Check if workflow_executions key in item
if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
workflowRuns["data"].push({
key: new Date(item["date"]),
data: item["workflow_executions"]
})
}
if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) {
subflowRuns["data"].push({
key: new Date(item["date"]),
data: item["subflow_executions"]
})
}
}
// Adds data for today
console.log("Inputdata: ", inputdata)
if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
appRuns["data"].push({
key: new Date(),
data: inputdata["daily_app_executions"]
})
}
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
workflowRuns["data"].push({
key: new Date(),
data: inputdata["daily_workflow_executions"]
})
}
if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) {
subflowRuns["data"].push({
key: new Date(),
data: inputdata["daily_subflow_executions"]
})
}
setSubflowRuns(subflowRuns)
setWorkflowRuns(workflowRuns)
setAppruns(appRuns)
}
const getStats = () => {
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
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!: ", response.status);
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson["success"] === false) {
return
}
setStatistics(responseJson)
handleDataSetting(responseJson, "day")
})
.catch((error) => {
console.log("error: ", error)
});
}
useEffect(() => {
getStats()
}, [])
const paperStyle = {
textAlign: "center",
padding: 40,
margin: 5,
backgroundColor: theme.palette.surfaceColor,
maxWidth: 300,
}
const data = (
<div className="content" style={{width: "100%", margin: "auto", }}>
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }}>
All Stat widgets are monthly and gathered from <a
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
target="_blank"
style={{ textDecoration: "none", color: "#f85a3e",}}
>Your Organization Statistics. </a>
This is a feature to help give you more insight into Shuffle, and will be populating over time.
</Typography>
{statistics !== undefined ?
<div style={{display: "flex", textAlign: "center",}}>
<Paper style={paperStyle}>
<Typography variant="h4">
{statistics.monthly_workflow_executions}
</Typography>
<Typography variant="h6">
Workflow Runs
</Typography>
</Paper>
<Paper style={paperStyle}>
<Typography variant="h4">
{statistics.monthly_app_executions}
</Typography>
<Typography variant="h6">
App Runs
</Typography>
</Paper>
</div>
: null}
{appRuns === undefined ?
null
:
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"Daily App Runs"}/>
}
{workflowRuns === undefined ?
null
:
<LineChartWrapper keys={workflowRuns} height={300} width={"100%"} inputname={"Daily Workflow Runs (including subflows)"}/>
}
{subflowRuns === undefined ?
null
:
<LineChartWrapper keys={subflowRuns} height={300} width={"100%"} inputname={"Subflow Runs"}/>
}
</div>
)
const dataWrapper = (
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div>
);
return dataWrapper;
}
export default AppStats;
File diff suppressed because it is too large Load Diff
+38 -7
View File
@@ -5,6 +5,7 @@ import { MuiChipsInput } from "mui-chips-input";
import UsecaseSearch from "../components/UsecaseSearch.jsx" import UsecaseSearch from "../components/UsecaseSearch.jsx"
import WorkflowGrid from "../components/WorkflowGrid.jsx" import WorkflowGrid from "../components/WorkflowGrid.jsx"
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx";
import { import {
Badge, Badge,
@@ -57,7 +58,7 @@ import {
} from "@mui/icons-material"; } from "@mui/icons-material";
const EditWorkflow = (props) => { const EditWorkflow = (props) => {
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, } = props
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
@@ -143,7 +144,9 @@ const EditWorkflow = (props) => {
return null return null
} }
const newWorkflow = isEditing === true ? false : true const newWorkflow = isEditing === true ? false : true
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
console.log("PRIO: ", priority)
var upload = ""; var upload = "";
var total_count = 0 var total_count = 0
@@ -161,6 +164,7 @@ const EditWorkflow = (props) => {
maxWidth: isMobile ? "90%" : 650, maxWidth: isMobile ? "90%" : 650,
minHeight: 400, minHeight: 400,
paddingTop: 25, paddingTop: 25,
paddingLeft: 50,
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
}, },
@@ -170,7 +174,7 @@ const EditWorkflow = (props) => {
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
<div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}> <div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}>
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
<Typography variant="h6" style={{flex: 9, }}> <Typography variant="h4" style={{flex: 9, }}>
{newWorkflow ? "New" : "Editing"} workflow {newWorkflow ? "New" : "Editing"} workflow
</Typography> </Typography>
{newWorkflow === true ? null : {newWorkflow === true ? null :
@@ -193,7 +197,7 @@ const EditWorkflow = (props) => {
</div> </div>
} }
</div> </div>
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}> <Typography variant="body2" color="textSecondary" style={{marginTop: 20, maxWidth: 440,}}>
Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a> Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
</Typography> </Typography>
{showUpload === true ? {showUpload === true ?
@@ -480,7 +484,7 @@ const EditWorkflow = (props) => {
</DialogContent> </DialogContent>
<DialogActions style={{paddingRight: 20, }}> <DialogActions style={{paddingRight: 100, }}>
<Button <Button
style={{}} style={{}}
onClick={() => { onClick={() => {
@@ -542,11 +546,38 @@ const EditWorkflow = (props) => {
}} }}
color="primary" color="primary"
> >
{submitLoading ? <CircularProgress color="secondary" /> : "Submit"} {submitLoading ? <CircularProgress color="secondary" /> : "Done"}
</Button> </Button>
</DialogActions> </DialogActions>
{newWorkflow === true ?
<span style={{marginTop: 30, }}>
<Typography variant="h6" style={{marginLeft: 30, paddingBottom: 0, }}>
Relevant Workflows
</Typography>
{newWorkflow === true && name.length > 5 ? {priority === null || priority === undefined ? null :
<div style={{marginLeft: 30, }}>
<WorkflowTemplatePopup
userdata={userdata}
globalUrl={globalUrl}
srcapp={priority.description.split("&").length > 2 ? priority.description.split("&")[0] : ""}
img1={priority.description.split("&").length > 2 ? priority.description.split("&")[1] : ""}
dstapp={priority.description.split("&").length > 3 ? priority.description.split("&")[2] : ""}
img2={priority.description.split("&").length > 3 ? priority.description.split("&")[3] : ""}
title={priority.name}
description={priority.description.split("&").length > 4 ? priority.description.split("&")[4] : ""}
apps={apps}
/>
</div>
}
</span>
: null}
{newWorkflow === true && name.length > 2 ?
<div style={{marginLeft: 30, }}> <div style={{marginLeft: 30, }}>
<WorkflowGrid <WorkflowGrid
maxRows={1} maxRows={1}
+324
View File
@@ -0,0 +1,324 @@
import React, { useState, useEffect } from "react";
import ReactGA from 'react-ga4';
import 'react-alice-carousel/lib/alice-carousel.css';
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
import theme from '../theme.jsx';
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
import {
Checkbox,
Button,
Collapse,
IconButton,
FormGroup,
FormControl,
InputLabel,
FormLabel,
FormControlLabel,
Select,
MenuItem,
Grid,
Paper,
Typography,
TextField,
Zoom,
List,
ListItem,
ListItemText,
Divider,
Tooltip,
Chip,
ButtonGroup,
Dialog,
DialogTitle,
DialogActions,
DialogContent,
} from "@mui/material";
import { useNavigate, Link } from "react-router-dom";
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
const ExploreWorkflow = (props) => {
const { userdata, globalUrl } = props
const [activeUsecases, setActiveUsecases] = useState(0);
const [modalOpen, setModalOpen] = React.useState(false);
const [suggestedUsecases, setSuggestedUsecases] = useState([])
const [usecasesSet, setUsecasesSet] = useState(false)
const [apps, setApps] = useState([])
const sizing = 475
let navigate = useNavigate();
const imagestyle = {
height: 40,
borderRadius: 40,
//border: "2px solid rgba(255,255,255,0.3)",
}
const loadApps = () => {
fetch(`${globalUrl}/api/v1/apps`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
return response.json();
})
.then((responseJson) => {
if (responseJson === null) {
console.log("null-response from server")
const pretend_apps = [{
"name": "TBD",
"app_name": "TBD",
"app_version": "TBD",
"description": "TBD",
"version": "TBD",
"large_image": "",
}]
setApps(pretend_apps)
return
}
if (responseJson.success === false) {
console.log("error loading apps: ", responseJson)
return
}
setApps(responseJson);
})
.catch((error) => {
console.log("App loading error: " + error.toString());
})
}
// Find priorities in userdata.priorities and check if the item.type === "usecase"
// If so, set the item.isActive to true
if (usecasesSet === false && userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && suggestedUsecases.length === 0) {
var tmpUsecases = []
for (let i = 0; i < userdata.priorities.length; i++) {
if (userdata.priorities[i].type !== "usecase" || userdata.priorities[i].active === false) {
continue
}
tmpUsecases.push(userdata.priorities[i])
}
setSuggestedUsecases(tmpUsecases)
setUsecasesSet(true)
loadApps()
}
const modalView = (
// console.log("key:", dataValue.key),
//console.log("value:",dataValue.value),
<Dialog
open={modalOpen}
onClose={() => {
setModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
<DialogTitle style={{}}>
<div style={{ color: "white", display: "flex", alignItems: "center", justifyContent: "center" }}>
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Sign Up</span>
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginLeft: 8, marginRight: 8 }} />
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Setup</span>
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginRight: 8 }} />
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Explore</span>
</div>
</DialogTitle>
<Typography style={{ fontSize: 16, width: 252, marginLeft: 167 }}>
Heres a recommended workflow:
</Typography>
{/* <div style={{ marginTop: 0, maxWidth: 700, minWidth: 700, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
<div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}>
<Tooltip title={"Previous usecase"}>
<IconButton
style={{
// backgroundColor: thumbIndex === 0 ? "inherit" : "white",
zIndex: 5000,
minHeight: 50,
maxHeight: 50,
color: "grey",
marginTop: 150,
borderRadius: 50,
border: "1px solid rgba(255,255,255,0.3)",
}}
onClick={() => {
slidePrev()
}}
>
<ArrowBackIosNewIcon />
</IconButton>
</Tooltip>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
<AliceCarousel
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
items={formattedCarousel}
activeIndex={thumbIndex}
infiniteLoop
mouseTracking={false}
responsive={responsive}
// activeIndex={activeIndex}
controlsStrategy="responsive"
autoPlay={false}
infinite={true}
animationType="fadeout"
animationDuration={800}
disableButtonsControls
/>
</div>
<Tooltip title={"Next usecase"}>
<IconButton
style={{
backgroundColor: thumbIndex === usecaseButtons.length - 1 ? "inherit" : "white",
zIndex: 5000,
minHeight: 50,
maxHeight: 50,
color: "grey",
marginTop: 150,
borderRadius: 50,
border: "1px solid rgba(255,255,255,0.3)",
}}
onClick={() => {
slideNext()
}}
>
<ArrowForwardIosIcon />
</IconButton>
</Tooltip>
</div>
</div>
</div> */}
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
<Button
style={{ borderRadius: "0px" }}
onClick={() => setModalOpen(false)}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "0px" }}
onClick={() => {
console.log("hello")
}}
color="primary"
>
Submit
</Button>
</DialogActions>
</Dialog>
);
return (
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
{modalView}
<Typography variant="h4" style={{ marginLeft: 8, marginTop: 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
Start using workflows
</Typography>
<Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)">
Based on what you selected heres our recommendations!
</Typography>
<div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
<Grid item xs={11} style={{}}>
{suggestedUsecases.length === 0 && usecasesSet ?
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
All Workflows are already added for your current apps!
</Typography>
:
suggestedUsecases.map((priority, index) => {
const srcapp = priority.description.split("&")[0]
var image1 = priority.description.split("&")[1]
var image2 = ""
var dstapp = ""
if (priority.description.split("&").length > 3) {
dstapp = priority.description.split("&")[2]
image2 = priority.description.split("&")[3]
}
const name = priority.name.replace("Suggested Usecase: ", "")
var description = ""
if (priority.description.split("&").length > 4) {
description = priority.description[4]
}
// FIXME: Should have a proper description
description = ""
return (
<WorkflowTemplatePopup
userdata={userdata}
globalUrl={globalUrl}
img1={image1}
srcapp={srcapp}
img2={image2}
dstapp={dstapp}
title={name}
description={description}
apps={apps}
/>
)
})}
</Grid>
<div>
<div style={{ marginTop: 32 }}>
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
<Button variant="contained" type="submit"
fullWidth style={{
borderRadius: 200,
height: 51,
width: 464,
fontSize: 16,
padding: "16px 24px",
margin: "auto",
itemAlign: "center",
background: activeUsecases === 0 ? "rgba(47, 47, 47, 1)" : "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
color: activeUsecases === 0? "rgba(158, 158, 158, 1)" : "rgba(241, 241, 241, 1)",
border: activeUsecases === 0 ? "1px solid rgba(158, 158, 158, 1)" : "none",
}}
onClick={() => {
navigate("/workflows?message="+activeUsecases+" workflows added")
}}>
Continue to workflows
</Button>
</Typography>
</div>
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
<Link style={{ color: "#f86a3e", marginLeft: 145 }} to="/usecases" className="btn btn-primary">
Explore usecases
</Link>
</Typography>
</div>
</div>
</div>
</div>
</div>
)
}
export default ExploreWorkflow
+4 -3
View File
@@ -212,7 +212,8 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
const notificationWidth = 300 const notificationWidth = 300
const imagesize = 22; const imagesize = 22;
const boxColor = "#86c142"; const boxColor = "#86c142";
const NotificationItem = (props) => { const NotificationItem = (props) => {
const {data} = props const {data} = props
@@ -263,7 +264,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
{data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ? {data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ?
<Link to={data.reference_url} style={{color: "#f86a3e", textDecoration: "none",}}> <Link to={data.reference_url} style={{color: "#f86a3e", textDecoration: "none",}}>
<Typography variant="body1"> <Typography variant="body1">
{data.title} {data.title} ({data.amount})
</Typography > </Typography >
</Link> </Link>
: :
@@ -277,7 +278,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
: :
null null
} }
<Typography variant="body2"> <Typography variant="body2" style={{maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
{data.description} {data.description}
</Typography > </Typography >
{/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ? {/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ?
+24 -24
View File
@@ -2278,30 +2278,30 @@ const ParsedAction = (props) => {
handleItemClick([innerdata]); handleItemClick([innerdata]);
}} }}
> >
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-455, padding: "10px 0px 10px 10px", backgroundColor: theme.palette.inputColor, overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}> <Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-455, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
<MenuItem <MenuItem
key={innerdata.name} key={innerdata.name}
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
marginLeft: 15, marginLeft: 15,
color: "white", color: "white",
minWidth: 250, minWidth: 250,
maxWidth: 250, maxWidth: 250,
padding: 0, padding: 0,
position: "relative", position: "relative",
}} }}
value={innerdata} value={innerdata}
onMouseOver={() => { onMouseOver={() => {
//console.log("HOVER: ", pathdata); //console.log("HOVER: ", pathdata);
}} }}
onClick={() => { onClick={() => {
handleItemClick([innerdata]); handleItemClick([innerdata]);
}} }}
> >
<Typography variant="h6" style={{paddingBottom: 5}}> <Typography variant="h6" style={{paddingBottom: 5}}>
{innerdata.name} {innerdata.name}
</Typography> </Typography>
</MenuItem> </MenuItem>
{parsedPaths.map((pathdata, index) => { {parsedPaths.map((pathdata, index) => {
// FIXME: Should be recursive in here // FIXME: Should be recursive in here
//<VpnKeyIcon style={iconStyle} /> //<VpnKeyIcon style={iconStyle} />
+1 -1
View File
@@ -69,7 +69,7 @@ const Priority = (props) => {
return ( return (
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: 100, maxHeight: 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}> <div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
<div style={{flex: 2, overflow: "hidden",}}> <div style={{flex: 2, overflow: "hidden",}}>
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null} {priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
+27 -15
View File
@@ -26,22 +26,21 @@ import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx";
import { import {
FullscreenExit as FullscreenExitIcon, FullscreenExit as FullscreenExitIcon,
Extension as ExtensionIcon, Extension as ExtensionIcon,
Apps as AppsIcon, Apps as AppsIcon,
FavoriteBorder as FavoriteBorderIcon, FavoriteBorder as FavoriteBorderIcon,
Schedule as ScheduleIcon, Schedule as ScheduleIcon,
FormatListNumbered as FormatListNumberedIcon, FormatListNumbered as FormatListNumberedIcon,
SquareFoot as SquareFootIcon, SquareFoot as SquareFootIcon,
Circle as CircleIcon, Circle as CircleIcon,
Add as AddIcon, Add as AddIcon,
PlayArrow as PlayArrowIcon, PlayArrow as PlayArrowIcon,
} from '@mui/icons-material';
import {
AutoFixHigh as AutoFixHighIcon, AutoFixHigh as AutoFixHighIcon,
Close as CloseIcon,
CompressOutlined, CompressOutlined,
QrCodeScannerOutlined, QrCodeScannerOutlined,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { validateJson } from "../views/Workflows.jsx"; import { validateJson } from "../views/Workflows.jsx";
import ReactJson from "react-json-view"; import ReactJson from "react-json-view";
import PaperComponent from "../components/PaperComponent.jsx"; import PaperComponent from "../components/PaperComponent.jsx";
@@ -823,10 +822,6 @@ const CodeEditor = (props) => {
} }
const executeSingleAction = (inputdata) => { const executeSingleAction = (inputdata) => {
//if (serverside === true) {
// return
//}
if (validation === true) { if (validation === true) {
inputdata = JSON.stringify(inputdata) inputdata = JSON.stringify(inputdata)
} }
@@ -834,7 +829,10 @@ const CodeEditor = (props) => {
// Shuffle Tools 1.2.0 (in most cases?) // Shuffle Tools 1.2.0 (in most cases?)
const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a" const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a"
const actiondata = {"description":"Repeats the call parameter","id":"","name":"repeat_back_to_me","label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters":[{"description":"The message to repeat","id":"","name":"call","example":"REPEATING: Hello world","value":inputdata,"multiline":true,"options":null,"action_field":"","variant":"STATIC_VALUE","required":true,"configuration":false,"tags":null,"schema":{"type":"string"},"skip_multicheck":false,"value_replace":null,"unique_toggled":false,"autocompleted":false}],"execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}} const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me"
const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"name":"call", "value": inputdata}]
const actiondata = {"description":"Repeats the call parameter","id":"","name":actionname,"label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters": params, "execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
setExecutionResult({ setExecutionResult({
"valid": false, "valid": false,
@@ -924,6 +922,20 @@ const CodeEditor = (props) => {
}, },
}} }}
> >
<IconButton
style={{
zIndex: 5000,
position: "absolute",
top: 14,
right: 18,
color: "grey",
}}
onClick={() => {
setExpansionModalOpen(false)
}}
>
<CloseIcon />
</IconButton>
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
<div style={{flex: 1, }}> <div style={{flex: 1, }}>
{ isFileEditor ? { isFileEditor ?
@@ -1555,7 +1567,7 @@ const CodeEditor = (props) => {
<IconButton disabled={executing} color="primary" style={{border: `1px solid ${theme.palette.primary.main}`, marginLeft: 100, padding: 8}} variant="contained" onClick={() => { <IconButton disabled={executing} color="primary" style={{border: `1px solid ${theme.palette.primary.main}`, marginLeft: 100, padding: 8}} variant="contained" onClick={() => {
executeSingleAction(expOutput) executeSingleAction(expOutput)
}}> }}>
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top"> <Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
{executing ? <CircularProgress style={{height: 18, width: 18, }} /> : <PlayArrowIcon style={{height: 18, width: 18, }} /> } {executing ? <CircularProgress style={{height: 18, width: 18, }} /> : <PlayArrowIcon style={{height: 18, width: 18, }} /> }
</Tooltip> </Tooltip>
File diff suppressed because it is too large Load Diff
+2 -5
View File
@@ -154,7 +154,6 @@ const AppGrid = props => {
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success !== false) { if (responseJson.success !== false) {
console.log("Usecases: ", responseJson)
//handleKeysetting(responseJson, workflows) //handleKeysetting(responseJson, workflows)
} }
}) })
@@ -187,10 +186,10 @@ const AppGrid = props => {
}, []) }, [])
if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) { if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) {
console.log("In refinement: ", inputsearch)
//setLocalMessage(inputsearch) //setLocalMessage(inputsearch)
refine(inputsearch) refine(inputsearch)
defaultSearch = inputsearch defaultSearch = inputsearch
return null
} else if (onlyResults === true) { } else if (onlyResults === true) {
// Don't return anything unless refinement works // Don't return anything unless refinement works
return null return null
@@ -244,9 +243,7 @@ const AppGrid = props => {
return ( return (
<div> <div>
{onlyResults === true && hits.length > 0 ? {onlyResults === true && hits.length > 0 ?
<Typography variant="h6" style={{paddingBottom: 0, }}> null
Relevant Workflows
</Typography>
: null} : null}
<Grid container spacing={4} style={paperAppContainer}> <Grid container spacing={4} style={paperAppContainer}>
{hits.map((data, index) => { {hits.map((data, index) => {
+1 -1
View File
@@ -277,7 +277,7 @@ const WorkflowPaper = (props) => {
> >
{data.tags !== undefined && data.tags !== null {data.tags !== undefined && data.tags !== null
? data.tags.map((tag, index) => { ? data.tags.map((tag, index) => {
if (index >= 3) { if (index >= 2) {
return null; return null;
} }
@@ -0,0 +1,382 @@
import React, { useState, useEffect } from "react";
import { toast } from "react-toastify"
import theme from '../theme.jsx';
import {
Button,
Typography,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Drawer,
CircularProgress,
IconButton,
} from "@mui/material";
import {
Check as CheckIcon,
TrendingFlat as TrendingFlatIcon,
Close as CloseIcon,
} from '@mui/icons-material';
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
const WorkflowTemplatePopup = (props) => {
const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props;
const [isActive, setIsActive] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const [workflowLoading, setWorkflowLoading] = useState(false);
const [workflow, setWorkflow] = useState({});
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
const imagestyle = {
height: 40,
width: 40,
borderRadius: 40,
}
if (title === undefined || title === null || title === "") {
console.log("No title for workflow template popup!");
return null
}
const getWorkflow = (workflowId) => {
fetch(`${globalUrl}/api/v1/workflows/${workflowId}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for framework!");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
console.log("Error in workflow loading for ID ", workflowId)
} else {
setWorkflow(responseJson)
}
})
.catch((error) => {
console.log("err in framework: ", error.toString());
setWorkflowLoading(false)
})
}
const loadAppAuth = () => {
fetch(`${globalUrl}/api/v1/apps/authentication`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for setting app auth :O!");
}
return response.json();
})
.then((responseJson) => {
if (!responseJson.success) {
toast("Failed to get app auth: " + responseJson.reason);
return
}
var newauth = [];
for (let authkey in responseJson.data) {
if (responseJson.data[authkey].defined === false) {
continue;
}
newauth.push(responseJson.data[authkey]);
}
setAppAuthentication(newauth);
})
.catch((error) => {
//toast(error.toString());
console.log("New auth error: ", error.toString());
});
}
const getGeneratedWorkflow = () => {
// POST
// https://shuffler.io/api/v1/workflows/merge
// destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",}
// id: ""
// middle:[]
// name: "Email analysis"
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
setWorkflowLoading(true)
// FIXME: Remove hardcoding here after testing, and user srcapp/dstapp
const newsrcapp = srcapp
const newdstapp = dstapp
const mergedata = {
name: title,
id: "",
source: {
app_name: newsrcapp,
},
middle: [],
destination: {
app_name: newdstapp,
},
}
fetch(globalUrl + "/api/v1/workflows/merge", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
body: JSON.stringify(mergedata),
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for framework!");
}
setWorkflowLoading(false)
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
console.log("Error in workflow template: ", responseJson.error);
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.")
setIsActive(true)
//setTimeout(() => {
// setModalOpen(false)
//}, 5000)
} else {
console.log("Success in workflow template: ", responseJson);
setIsActive(true)
if (responseJson.workflow_id === "") {
console.log("Failed to build workflow for these tools. Closing in 3 seconds.")
return
}
getWorkflow(responseJson.workflow_id)
}
})
.catch((error) => {
console.log("err in framework: ", error.toString());
setWorkflowLoading(false)
})
}
const isFinished = () => {
// Look for configuration fields being done in the current modal
// 1. Start by finding the modal
const template = document.getElementById("workflow-template")
if (template === null || template == undefined) {
return true
}
// Find item in template with id app-config
const appconfig = template.getElementsByClassName("app-config")
if (appconfig === null || appconfig == undefined) {
return true
}
console.log("APPCONFIG: ", appconfig)
return false
}
const ModalView = () => {
return (
<Drawer
anchor={"left"}
open={modalOpen}
onClose={() => {
setModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: "black",
color: "white",
minWidth: 700,
maxWidth: 700,
paddingTop: 75,
itemAlign: "center",
},
}}
>
<IconButton
style={{
zIndex: 5000,
position: "absolute",
top: 14,
right: 14,
color: "white",
}}
onClick={() => {
setModalOpen(false);
}}
>
<CloseIcon />
</IconButton>
<DialogContent style={{marginTop: 0, marginLeft: 75, maxWidth: 470, }}>
<Typography variant="h4">
<b>Configure Workflow</b>
</Typography>
<Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}>
Selected Workflow:
</Typography>
<div style={{marginBottom: 0, }} id="workflow-template">
<WorkflowTemplatePopup2
globalUrl={globalUrl}
img1={img1}
srcapp={srcapp}
img2={img2}
dstapp={dstapp}
title={title}
description={description}
visualOnly={true}
/>
</div>
{workflowLoading ?
<div style={{marginTop: 75, textAlign: "center", }}>
<Typography variant="h4"> Generating the Workflow...
</Typography>
<CircularProgress style={{marginLeft: 125, marginTop: 10, }}/>
</div>
:
<div>
<Typography variant="h6" style={{marginTop: 75, }}>
{errorMessage !== "" ? errorMessage : ""}
</Typography>
</div>
}
<ConfigureWorkflow
userdata={userdata}
theme={theme}
globalUrl={globalUrl}
workflow={workflow}
appAuthentication={appAuthentication}
setAppAuthentication={setAppAuthentication}
apps={apps}
/>
{errorMessage === "" ?
<Button
style={{marginTop: 50, }}
variant={isFinished() ? "contained" : "outlined"}
>
Done
</Button>
: null}
</DialogContent>
</Drawer>
)
}
var parsedTitle = title
const maxlength = 30
if (title.length > maxlength) {
parsedTitle = title.substring(0, maxlength) + "..."
}
parsedTitle.replaceAll("_", " ")
return (
<div style={{ display: "flex", maxWidth: 470, minWidth: 470, height: 78, borderRadius: 8 }}>
<ModalView />
<div
// variant={isActive === 1 ? "contained" : "outlined"}
color="secondary"
disabled={visualOnly === true}
style={{
margin: 4,
width: "100%",
borderRadius: 8,
textTransform: "none",
backgroundColor: theme.palette.inputColor,
border: isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? "1px solid #f85a3e" : "1px solid rgba(33, 33, 33, 1)",
cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer",
padding: "10px 20px 10px 20px",
position: "relative",
}}
onMouseEnter={() => {
setIsHovered(true)
}}
onMouseLeave={() => {
setIsHovered(false)
}}
onClick={() => {
if (visualOnly === true) {
console.log("Not showing more than visuals.")
return
}
//setIsActive(!isActive)
if (errorMessage !== "") {
toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.")
} else if (isActive) {
toast("Workflow already generated. Please try another template, or continue to the next page.")
// FIXME: Remove these?
loadAppAuth()
setModalOpen(true)
//getGeneratedWorkflow()
} else {
loadAppAuth()
setModalOpen(true)
getGeneratedWorkflow()
}
}}
>
<div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}>
<div style={{display: "flex", flex: 1, marginTop: 3, }}>
<img src={img1} style={imagestyle} />
{img2 !== "" ?
<span style={{display: "flex", }}>
<TrendingFlatIcon style={{ marginTop: 7 }} />
<img src={img2} style={imagestyle} />
</span>
:
<span style={{width: 50, }} />
}
</div>
<div style={{ flex: 3, marginLeft: 20, }}>
<Typography variant="body1" style={{ marginTop: description.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)">
{parsedTitle}
</Typography>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, marginRight: 0, maxHeight: 16, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
{description}
</Typography>
</div>
</div>
<div>
{isActive === true && errorMessage === "" ?
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
: ""}
</div>
</div>
</div>
)
}
export default WorkflowTemplatePopup
+3 -6
View File
@@ -18,10 +18,12 @@ const theme = createTheme(adaptV4Theme({
secondary: "rgba(255,255,255,0.7)", secondary: "rgba(255,255,255,0.7)",
}, },
type: "dark", type: "dark",
inputColor: "#383B40", inputColor: "rgba(39,41,45,1)",
//inputColor: "#383B40",
surfaceColor: "#27292d", surfaceColor: "#27292d",
platformColor: "#1c1c1d", platformColor: "#1c1c1d",
backgroundColor: "#1a1a1a", backgroundColor: "#1a1a1a",
green: "#5cc879",
borderRadius: 5, borderRadius: 5,
defaultBorder: "1px solid rgba(255,255,255,0.3)", defaultBorder: "1px solid rgba(255,255,255,0.3)",
jsonTheme: "brewer", jsonTheme: "brewer",
@@ -70,11 +72,6 @@ const theme = createTheme(adaptV4Theme({
}, },
}, },
overrides: { overrides: {
MuiPaper: {
root: {
backgroundColor: "#1c1c1d",
},
},
MuiMenu: { MuiMenu: {
list: { list: {
backgroundColor: "#27292d", backgroundColor: "#27292d",
+19 -11
View File
@@ -1692,15 +1692,24 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
const userId = user.id; const userId = user.id;
const data = { user_id: userId }; const data = { user_id: userId };
fetch(globalUrl + "/api/v1/generateapikey", { console.log(user, userdata)
var fetchdata = {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
body: JSON.stringify(data),
credentials: "include", credentials: "include",
}) }
if (userId === userdata.id) {
fetchdata.method = "GET"
} else {
fetchdata.body = JSON.stringify(data)
}
fetch(globalUrl + "/api/v1/generateapikey", fetchdata)
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for WORKFLOW EXECUTION :O!"); console.log("Status not 200 for WORKFLOW EXECUTION :O!");
@@ -1889,8 +1898,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
open={selectedUserModalOpen} open={selectedUserModalOpen}
onClose={() => { onClose={() => {
setSelectedUserModalOpen(false); setSelectedUserModalOpen(false);
setImage2FA("");
setSecret2FA(""); setImage2FA("");
setValue2FA("");
setSecret2FA("");
setShow2faSetup(false);
}} }}
PaperProps={{ PaperProps={{
style: { style: {
@@ -2156,7 +2168,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
<Card <Card
style={{ style={{
margin: 4, margin: 4,
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
minHeight: expanded ? 250 : "inherit", minHeight: expanded ? 250 : "inherit",
maxHeight: expanded ? 250 : "inherit", maxHeight: expanded ? 250 : "inherit",
@@ -2515,9 +2527,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
</span> </span>
/> />
<Tab <Tab
disabled={!isCloud}
label=<span> label=<span>
Billing Billing (Beta)
</span> </span>
/> />
<Tab <Tab
@@ -3246,10 +3257,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
selectedOrganization.child_orgs !== null && selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0 selectedOrganization.child_orgs.length > 0
) { ) {
console.log("In here?");
var active = []; var active = [];
for (var key in userdata.orgs) { for (var key in userdata.orgs) {
console.log("ORG: ", userdata.orgs[key]);
const found = const found =
selectedOrganization.child_orgs.find( selectedOrganization.child_orgs.find(
(item) => item.id === userdata.orgs[key].id (item) => item.id === userdata.orgs[key].id
@@ -3299,7 +3308,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
) : null; ) : null;
const run2FASetup = (data) => { const run2FASetup = (data) => {
console.log("2fa: ", data, show2faSetup);
if (!show2faSetup) { if (!show2faSetup) {
get2faCode(data.id); get2faCode(data.id);
} else { } else {
+45 -13
View File
@@ -580,15 +580,39 @@ const AngularWorkflow = (defaultprops) => {
const cytoscapeWidth = isMobile ? bodyWidth - leftBarSize : bodyWidth - leftBarSize - 25 const cytoscapeWidth = isMobile ? bodyWidth - leftBarSize : bodyWidth - leftBarSize - 25
const [elements, setElements] = useState([]); const [elements, setElements] = useState([]);
const [loopRunning, setLoopRunning] = useState(false)
const stop = () => {
setLoopRunning(false)
}
const start = () => {
setLoopRunning(true)
}
useEffect(() => {
console.log("In useeffect for loopRunning: ", loopRunning)
if (loopRunning) {
const intervalId = setInterval(() => {
if (!loopRunning) {
clearInterval(intervalId);
}
fetchUpdates()
}, 3000)
return () => clearInterval(intervalId);
}
}, [loopRunning])
// No point going as fast, as the nodes aren't realtime anymore, but bulk updated. // No point going as fast, as the nodes aren't realtime anymore, but bulk updated.
// Set it from 2500 to 6000 to reduce overall load //const { start, stop } = useInterval({
const { start, stop } = useInterval({ // duration: 3000,
duration: 3000, // startImmediate: false,
startImmediate: false, // callback: () => {
callback: () => { // fetchUpdates();
fetchUpdates(); // },
}, //});
});
const getAppDocs = (appname, location, version) => { const getAppDocs = (appname, location, version) => {
fetch(`${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`, { fetch(`${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`, {
@@ -924,7 +948,7 @@ const AngularWorkflow = (defaultprops) => {
}; };
const getWorkflowExecution = (id, execution_id) => { const getWorkflowExecution = (id, execution_id) => {
fetch(`${globalUrl}/api/v1/workflows/${id}/executions`, { fetch(`${globalUrl}/api/v2/workflows/${id}/executions`, {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -940,11 +964,10 @@ const AngularWorkflow = (defaultprops) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) { if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null && responseJson.executions.length > 0) {
// FIXME: Sort this by time
// - means it's opposite // - means it's opposite
const newkeys = sortByKey(responseJson, "-started_at"); const newkeys = sortByKey(responseJson.executions, "-started_at");
setWorkflowExecutions(newkeys); setWorkflowExecutions(newkeys);
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
@@ -1021,6 +1044,7 @@ const AngularWorkflow = (defaultprops) => {
}, },
body: JSON.stringify(executionRequest), body: JSON.stringify(executionRequest),
credentials: "include", credentials: "include",
cors: "no-cors",
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
@@ -1279,7 +1303,7 @@ const AngularWorkflow = (defaultprops) => {
}; };
const sendStreamRequest = (body) => { const sendStreamRequest = (body) => {
console.log("Stream not activated yet.") //console.log("Stream not activated yet.")
return return
// Session may be important here huh // Session may be important here huh
@@ -15302,6 +15326,10 @@ const AngularWorkflow = (defaultprops) => {
overflow: "hidden", overflow: "hidden",
}} }}
onMouseOver={() => { onMouseOver={() => {
if (cy == undefined || cy == null) {
return
}
var currentnode = cy.getElementById(data.action.id); var currentnode = cy.getElementById(data.action.id);
if (currentnode !== undefined && currentnode !== null && currentnode.length !== 0) { if (currentnode !== undefined && currentnode !== null && currentnode.length !== 0) {
currentnode.addClass("shuffle-hover-highlight"); currentnode.addClass("shuffle-hover-highlight");
@@ -15314,6 +15342,10 @@ const AngularWorkflow = (defaultprops) => {
//) //)
}} }}
onMouseOut={() => { onMouseOut={() => {
if (cy == undefined || cy == null) {
return
}
var currentnode = cy.getElementById(data.action.id); var currentnode = cy.getElementById(data.action.id);
if (currentnode.length !== 0) { if (currentnode.length !== 0) {
currentnode.removeClass("shuffle-hover-highlight"); currentnode.removeClass("shuffle-hover-highlight");
+2 -1
View File
@@ -1354,7 +1354,8 @@ const Apps = (props) => {
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
cursor: hover ? "pointer" : "default", cursor: hover ? "pointer" : "default",
textAlign: "center", textAlign: "center",
height: 125, minHeight: 150,
maxHeight: 150,
}} }}
> >
{icon} {icon}
+181 -121
View File
@@ -6,6 +6,7 @@ import { makeStyles, } from "@mui/styles";
import classNames from "classnames"; import classNames from "classnames";
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
// react plugin used to create charts // react plugin used to create charts
//import { Line, Bar } from "react-chartjs-2"; //import { Line, Bar } from "react-chartjs-2";
@@ -90,7 +91,7 @@ const useStyles = makeStyles({
}, },
}); });
const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => { const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
const [expandedIndex, setExpandedIndex] = useState(-1); const [expandedIndex, setExpandedIndex] = useState(-1);
const [expandedItem, setExpandedItem] = useState(-1); const [expandedItem, setExpandedItem] = useState(-1);
const [inputUsecase, setInputUsecase] = useState({}); const [inputUsecase, setInputUsecase] = useState({});
@@ -103,17 +104,69 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
const [selectedWorkflows, setSelectedWorkflows] = useState([]) const [selectedWorkflows, setSelectedWorkflows] = useState([])
const [firstLoad, setFirstLoad] = useState(true) const [firstLoad, setFirstLoad] = useState(true)
const [apps, setApps] = useState([])
const classes = useStyles(); const classes = useStyles();
let navigate = useNavigate(); let navigate = useNavigate();
const [mitreTags, setMitreTags] = useState([]); const [mitreTags, setMitreTags] = useState([]);
const loadApps = () => {
fetch(`${globalUrl}/api/v1/apps`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
return response.json();
})
.then((responseJson) => {
if (responseJson === null) {
console.log("null-response from server")
const pretend_apps = [{
"name": "TBD",
"app_name": "TBD",
"app_version": "TBD",
"description": "TBD",
"version": "TBD",
"large_image": "",
}]
setApps(pretend_apps)
return
}
if (responseJson.success === false) {
console.log("error loading apps: ", responseJson)
return
}
setApps(responseJson);
})
.catch((error) => {
console.log("App loading error: " + error.toString());
})
}
useEffect(() => {
loadApps()
}, [])
if (keys === undefined || keys === null || keys.length === 0) { if (keys === undefined || keys === null || keys.length === 0) {
return null return null
} }
const getUsecase = (name, index, subindex) => { const parseUsecase = (parsedUsecase, subcase) => {
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(name.replaceAll(" ", "_"))}`, { console.log("parseUsecase: ", parsedUsecase, subcase)
}
const getUsecase = (subcase, index, subindex) => {
console.log(subcase)
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -129,14 +182,26 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
var parsedUsecase = responseJson
if (responseJson.success === false) { if (responseJson.success === false) {
setInputUsecase({ parsedUsecase = subcase
"name": name,
}) //img1={inputUsecase.srcimg}
//srcapp={inputUsecase.srcapp}
//img2={inputUsecase.dstimg}
//dstapp={inputUsecase.dstapp}
//title={inputUsecase.name}
parsedUsecase = parseUsecase(parsedUsecase, subcase)
} else { } else {
setInputUsecase(responseJson) parsedUsecase = responseJson
parsedUsecase = parseUsecase(parsedUsecase, responseJson)
} }
// Look for the type of app and fill in img1, srcapp...
setInputUsecase(parsedUsecase)
setExpandedIndex(index) setExpandedIndex(index)
setExpandedItem(subindex) setExpandedItem(subindex)
@@ -289,6 +354,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) { if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) {
setFirstLoad(false) setFirstLoad(false)
setSelectedWorkflows(subcase.matches) setSelectedWorkflows(subcase.matches)
} }
} }
@@ -316,10 +382,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
} }
} }
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
const finished = subcase.matches.length > 0 const finished = subcase.matches.length > 0
const backgroundColor = theme.palette.surfaceColor const backgroundColor = theme.palette.surfaceColor
//"inherit"
const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}` const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}`
const fixedName = subcase.name.toLowerCase().replace("_", " ") const fixedName = subcase.name.toLowerCase().replace("_", " ")
@@ -330,14 +394,14 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
//setSelectedWorkflows([]) //setSelectedWorkflows([])
if (selectedItem) { if (selectedItem) {
} else { } else {
getUsecase(subcase.name, index, subindex) getUsecase(subcase, index, subindex)
navigate(`/usecases?selected_object=${fixedName}`) navigate(`/usecases?selected_object=${fixedName}`)
//const newitem = removeParam("selected_object", cursearch); //const newitem = removeParam("selected_object", cursearch);
//navigate(curpath + newitem) //navigate(curpath + newitem)
} }
}}> }}>
<Paper style={{padding: 25, minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => { <Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
}}> }}>
{!selectedItem ? {!selectedItem ?
<div style={{textAlign: "left", position: "relative",}}> <div style={{textAlign: "left", position: "relative",}}>
@@ -649,12 +713,12 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
: null} : null}
{workflows !== undefined && workflows !== null && workflows.length > 0 ? {workflows !== undefined && workflows !== null && workflows.length > 0 ?
<Autocomplete <Autocomplete
multiple multiple
id="workflow_matching" id="workflow_matching"
options={workflows} options={workflows}
autoHighlight autoHighlight
value={selectedWorkflows} value={selectedWorkflows}
classes={{ inputRoot: classes.inputRoot }} classes={{ inputRoot: classes.inputRoot }}
ListboxProps={{ ListboxProps={{
style: { style: {
@@ -662,7 +726,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
color: "white", color: "white",
}, },
}} }}
getOptionSelected={(option, value) => option.id === value.id} getOptionSelected={(option, value) => option.id === value.id}
getOptionLabel={(option) => { getOptionLabel={(option) => {
if ( if (
@@ -685,120 +749,120 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
console.log("CLICK: ", newValue) console.log("CLICK: ", newValue)
//handleWorkflowSelectionUpdate({ target: { value: newValue} }) //handleWorkflowSelectionUpdate({ target: { value: newValue} })
//setSelectedWorkflows= //setSelectedWorkflows=
//var newvalue = [] //var newvalue = []
//for (var key in newValue) { //for (var key in newValue) {
// if (newValue[key].id !== undefined) { // if (newValue[key].id !== undefined) {
// newvalue.push(newValue[key].id) // newvalue.push(newValue[key].id)
// } // }
//} //}
// Doing this way as you may want to remove some too // Doing this way as you may want to remove some too
for (var key in workflows) { for (var key in workflows) {
if (!newValue.find(data => data.id === workflows[key].id)) { if (!newValue.find(data => data.id === workflows[key].id)) {
// Check if it has the one in it // Check if it has the one in it
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) { if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name) const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
if (filtered !== undefined && filtered !== null) { if (filtered !== undefined && filtered !== null) {
//console.log("Removing: ", workflows[key].name, workflows[key]) //console.log("Removing: ", workflows[key].name, workflows[key])
workflows[key]["usecase_ids"] = filtered workflows[key]["usecase_ids"] = filtered
setWorkflow(workflows[key]) setWorkflow(workflows[key])
} }
} }
continue continue
} }
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) { if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
workflows[key]["usecase_ids"] = [subcase.name] workflows[key]["usecase_ids"] = [subcase.name]
console.log("Setting: ", workflows[key].name) console.log("Setting: ", workflows[key].name)
setWorkflow(workflows[key]) setWorkflow(workflows[key])
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) { } else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
workflows[key]["usecase_ids"].push(subcase.name) workflows[key]["usecase_ids"].push(subcase.name)
console.log("Adding: ", workflows[key].name) console.log("Adding: ", workflows[key].name)
setWorkflow(workflows[key]) setWorkflow(workflows[key])
} }
} }
setWorkflows(workflows) setWorkflows(workflows)
console.log("New: ", newValue) console.log("New: ", newValue)
setSelectedWorkflows(newValue) setSelectedWorkflows(newValue)
//setUpdate(Math.random()) //setUpdate(Math.random())
}} }}
renderOption={(props, option) => { renderOption={(props, data, state) => {
//console.log("In options?: ", props, option) var newname = data.name
if (newname === undefined || newname === null) {
newname = "placeholder"
}
var newname = props.name if (newname.length > 2) {
if (newname === undefined || newname === null) { newname = newname.charAt(0).toUpperCase() + newname.substring(1)
newname = "placeholder" }
} return (
<li {...props}>
if (newname.length > 2) { <Tooltip arrow placement="left" title={
newname = newname.charAt(0).toUpperCase() + newname.substring(1) <span style={{}}>
} {data.image !== undefined && data.image !== null && data.image.length > 0 ?
return ( <img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
<li {...props}> : null}
<Tooltip arrow placement="left" title={ <Typography>
<span style={{}}> Choose {newname}
{props.image !== undefined && props.image !== null && props.image.length > 0 ? </Typography>
<img src={props.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} /> </span>
: null} } placement="bottom">
<Typography> <span>
Choose {newname} <Checkbox
</Typography> icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
</span> checkedIcon={<CheckBoxIcon fontSize="small" />}
} placement="bottom"> style={{ marginRight: 8 }}
<span> checked={selectedWorkflows.find(wf => wf.id === data.id) !== undefined}
<Checkbox />
icon={<CheckBoxOutlineBlankIcon fontSize="small" />} {newname}
checkedIcon={<CheckBoxIcon fontSize="small" />} </span>
style={{ marginRight: 8 }} </Tooltip>
checked={option.selected} </li>
/> )
{newname} }}
</span>
</Tooltip>
</li>
)
}}
renderInput={(params) => { renderInput={(params) => {
return ( return (
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
}} }}
{...params} {...params}
label="Find your workflows" label="Find your workflows"
variant="outlined" variant="outlined"
/> />
); );
}} }}
/> />
: null} : null}
<span style={{top: 30, position: "relative",}}>
<Typography variant="body1" style={{marginTop: 0,}} onClick={() => {}}>
Try it out
</Typography>
<WorkflowTemplatePopup
userdata={userdata}
globalUrl={globalUrl}
img1={inputUsecase.srcimg}
srcapp={inputUsecase.srcapp}
img2={inputUsecase.dstimg}
dstapp={inputUsecase.dstapp}
title={inputUsecase.name}
description={inputUsecase.description}
apps={apps}
/>
</span>
{/*
{/*subcase.matches.length > 0 ?
<Grid container style={{maxWidth: 325, marginTop: 10, }}>
{subcase.matches.map((workflow, workflowindex) => {
return (
<Grid key={workflowindex} item index={workflowindex} xs={12}>
<WorkflowPaper key={workflowindex} data={workflow} />
</Grid>
)
})}
</Grid>
:
<div>
<Typography variant="body1" color="textSecondary">
No workflow selected yet.
</Typography>
</div>
*/}
{subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ? {subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ?
<div style={{marginTop: 25, }}> <div style={{marginTop: 25, }}>
@@ -852,15 +916,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
<Typography variant="body1" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {}}> <Typography variant="body1" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {}}>
See other Public Workflows for {} <OpenInNewIcon style={{marginTop: 5, marginLeft: 15, }}/> See other Public Workflows for {} <OpenInNewIcon style={{marginTop: 5, marginLeft: 15, }}/>
</Typography> </Typography>
{/*
<div>
<Typography variant="body1" color="textSecondary">
No workflows yet.
</Typography>
</div>
*/}
</a> </a>
</div> </div>
*/}
</div> </div>
} }
<div style={{ <div style={{
@@ -868,6 +926,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
width: 350, width: 350,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
padding: 5,
backgroundColor: theme.palette.backgroundColor,
}}> }}>
<AppFramework <AppFramework
inputUsecase={inputUsecase} inputUsecase={inputUsecase}
+13 -6
View File
@@ -305,7 +305,6 @@ const LoginDialog = (props) => {
paddingBottom: "30px", paddingBottom: "30px",
paddingTop: "30px", paddingTop: "30px",
position: "relative", position: "relative",
backgroundColor: theme.palette.inputColor,
textAlign: "left", textAlign: "left",
marginTop: 15, marginTop: 15,
}} }}
@@ -331,17 +330,25 @@ const LoginDialog = (props) => {
variant="body2" variant="body2"
style={{ marginBottom: 20, color: "white" }} style={{ marginBottom: 20, color: "white" }}
> >
<b>1.</b> Make sure shuffle-database folder has correct access:{" "} <b>1.</b> Make sure shuffle-database folder has correct access, and that you have a minimum of <b>2Gb of RAM available</b>:{" "}
<br /> <br />
<br /> <br />
sudo chown 1000:1000 -R shuffle-database sudo chown 1000:1000 -R shuffle-database
</Typography> </Typography>
<Typography <Typography
variant="body2" variant="body2"
style={{ marginBottom: 20, color: "white" }} style={{ marginBottom: 20, color: "white" }}
> >
<b>2</b>. Restart docker-compose: <b>2.</b> Disable memory swap on the host
<br />
<br />
sudo swapoff -a
</Typography>
<Typography
variant="body2"
style={{ marginBottom: 20, color: "white" }}
>
<b>3</b>. Restart docker-compose:
<br /> <br />
<br /> <br />
sudo docker-compose restart sudo docker-compose restart
@@ -513,7 +520,7 @@ const LoginDialog = (props) => {
//id="sso_button" //id="sso_button"
const ssoBtn = document.getElementById("sso_button"); const ssoBtn = document.getElementById("sso_button");
if (ssoBtn !== undefined && ssoBtn !== null) { if (ssoBtn !== undefined && ssoBtn !== null) {
console.log("SSO BTN: ", ssoBtn) //console.log("SSO BTN: ", ssoBtn)
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
var tmpView = new URLSearchParams(cursearch).get("autologin"); var tmpView = new URLSearchParams(cursearch).get("autologin");
if (tmpView !== undefined && tmpView !== null) { if (tmpView !== undefined && tmpView !== null) {
@@ -527,7 +534,7 @@ const LoginDialog = (props) => {
}, 200); }, 200);
}, [ssoUrl]) }, [ssoUrl])
return <div>{loadedCheck}</div>; return <div style={{paddingBottom: 150, }}>{loadedCheck}</div>;
}; };
export default LoginDialog; export default LoginDialog;
+17 -17
View File
@@ -26,7 +26,7 @@ const SetAuthentication = (props) => {
//const alert = useAlert(); //const alert = useAlert();
const parseIncomingOpenapiData = (data) => { const parseIncomingOpenapiData = (data) => {
if (data.app === undefined || data.app === null) { if (data.app === undefined || data.app === null) {
return return
} }
@@ -43,11 +43,11 @@ const SetAuthentication = (props) => {
parsedapp.name = parsedapp.name.replaceAll("_", " "); parsedapp.name = parsedapp.name.replaceAll("_", " ");
setApp(parsedapp); setApp(parsedapp);
document.title = parsedapp.name + " App Auth"; document.title = parsedapp.name + " App Auth";
} }
console.log("App: ", app) console.log("App: ", app)
const getApp = (appid) => { const getApp = (appid) => {
if (serverside === true) { if (serverside === true) {
@@ -64,21 +64,21 @@ const SetAuthentication = (props) => {
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
if (isCloud) { if (isCloud) {
ReactGA.event({ ReactGA.event({
category: "appauth", category: "appauth",
action: `app_not_found`, action: `app_not_found`,
label: appid, label: appid,
}); });
} }
} else { } else {
if (isCloud) { if (isCloud) {
ReactGA.event({ ReactGA.event({
category: "appauth", category: "appauth",
action: `app_found`, action: `app_found`,
label: appid, label: appid,
}); });
} }
} }
return response.json(); return response.json();
+48 -36
View File
@@ -23,6 +23,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import Drift from "react-driftjs"; import Drift from "react-driftjs";
const Welcome = (props) => { const Welcome = (props) => {
@@ -301,44 +302,47 @@ const Welcome = (props) => {
flex: 1, flex: 1,
padding: 0, padding: 0,
textAlign: "center", textAlign: "center",
maxWidth: 300, maxWidth: 275,
minWidth: 300, minWidth: 275,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
} }
const actionObject = { const actionObject = {
padding: "35px", padding: "25px",
maxHeight: 300, maxHeight: 280,
minHeight: 300, minHeight: 280,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
} }
const imageStyle = { const imageStyle = {
width: 150, width: 70,
// height: 150, // height: 150,
// margin: "auto", // margin: "auto",
// marginTop: 10, // marginTop: 10,
borderRadius: 75, marginBottom: 18,
// borderRadius: 75,
objectFit: "scale-down", objectFit: "scale-down",
} }
const buttonStyle = { const buttonStyle = {
borderRadius: 8, borderRadius: 200,
height: 51, height: 51,
width: 464, width: 464,
fontSize: 16, fontSize: 16,
background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
padding: "16px 24px", padding: "16px 24px",
top: 75, top: 105,
margin: "auto", margin: "auto",
itemAlign: "center", itemAlign: "center",
marginLeft: "65px",
} }
const defaultImage = "/images/experienced.png" const defaultImage = "/images/experienced.png"
const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage
return ( return (
<div style={{width: 1000, margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}> <div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
{/* {/*
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}> <div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} /> <img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
@@ -346,7 +350,7 @@ const Welcome = (props) => {
*/} */}
{showWelcome === true ? {showWelcome === true ?
<div> <div>
<div style={{minWidth: 500, maxWidth: 500, margin: "auto", marginTop: isCloud ? "auto" : 20, }}> {/* <div style={{minWidth: 500, maxWidth: 500, margin: "auto", marginTop: isCloud ? "auto" : 20, }}>
<Stepper <Stepper
activeStep={activeStep} activeStep={activeStep}
color="primary" color="primary"
@@ -379,8 +383,8 @@ const Welcome = (props) => {
) )
})} })}
</Stepper> </Stepper>
</div> </div> */}
<Grid container spacing={2} style={{ padding: 0, maxWidth: 1000, minWidth: 1000, margin: "auto", }}> <Grid container spacing={1} style={{ padding: 0, maxWidth: 500, minWidth: 500, marginRight : "auto" , marginLeft: "auto" }}>
<Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}> <Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}>
<div> <div>
{/* {/*
@@ -414,7 +418,7 @@ const Welcome = (props) => {
/> />
</div> </div>
</Grid> </Grid>
{frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null : {/* {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
<div style={{marginTop: 25, }}> <div style={{marginTop: 25, }}>
<Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}> <Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}>
App Framework App Framework
@@ -439,32 +443,35 @@ const Welcome = (props) => {
/> />
</Fade> </Fade>
</div> </div>
} } */}
</Grid> </Grid>
</div> </div>
: :
<Fade in={true}> <Fade in={true}>
<div style={{maxWidth: 700, margin: "auto", marginTop: 50, }}> <div style={{maxWidth: 590, margin: "auto", marginTop: 50, }}>
{/*
<div style={{display:"flex"}}> {/* <div style={{display:"flex"}}>
<ArrowBackIosIcon style={{color: "#9E9E9E",}} onClick={() => { <ArrowBackIosIcon style={{color: "#9E9E9E", width: "16px",}} onClick={() => {
navigate("/login") navigate("/workflows")
}}/> }}/>
<Typography variant="body1" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 50, paddingRight: "366px"}} onClick={() => { <Typography variant="h8" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 20, paddingRight: "366px"}} onClick={() => {
navigate("/login") navigate("/workflows")
}}> }}>
Back Back
</Typography> </Typography>
</div> </div> */}
*/}
<Typography variant="h4" style={{color: "#F1F1F1", textAlign: "center", marginTop: 50, }}> <Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}>
Help us get to know you Help us get to know you
</Typography> </Typography>
<Typography variant="body1" style={{color: "#9E9E9E", textAlign: "center", marginBottom: 50,}}> <Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
We will use this information to personalize your automation Let us help you create a smoother journey.
</Typography> </Typography>
{/* <Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
We will use this information to personalize your automation
</Typography> */}
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}> <div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}>
<div style={{border: "1px solid #806BFF", borderRadius: theme.palette.borderRadius, }}> <div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
<Card style={paperObject} onClick={() => { <Card style={paperObject} onClick={() => {
if (isCloud) { if (isCloud) {
ReactGA.event({ ReactGA.event({
@@ -506,7 +513,7 @@ const Welcome = (props) => {
navigate("/workflows?message=Skipped intro") navigate("/workflows?message=Skipped intro")
}}> }}>
<CardActionArea style={actionObject}> <CardActionArea style={actionObject}>
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 38 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, }} /> <img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
<Typography variant="h4" style={{color: "#F1F1F1"}}> <Typography variant="h4" style={{color: "#F1F1F1"}}>
Experienced Experienced
</Typography> </Typography>
@@ -517,17 +524,22 @@ const Welcome = (props) => {
</Card> </Card>
</div> </div>
{/*
<div style={{display: "flex", flexDirection: "row", }}> <div style={{ flexDirection: "row", }}>
<Button variant="contained" type="submit" fullWidth style={buttonStyle} onClick={() => { <Button variant="contained" type="submit" fullWidth style={buttonStyle} onClick={() => {
navigate("/workflows?message=Skipped intro continue") ReactGA.event({
category: "welcome",
action: "click_welcome_continue",
label: "",
})
}}> }}>
Continue Continue
</Button> </Button>
</div> </div>
*/}
<div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
{/* <div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
if (window.drift !== undefined) { if (window.drift !== undefined) {
window.drift.api.startInteraction({ interactionId: 340045 }) window.drift.api.startInteraction({ interactionId: 340045 })
} else { } else {
@@ -537,7 +549,7 @@ const Welcome = (props) => {
<Typography variant="body1" style={{margin: "auto", textAlign: "center"}}> <Typography variant="body1" style={{margin: "auto", textAlign: "center"}}>
Want a demo instead? Want a demo instead?
</Typography> </Typography>
</div> </div> */}
</div> </div>
</Fade> </Fade>
} }
+127 -137
View File
@@ -11,13 +11,13 @@ import { isMobile } from "react-device-detect"
import { import {
Badge, Badge,
Divider, Divider,
Avatar, Avatar,
Drawer, Drawer,
Grid, Grid,
InputLabel, InputLabel,
Select, Select,
ListSubheader, ListSubheader,
Paper, Paper,
Tooltip, Tooltip,
Button, Button,
@@ -533,7 +533,6 @@ const Workflows = (props) => {
document.title = "Shuffle - Workflows"; document.title = "Shuffle - Workflows";
let navigate = useNavigate(); let navigate = useNavigate();
//const alert = useAlert();
const classes = useStyles(theme); const classes = useStyles(theme);
const imgSize = 60; const imgSize = 60;
@@ -590,6 +589,9 @@ const Workflows = (props) => {
const [drawerOpen, setDrawerOpen] = React.useState(false) const [drawerOpen, setDrawerOpen] = React.useState(false)
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 [apps, setApps] = React.useState([]);
const drawerWidth = drawerOpen ? 325 : 0 const drawerWidth = drawerOpen ? 325 : 0
@@ -725,6 +727,30 @@ const Workflows = (props) => {
} }
}; };
const getApps = () => {
fetch(`${globalUrl}/api/v1/apps`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
}
return response.json();
})
.then((responseJson) => {
setApps(responseJson);
})
.catch((error) => {
console.log("App loading error: "+error.toString());
});
}
const addFilter = (data) => { const addFilter = (data) => {
if (data === null || data === undefined) { if (data === null || data === undefined) {
console.log("No filter data") console.log("No filter data")
@@ -891,13 +917,15 @@ const Workflows = (props) => {
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
minWidth: 500, minWidth: 500,
padding: 50,
}, },
}} }}
> >
<DialogTitle> <DialogTitle>
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}> <div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
Are you sure you want to delete this workflow? <div /> Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}? <div />
Other workflows relying on this one may stop working
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
</div> </div>
</DialogTitle> </DialogTitle>
<DialogContent <DialogContent
@@ -912,7 +940,21 @@ const Workflows = (props) => {
setTimeout(() => { setTimeout(() => {
getAvailableWorkflows(); getAvailableWorkflows();
}, 1000); }, 1000);
} } else if (selectedWorkflowIndexes.length > 0) {
// Do backwards so it doesn't change
for (var i = selectedWorkflowIndexes.length - 1; i >= 0; i--) {
const workflow = filteredWorkflows[selectedWorkflowIndexes[i]-1]
if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) {
deleteWorkflow(workflow.id);
}
}
setTimeout(() => {
getAvailableWorkflows();
}, 1000);
setSelectedWorkflowIndexes([]);
}
setDeleteModalOpen(false); setDeleteModalOpen(false);
}} }}
color="primary" color="primary"
@@ -1115,9 +1157,9 @@ const Workflows = (props) => {
// Ensures the zooming happens only once per load // Ensures the zooming happens only once per load
setTimeout(() => { setTimeout(() => {
fetchUsecases(newarray) fetchUsecases(newarray)
setFirstLoad(false) setFirstLoad(false)
}, 100) }, 100)
} else { } else {
if (isLoggedIn) { if (isLoggedIn) {
@@ -1223,8 +1265,9 @@ const Workflows = (props) => {
setView(tmpView); setView(tmpView);
} }
getApps()
getAvailableWorkflows(); getAvailableWorkflows();
getFramework() getFramework()
} }
}, []) }, [])
@@ -1280,6 +1323,7 @@ const Workflows = (props) => {
color: "white", color: "white",
margin: "10px", margin: "10px",
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
position: "relative",
}; };
const workflowActionStyle = { const workflowActionStyle = {
@@ -1708,21 +1752,22 @@ const Workflows = (props) => {
<MenuItem <MenuItem
style={{ backgroundColor: theme.palette.inputColor, color: "white" }} style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
onClick={(event) => { onClick={(event) => {
event.stopPropagation() event.stopPropagation()
ReactDOM.unstable_batchedUpdates(() => { ReactDOM.unstable_batchedUpdates(() => {
setModalOpen(true); setIsEditing(true)
setEditingWorkflow(JSON.parse(JSON.stringify(data))); setModalOpen(true);
setNewWorkflowName(data.name); setEditingWorkflow(JSON.parse(JSON.stringify(data)));
setNewWorkflowDescription(data.description); setNewWorkflowName(data.name);
setDefaultReturnValue(data.default_return_value); setNewWorkflowDescription(data.description);
if (data.tags !== undefined && data.tags !== null) { setDefaultReturnValue(data.default_return_value);
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))); if (data.tags !== undefined && data.tags !== null) {
} setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
}
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) { if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
setSelectedUsecases(data.usecase_ids) setSelectedUsecases(data.usecase_ids)
} }
}) })
}} }}
key={"change"} key={"change"}
> >
@@ -2654,6 +2699,7 @@ const Workflows = (props) => {
renderCell: (params) => {}, renderCell: (params) => {},
}, },
]; ];
let rows = []; let rows = [];
rows = filteredWorkflows.map((data, index) => { rows = filteredWorkflows.map((data, index) => {
let obj = { let obj = {
@@ -2671,17 +2717,55 @@ const Workflows = (props) => {
className={classes.datagrid} className={classes.datagrid}
rows={rows} rows={rows}
columns={columns} columns={columns}
pageSize={20} pageSize={25}
checkboxSelection checkboxSelection
autoHeight autoHeight
density="standard" density="standard"
onSelectionModelChange={(newSelection) => {
//setSelectedWorkflows(newSelection.selectionModel);
console.log(newSelection)
setSelectedWorkflowIndexes(newSelection)
}}
selectionModel={selectedWorkflowIndexes}
components={{ components={{
Toolbar: GridToolbar, Toolbar: GridToolbar,
}} }}
/> />
); );
} }
return <div style={gridContainer}>{workflowData}</div>; return (
<div style={gridContainer}>
<Tooltip title={`New Workflow`} placement="bottom">
<IconButton
style={{position: "absolute", top: 10, right: 50, zIndex: 1000}}
onClick={() => {
setModalOpen(true)
setIsEditing(false)
}}
>
<AddCircleIcon style={{}} />
</IconButton>
</Tooltip>
{filteredWorkflows.length === 0 ? null :
<IconButton
style={{position: "absolute", top: 10, right: 10, zIndex: 1000}}
disabled={selectedWorkflowIndexes.length === 0}
onClick={() => {
setDeleteModalOpen(true)
}}
>
<Tooltip
title={`Delete ${selectedWorkflowIndexes.length} workflows`}
placement="top"
>
<DeleteIcon />
</Tooltip>
</IconButton>
}
{workflowData}
</div>
)
}; };
var total_count = 0 var total_count = 0
@@ -3152,70 +3236,20 @@ const Workflows = (props) => {
const WorkflowView = () => { const WorkflowView = () => {
if (workflows.length === 0) { if (workflows.length === 0) {
// Not going there yet
//if ((userdata.tutorials !== undefined && userdata.tutorials !== null && !userdata.tutorials.includes("getting-started")) || userdata.tutorials === null) {
// return <Navigate to="/getting-started" replace />;
//}
//return (
// <div style={emptyWorkflowStyle}>
// <Paper style={boxStyle}>
// <div>
// <h2>Welcome to Shuffle</h2>
// </div>
// <div>
// <p>
// <b>Shuffle</b> is a flexible, easy to use, automation platform
// allowing users to integrate their services and devices freely.
// It's made to significantly reduce the amount of manual labor,
// and is focused on security applications.{" "}
// <a
// href="/docs/about"
// style={{ textDecoration: "none", color: "#f85a3e" }}
// >
// Click here to learn more.
// </a>
// </p>
// </div>
// <div>
// If you want to jump straight into it, click here to create your
// first workflow:
// </div>
// <div style={{ display: "flex" }}>
// <Button
// id="second-step"
// color="primary"
// style={{ marginTop: "20px" }}
// variant="outlined"
// onClick={() => setModalOpen(true)}
// >
// New workflow
// </Button>
// <span style={{ paddingTop: 20, display: "flex" }}>
// <Typography
// style={{ marginTop: 5, marginLeft: 30, marginRight: 15 }}
// >
// ..OR
// </Typography>
// {workflowButtons}
// </span>
// </div>
// </Paper>
// </div>
//)
} }
var workflowDelay = -150 var workflowDelay = -150
var appDelay = -75 var appDelay = -75
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
return ( return (
<div style={viewStyle}> <div style={viewStyle}>
<div style={workflowViewStyle}> <div style={workflowViewStyle}>
<div style={{ display: "flex", marginTop: 25, }}> <div style={{ display: "flex", marginTop: 25, }}>
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<Typography variant="h1" style={{fontSize: 30}}> <Typography variant="h1" style={{fontSize: 30}}>
Workflows Workflows
</Typography> </Typography>
</div> </div>
{/* {/*
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
@@ -3243,6 +3277,7 @@ const Workflows = (props) => {
minWidth: 275, minWidth: 275,
}, },
}} }}
rows={1}
placeholder="Filter Workflows" placeholder="Filter Workflows"
color="primary" color="primary"
fullWidth fullWidth
@@ -3268,53 +3303,6 @@ const Workflows = (props) => {
{workflowButtons} {workflowButtons}
</div> </div>
</div> </div>
{/*
<div style={flexContainerStyle}>
<div style={{...flexBoxStyle, ...activeWorkflowStyle}}>
<div style={flexContentStyle}>
<div><img src={mobileImage} style={iconStyle} /></div>
<div style={ blockRightStyle }>
<div style={counterStyle}>{workflows.length}</div>
<div style={fontSize_16}>ACTIVE WORKFLOWS</div>
</div>
</div>
</div>
<div style={{...flexBoxStyle, ...availableWorkflowStyle}}>
<div style={flexContentStyle}>
<div><img src={bookImage} style={iconStyle} /></div>
<div style={ blockRightStyle }>
<div style={counterStyle}>{workflows.length}</div>
<div style={fontSize_16}>AVAILABE WORKFLOWS</div>
</div>
</div>
</div>
<div style={{...flexBoxStyle, ...notificationStyle}}>
<div style={flexContentStyle}>
<div><img src={bagImage} style={iconStyle} /></div>
<div style={ blockRightStyle }>
<div style={counterStyle}>{workflows.length}</div>
<div style={fontSize_16}>NOTIFICATIONS</div>
</div>
</div>
</div>
</div>
*/}
{/*
chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {
console.log("VALUE: ", value)
return (
<Chip
key={key}
style={chipStyle}
>
{value}
</Chip>
)
}}
*/}
<div style={{width: "100%", minHeight: isMobile ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}> <div style={{width: "100%", minHeight: isMobile ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}>
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ? {!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ?
@@ -3772,12 +3760,12 @@ const Workflows = (props) => {
} }
const gettingStartedDrawer = const gettingStartedDrawer =
<Drawer <Drawer
anchor={"right"} anchor={"right"}
open={drawerOpen} open={drawerOpen}
variant="persistent" variant="persistent"
keepMounted={true} keepMounted={true}
PaperProps={{ PaperProps={{
style: { style: {
resize: "both", resize: "both",
overflow: "auto", overflow: "auto",
@@ -3928,6 +3916,7 @@ const Workflows = (props) => {
{modalOpen === true ? {modalOpen === true ?
<EditWorkflow <EditWorkflow
globalUrl={globalUrl} globalUrl={globalUrl}
userdata={userdata}
workflow={editingWorkflow} workflow={editingWorkflow}
setWorkflow={setEditingWorkflow} setWorkflow={setEditingWorkflow}
modalOpen={modalOpen} modalOpen={modalOpen}
@@ -3938,6 +3927,7 @@ const Workflows = (props) => {
isEditing={isEditing} isEditing={isEditing}
workflows={workflows} workflows={workflows}
apps={apps}
setWorkflows={setWorkflows} setWorkflows={setWorkflows}
/> />
: null} : null}
+8 -8
View File
@@ -1,5 +1,5 @@
NAME=shuffle-worker NAME=shuffle-worker
VERSION=1.2.1 VERSION=1.2.2
echo "Running docker build with $NAME:$VERSION" echo "Running docker build with $NAME:$VERSION"
#CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
@@ -11,10 +11,10 @@ docker build . -t frikky/shuffle:$NAME -t frikky/shuffle:$NAME_$VERSION -t docke
#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION #docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION
#docker tag frikky/shuffle:0.8.51 ghcr.io/frikky/shuffle-worker:0.8.5 #docker tag frikky/shuffle:0.8.51 ghcr.io/frikky/shuffle-worker:0.8.5
#docker tag frikky/shuffle:$NAME ghcr.io/frikky/shuffle-worker:0.8.52 #docker tag frikky/shuffle:$NAME ghcr.io/frikky/shuffle-worker:0.8.52
docker push frikky/shuffle:$NAME #docker push frikky/shuffle:$NAME
docker push ghcr.io/frikky/$NAME:$VERSION #docker push ghcr.io/frikky/$NAME:$VERSION
docker push ghcr.io/frikky/$NAME:nightly #docker push ghcr.io/frikky/$NAME:nightly
#
docker push shuffle/shuffle:$NAME #docker push shuffle/shuffle:$NAME
docker push ghcr.io/shuffle/$NAME:$VERSION #docker push ghcr.io/shuffle/$NAME:$VERSION
docker push ghcr.io/shuffle/$NAME:nightly #docker push ghcr.io/shuffle/$NAME:nightly
+7 -5
View File
@@ -11,7 +11,7 @@ require (
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.4.17 github.com/shuffle/shuffle-shared v0.4.50
) )
require ( require (
@@ -47,16 +47,18 @@ require (
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opensearch-project/opensearch-go v1.1.0 // indirect
github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect golang.org/x/crypto v0.1.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect golang.org/x/net v0.7.0 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
golang.org/x/sys v0.1.0 // indirect golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.1.12 // indirect golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.106.0 // indirect google.golang.org/api v0.106.0 // indirect
+53
View File
@@ -60,17 +60,34 @@ github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ=
github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg=
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ=
github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk=
github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4=
github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw= github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw=
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y=
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -86,6 +103,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM= github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM=
github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ= github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ=
@@ -144,6 +162,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
@@ -155,6 +174,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@@ -181,6 +201,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -201,6 +223,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M=
github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo=
github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ=
github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -222,6 +248,8 @@ github.com/shuffle/shuffle-shared v0.4.9 h1:mGCaLcSbrsQCy26pJXPlZAtitEzEEwqotGNj
github.com/shuffle/shuffle-shared v0.4.9/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ= github.com/shuffle/shuffle-shared v0.4.9/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ=
github.com/shuffle/shuffle-shared v0.4.17 h1:56ll366bdmIJu/7GFqNC2XTjjl0SGBf430PSq+EB6Ro= github.com/shuffle/shuffle-shared v0.4.17 h1:56ll366bdmIJu/7GFqNC2XTjjl0SGBf430PSq+EB6Ro=
github.com/shuffle/shuffle-shared v0.4.17/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ= github.com/shuffle/shuffle-shared v0.4.17/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ=
github.com/shuffle/shuffle-shared v0.4.50 h1:fJLfhWIJ5mYap4JwHnD/B5aaLyIULwylFSl3FoWlajM=
github.com/shuffle/shuffle-shared v0.4.50/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -235,10 +263,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -256,6 +286,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -322,8 +354,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -344,6 +382,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -375,17 +414,31 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=