Merge branch '2.0.0' of https://github.com/Shuffle/Shuffle into flipBranch

This commit is contained in:
monilprajapati
2024-07-04 17:28:31 +05:30
11 changed files with 1550 additions and 955 deletions
+76 -11
View File
@@ -79,6 +79,7 @@ const AppGrid = (props) => {
const [formMail, setFormMail] = React.useState("");
const [message, setMessage] = React.useState("");
const [formMessage, setFormMessage] = React.useState("");
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const buttonStyle = {
borderRadius: 30,
@@ -352,7 +353,7 @@ const AppGrid = (props) => {
if (responseJson.success === false) {
toast.error(responseJson.reason);
} else {
toast.success(`App ${type}d Successfully!`);
//toast.success(`App ${type}d Successfully!`);
if (type === 'activate') {
setAllActivatedAppIds(prev => [...prev, data.objectID]);
setIsAnyAppActivated(true);
@@ -395,7 +396,7 @@ const AppGrid = (props) => {
{!isLoading ? (
<div>
{hits.length === 0 && searchQuery.length >= 0 && showNoAppFound ? (
<Typography variant="body1" style={{ marginTop: '30%' }}>No App Found</Typography>
<Typography variant="body1" style={{ marginTop: '30%' }}>No Apps Found</Typography>
) : (
<Grid item spacing={2} justifyContent="flex-start">
<div
@@ -1551,9 +1552,13 @@ const AppGrid = (props) => {
<Typography variant="h5" style={{ marginBottom: 30, marginTop: 30, fontWeight: "400", fontSize: 24 }}>
Filter By
</Typography>
<FilterUsersAndOrgsAppByCategory selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps} setselectedCategoryForUsersAndOgsApps={setselectedCategoryForUsersAndOgsApps} />
<FilterUsersAndOrgsAppByActionLabel selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps} setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps} />
<FilterUsersAndOrgsAppByCreatedWith selectedOptionOfCreatedWith={selectedOptionOfCreatedWith} setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith} />
<FilterUsersAndOrgsAppCreatedBy />
</div>
)}
@@ -1722,6 +1727,10 @@ const AppGrid = (props) => {
? `/apps/${data.id}`
: `https://shuffler.io/apps/${data.id}`;
if (data.name === "" && data.id === "") {
return null
}
return (
<Zoom
key={index}
@@ -1818,17 +1827,71 @@ const AppGrid = (props) => {
width: 230,
textAlign: 'start',
marginLeft: 8,
color: "rgba(158, 158, 158, 1)"
color: "rgba(158, 158, 158, 1)",
display: "flex",
}}
>
{data.tags &&
data.tags.map((tag, tagIndex) => (
<span key={tagIndex}>
{normalizedString(tag)}
{tagIndex < data.tags.length - 1 ? ", " : ""}
</span>
))}
</div>
<div style={{minWidth: 120, overflow: "hidden", }}>
{data.generated !== true ?
<div>
{data.tags &&
data.tags.slice(0,2).map((tag, tagIndex) => (
<span key={tagIndex}>
{normalizedString(tag)}
{tagIndex < data.tags.length - 1 ? ", " : ""}
</span>
))
}
</div>
: null}
</div>
{currTab === 1 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true ?
<Button style={{
marginLeft: 15,
width: 102,
height: 35,
borderRadius: 200,
backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
}}
onClick={(event) => {
//deactivatedIndexes.push(index)
//setDeactivatedIndexes(deactivatedIndexes)
event.preventDefault();
event.stopPropagation();
//handleActivateButton(event, data, "deactivate");
// FIXME: Put this in a function lol
const url = `${globalUrl}/api/v1/apps/${data.id}/deactivate`;
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
credentials: "include",
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.success === false) {
toast.error(responseJson.reason);
} else {
toast.success("App Deactivated Successfully. Reload UI to see updated changes.")
//const updatedIds = allActivatedAppIds.filter(id => id !== data.objectID);
//setAllActivatedAppIds(updatedIds);
}
})
.catch(error => {
console.log("app error: ", error.toString());
})
}}>
Deactivate
</Button>
: null}
</div>
{/* )} */}
</div>
</ButtonBase>
@@ -1946,6 +2009,7 @@ const AppGrid = (props) => {
selectedOptionOfCreatedWith={selectedOptionOfCreatedWith}
/>
)}
<AppTab
selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps}
selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps}
@@ -1954,6 +2018,7 @@ const AppGrid = (props) => {
setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps}
setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith}
/>
</div>
<Configure clickAnalytics />
</InstantSearch>
+5 -5
View File
@@ -1623,12 +1623,12 @@ const Billing = (props) => {
{userdata.support === true ?
<div style={{ marginBottom: 10, marginTop: clickedFromOrgTab ? 16 : null, color: clickedFromOrgTab ? "#F1F1F1" : null }}>
For sales: Create&nbsp;
<a href={"https://docs.google.com/document/d/1OeJSi42812EMg7fUAw1HAj1ymOG8rfp8Ma_DGJKvwgI/copy?usp=sharing&organization=" + selectedOrganization.id} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
New Cloud Contract
<a href={"https://docs.google.com/document/d/1N-ZJNn8lWaqiXITrqYcnTt53oXGLNYFEzc5PU-tdAps/copy" + selectedOrganization.id} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
EU contract
</a>
&nbsp;or&nbsp;
<a href={"https://docs.google.com/document/d/1IguxpeV4Wwwr9C0MPyUNhhajEu_PxjRfP7f0hYyRYOI/copy?usp=sharing&organization=" + selectedOrganization.id} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
New Onprem Contract
<a href={"https://docs.google.com/document/d/1cF-Cwxt1TcahlLrpl1GH2hFZO4gxppLeYjHW6QB0EzQ/copy" + selectedOrganization.id} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
NOT EU contract
</a>
&nbsp; - &nbsp;
<a href={"https://drive.google.com/drive/folders/1zVvwwkbQXW3p-DJYa0GBDzFo_ZnV_I_5"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
@@ -1636,7 +1636,7 @@ const Billing = (props) => {
</a>
&nbsp; - &nbsp;
<a href={"https://github.com/Shuffle/Shuffle-docs/tree/master/handbook/Sales"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
Sales Process
Sales Process (old)
</a>
</div>
:
+12 -1
View File
@@ -6,6 +6,7 @@ import UsecaseSearch from "../components/UsecaseSearch.jsx"
import WorkflowGrid from "../components/WorkflowGrid.jsx"
import dayjs from 'dayjs';
import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx";
import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"
import {
Badge,
@@ -206,6 +207,16 @@ const EditWorkflow = (props) => {
<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>
</Typography>
<div style={{marginTop: 10, marginBottom: 10, marginRight: 50, }}>
<WorkflowValidationTimeline
originalWorkflow={workflow}
apps={apps}
workflow={workflow}
/>
</div>
{showUpload === true ?
<div style={{ float: "right" }}>
<Tooltip color="primary" title={"Import manually"} placement="top">
@@ -957,7 +968,7 @@ const EditWorkflow = (props) => {
}}
>
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
{showMoreClicked ? "Collapse": "Expand"}
{showMoreClicked ? "Less Options": "More Options"}
</Button>
</Tooltip>
</div>
+11 -11
View File
@@ -515,16 +515,16 @@ const LicencePopup = (props) => {
}
useEffect(() => {
console.log("New variant: ", shuffleVariant)
console.log("New variant: ", shuffleVariant)
if (shuffleVariant === 1) {
setCalculatedCost("$600")
setSelectedValue(8)
} else {
setCalculatedCost("$540")
setSelectedValue(300)
}
}, [shuffleVariant])
if (shuffleVariant === 1) {
setCalculatedCost("$960")
setSelectedValue(8)
} else {
setCalculatedCost("$960")
setSelectedValue(300)
}
}, [shuffleVariant])
if (typeof window === 'undefined' || window.location === undefined) {
return null
@@ -680,7 +680,7 @@ const LicencePopup = (props) => {
color: "white",
}
console.log("Priceitem: ", shuffleVariant)
const isLoggedInHandler = () => {
if (calculatedCost === payasyougo) {
handlePayasyougo(props.userdata)
@@ -690,7 +690,7 @@ const LicencePopup = (props) => {
const priceItem = window.location.origin === "https://shuffler.io" ?
shuffleVariant === 0 ? "app_executions" : "cores"
:
shuffleVariant === 0 ? "price_1MROFrDzMUgUjxHShcSxgHO1" : "price_1NXjQqDzMUgUjxHSg690R4FP"
shuffleVariant === 0 ? "price_1PWI5zDzMUgUjxHSKkz0fGdN" : "price_1NXjQqDzMUgUjxHSg690R4FP"
const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
const failUrl = `${window.location.origin}/pricing?admin_tab=billing&payment=failure`
+6 -6
View File
@@ -517,12 +517,11 @@ const Header = (props) => {
window.location.href = responseJson["url"]
return
}, 2000)
}
} else {
setTimeout(() => {
window.location.reload()
}, 2000);
}
toast("Successfully changed active organization - refreshing!");
} else {
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
@@ -1547,7 +1546,7 @@ const Header = (props) => {
<div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}>
<Typography variant="body1" style={{ paddingTop: 7, margin: "auto", textAlign: "center", color: "white", }}>
{/* Shuffle 1.4.0 is out! Read more about&nbsp; */}
Shuffle now offers&nbsp;
Early Success! More&nbsp;
{/* <u>
<a href="https://github.com/Shuffle/Shuffle/releases/tag/v1.4.0" target="_blank" style={{ color: "inherit", }} onClick={() => {
ReactGA.event({
@@ -1596,10 +1595,11 @@ const Header = (props) => {
navigate("/training")
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
Public Training!
}} style={{ cursor: "pointer", textDecoration: "none", fontWeight:600, color: "rgba(255,255,255,0.8)" }}>
Public Trainings
</span>
</u>
&nbsp;Ahead!
</Typography>
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => {
setShowTopbar(false)
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -307,7 +307,6 @@ const ParsedAction = (props) => {
(action) =>
action.name.toLowerCase() === selectedAction.name.toLowerCase()
);
console.log("FOUNDACTION: ", foundAction);
if (foundAction !== null && foundAction !== undefined) {
var foundparams = [];
for (let [paramkey,paramkeyval] in Object.entries(foundAction.parameters)) {
@@ -549,6 +548,11 @@ const ParsedAction = (props) => {
}
}
}
if (parentNode.label === undefined) {
parentNode.label = ""
}
newActionList.push({
type: "action",
id: parentNode.id,
@@ -0,0 +1,443 @@
import React, { useState, } from "react";
import { makeStyles, createStyles } from "@mui/styles";
import { toast } from "react-toastify"
import {
Tooltip,
Typography,
Avatar,
AvatarGroup,
} from "@mui/material"
import {
green,
yellow,
red,
} from "../views/AngularWorkflow.jsx"
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import theme from "../theme.jsx";
const itemHeight = 40
export const getParentNodes = (workflow, action) => {
if (action === undefined || action === null) {
return []
}
if (workflow.actions === undefined || workflow.actions === null) {
workflow.actions = []
}
if (workflow.triggers === undefined || workflow.triggers === null) {
workflow.triggers = []
}
if (workflow.branches === undefined || workflow.branches === null) {
workflow.branches = []
}
var allkeys = [action.id];
var handled = [];
var results = [];
// maxiter = max amount of parent nodes to loop
// also handles breaks if there are issues
var iterations = 0;
var maxiter = 10;
while (true) {
for (let parentkey in allkeys) {
if (allkeys[parentkey] === undefined) {
continue
}
var currentnode = workflow.actions.find((element) => element.id === allkeys[parentkey])
if (currentnode === undefined) {
currentnode = workflow.triggers.find((element) => element.id === allkeys[parentkey])
if (currentnode === undefined) {
console.log("Could not find parent node for: ", allkeys[parentkey])
continue
}
}
if (handled.includes(currentnode.id)) {
continue
} else {
handled.push(currentnode.id);
results.push(currentnode);
}
// Get the name / label here too?
if (currentnode.length === 0) {
continue;
}
// FIXME: This part is only handling first level,
// but needs to recurse
var incomingEdges = []
for (var branchkey in workflow.branches) {
const branch = workflow.branches[branchkey]
if (branch.destination_id !== currentnode.id) {
continue
}
// Go up in the levels
const parents = getParentNodes(workflow, {
id: branch.source_id,
})
if (parents.length > 0) {
incomingEdges = incomingEdges.concat(parents)
}
incomingEdges.push(branch)
}
for (let i = 0; i < incomingEdges.length; i++) {
var tmp = incomingEdges[i];
if (tmp.decorator === true) {
continue
}
if (!allkeys.includes(tmp.source_id)) {
allkeys.push(tmp.source_id)
}
}
}
if (results.length === allkeys.length || iterations === maxiter) {
break
}
iterations += 1
}
// Remove on the end as we don't want to remove everything
results = results.filter((data) => data.id !== action.id)
results = results.filter((data) => data.type === "ACTION" || data.app_name === "Shuffle Workflow" || data.app_name === "User Input" || data.app_name === "shuffle-subflow")
results.push({ label: "Execution Argument", type: "INTERNAL" })
return results
}
const WorkflowValidationTimeline = (props) => {
const { workflow, originalWorkflow, apps, getParents, execution} = props
if (workflow === undefined || workflow === null) {
return null
}
if (workflow.validation === undefined || workflow.validation === null) {
return null
}
if (workflow.actions === undefined || workflow.actions === null) {
workflow.actions = []
}
if (workflow.triggers === undefined || workflow.triggers === null) {
workflow.triggers = []
}
if (workflow.branches === undefined || workflow.branches === null) {
workflow.branches = []
}
var results = []
if (execution !== undefined) {
results = execution.results
}
// 1. Find startnode
// 2. Map childnodes from it
const startnodeId = workflow.start
// Find parent of startnodeId and if it's a webhook
var relevantactions = []
for (var key in workflow.branches) {
const branch = workflow.branches[key]
if (branch.destination_id !== startnodeId) {
continue
}
for (var triggerkey in workflow.triggers) {
const trigger = workflow.triggers[triggerkey]
if (trigger.trigger_type !== "WEBHOOK") {
continue
}
if (trigger.id === branch.source_id) {
trigger.order = -1
relevantactions.push(trigger)
break
}
}
}
if (getParents !== undefined) {
for (var key in workflow.actions) {
const action = workflow.actions[key]
if (action.id === startnodeId) {
action.order = 0
relevantactions.push(action)
continue
}
const parents = getParents(action)
//const parents = getParentNodes(workflow, action)
//console.log("PARENTS", key, parents)
if (parents !== undefined && parents !== null) {
const parentfound = parents.find((element) => element.id === startnodeId)
if (parentfound !== undefined) {
// FIXME: add order here based on how many steps away from the startnode
// This just has the parent count
action.order = parents.length
relevantactions.push(action)
}
}
}
} else {
for (var key in workflow.triggers) {
const trigger = workflow.triggers[key]
if (trigger.trigger_type !== "SUBFLOW" && trigger.trigger_type !== "USERINPUT") {
continue
}
if (workflow.actions.find((element) => element.id === trigger.id) === undefined) {
workflow.actions.push(trigger)
}
}
relevantactions = workflow.actions
}
// Sort according to how many parents a node has. MAY be wrong~
relevantactions.sort((a, b) => {
if (a.order === undefined) {
return 1
}
if (b.order === undefined) {
return -1
}
return a.order - b.order
})
// FIXME: Add other relevant items as well from subflows (?)
var nodecolor = "grey"
var branchcolor = "grey"
var skipped = false
var previousTools = false
return (
<div style={{border: workflow.validation.valid ? `2px solid ${green}` : "1px solid rgba(255,255,255,0.4)", padding: "10px 20px 10px 20px", borderRadius: theme.palette.borderRadius, }}>
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
{relevantactions.map((action, index) => {
action.result = {}
if (results !== undefined) {
const foundResult = results.find((element) => element.action.id === action.id)
if (foundResult !== undefined) {
action.result = foundResult
action.status = foundResult.status
}
}
const lastitem = index === relevantactions.length - 1
if (!lastitem) {
if (action.app_name === "Shuffle Tools") {
if (action.status === "SUCCESS") {
branchcolor = red
// Check action.result for the actual status
const validate = validateJson(action.result.result)
if (validate.valid) {
if (validate.result.success === true) {
branchcolor = green
} else {
branchcolor = "grey"
}
}
} else if (action.status === "SKIPPED") {
branchcolor = "grey"
} else {
if (action.status === undefined) {
branchcolor = green
} else {
branchcolor = red
}
}
previousTools = true
return null
} else {
if (action.status === "SUCCESS") {
nodecolor = green
} else if (action.status === "SKIPPED") {
nodecolor = "grey"
} else {
if (action.status === undefined) {
nodecolor = green
} else {
nodecolor = red
}
}
}
} else {
nodecolor = "grey"
}
if (action.status === "SKIPPED") {
skipped = true
}
var image = ""
if (action.large_image !== undefined && action.large_image !== null && action.large_image !== "") {
image = action.large_image
} else {
if (originalWorkflow !== undefined) {
for (var key in originalWorkflow.actions) {
if (originalWorkflow.actions[key].id === action.id) {
image = originalWorkflow.actions[key].large_image
break
}
}
if (image === "") {
for (var key in originalWorkflow.triggers) {
if (originalWorkflow.triggers[key].id === action.id) {
image = originalWorkflow.triggers[key].large_image
break
}
}
}
}
}
var founderror = ""
if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.errors !== undefined && workflow.validation.errors !== null) {
const foundError = workflow.validation.errors.find((element) => element.action_id === action.id)
if (foundError !== undefined) {
founderror = foundError.error
nodecolor = yellow
branchcolor = yellow
}
}
if (skipped && !lastitem) {
nodecolor = "grey"
branchcolor = "grey"
}
if (previousTools) {
previousTools = false
} else {
branchcolor = nodecolor
}
var appgroup = []
if (action.trigger_type === "WEBHOOK") {
nodecolor = green
branchcolor = green
} else if (action.app_name === "shuffle-subflow") {
if (action.status === "SUCCESS") {
nodecolor = green
branchcolor = green
}
for (var subflowkey in workflow.validation.subflow_apps) {
const subflowApp = workflow.validation.subflow_apps[subflowkey]
if (subflowApp.error === action.id) {
appgroup.push(subflowApp)
}
}
}
var flex = index !== 0 && index !== relevantactions.length - 1 ? 1 : 3
const branchTooltip = branchcolor === yellow ? "Check nodes for errors" : ""
return (
<div style={{display: "flex", flex: flex, justifyContent: "right", }}>
{lastitem ?
<Tooltip title={branchTooltip}>
<div style={{marginLeft: 0, marginRight: 0, marginTop: 20, height: 3, width: "100%", backgroundColor: branchcolor, }} />
</Tooltip>
: null}
{appgroup.length > 0 ?
<AvatarGroup max={4} style={{height: itemHeight+8, }}>
{appgroup.map((subflowApp, subflowIndex) => {
var appimage = ""
if (apps !== undefined && apps !== null && apps.length > 0) {
for (var key in apps) {
const app = apps[key]
if (app.name === subflowApp.app_name) {
appimage = apps[key].large_image
break
}
}
}
return (
<Tooltip title={`App: ${subflowApp.app_name} - subflow app`}>
<Avatar
style={{
border: `4px solid ${nodecolor}`,
}}
src={appimage}
/>
</Tooltip>
)
})}
</AvatarGroup>
:
<Tooltip title={
<Typography variant="body1" style={{margin: 5, color: "white", }}>
{founderror.length > 0 ? founderror : ``}
</Typography>
} placement="top">
<Avatar
variant="round"
sx={{ backgroundColor: nodecolor, width: itemHeight, height: itemHeight, borderRadius: 50, border: `4px solid ${nodecolor}`,}}
>
{image !== "" ?
<img
src={image}
style={{
width: itemHeight,
height: itemHeight,
}}
/>
: null}
</Avatar>
</Tooltip>
}
{lastitem ? null :
<Tooltip title={branchTooltip}>
<div style={{marginLeft: 0, marginRight: 0, marginTop: 20, height: 3, width: "100%", backgroundColor: branchcolor, }} />
</Tooltip>
}
</div>
)
})}
</div>
</div>
)
}
export default WorkflowValidationTimeline
+5 -1
View File
@@ -5385,13 +5385,17 @@ If you're interested, please let me know a time that works for you, or set up a
<ListItem key={index} style={{ backgroundColor: bgColor }}>
<ListItemText
primary=
<Tooltip title={data.validation.valid === true ? "Valid. Click to explore." : "Configuration failed. Click to learn why"} placement="top">
<Tooltip title={data.validation !== null && data.validation !== undefined && data.validation.valid === true ? "Valid. Click to explore." : "Configuration failed. Click to learn why"} placement="top">
<IconButton>
{validIcon}
</IconButton>
</Tooltip>
style={{ minWidth: 65, maxWidth: 65, }}
onClick={() => {
if (data.validation === null || data.validation === undefined) {
return
}
if (data.validation.workflow_id === undefined || data.validation.workflow_id === null || data.validation.workflow_id.length === 0) {
toast.warn("No workflow runs found for this auth yet. Check back later.")
return
+28 -7
View File
@@ -139,6 +139,7 @@ import Draggable from "react-draggable";
import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
@@ -319,6 +320,7 @@ export function SetJsonDotnotation(jsonInput, inputKey) {
export const green = "#86c142";
export const yellow = "#FECC00";
export const red = "red";
export function removeParam(key, sourceURL) {
if (sourceURL === undefined) {
@@ -1641,7 +1643,6 @@ const releaseToConnectLabel = "Release to Connect"
setExecutionData(responseJson)
} else {
if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING" || responseJson.status === "FINISHED") {
console.log("DONE!")
stop()
}
@@ -6732,7 +6733,6 @@ const releaseToConnectLabel = "Release to Connect"
const parentlabel = parentNode.data("label").toLowerCase().replace(" ", "_")
const parentname = parentNode.data("app_name").toLowerCase().replace(" ", "_")
if (!parentlabel.startsWith(parentname)+"_") {
console.log("Return 1")
return
}
@@ -7135,7 +7135,6 @@ const releaseToConnectLabel = "Release to Connect"
// Find how many executions it has
var executions = 0
const matchingExecutions = workflowExecutions.filter((execution => execution.execution_source === nodedata.app_name.toLowerCase()))
console.log("Matches: ", matchingExecutions.length)
const color = matchingExecutions.length > 0 ? "#34a853" : "#ea4436"
const decoratorNode = {
position: {
@@ -10204,6 +10203,7 @@ const releaseToConnectLabel = "Release to Connect"
// Starts on current node and climbs UP the tree to the root object.
// Sends back everything in it's path
// FIXME: Use the GetParentNodes in WorkflowValidationTimeline.jsx instead
const getParents = (action) => {
if (action === undefined || action === null) {
return []
@@ -18720,6 +18720,7 @@ const releaseToConnectLabel = "Release to Connect"
: null}
</div>
{executionData.workflow !== undefined && executionData.workflow !== null && executionData.workflow.actions !== undefined && executionData.workflow.actions !== null && executionData.workflow.actions.length > 0 ?
<div style={{ display: "flex", marginLeft: 10, }}>
<Typography variant="body1">
@@ -18831,12 +18832,31 @@ const releaseToConnectLabel = "Release to Connect"
{new Date(executionData.completed_at * 1000).toLocaleString("en-GB")}
</Typography>
</div>
) : null}
{executionData.workflow !== undefined && executionData.workflow !== null && executionData.status !== "EXECUTING" ?
<div style={{marginTop: 5, marginBottom: 5, }}>
<WorkflowValidationTimeline
originalWorkflow={workflow}
apps={apps}
workflow={executionData.workflow}
getParents={getParents}
execution={executionData}
/>
</div>
: null}
<div style={{ marginTop: 10 }} />
{executionData.execution_argument !== undefined &&
executionData.execution_argument.length > 0
{executionData.execution_argument !== undefined && executionData.execution_argument !== null &&
executionData.execution_argument.length > 1
? parsedExecutionArgument()
: null}
:
null}
<Divider
style={{
backgroundColor: "rgba(255,255,255,0.6)",
@@ -18844,6 +18864,7 @@ const releaseToConnectLabel = "Release to Connect"
marginBottom: 20,
}}
/>
{executionData.results !== undefined &&
executionData.results !== null &&
executionData.results.length > 1 &&
@@ -19538,7 +19559,7 @@ const releaseToConnectLabel = "Release to Connect"
if (stringjson.includes("\n") && !stringjson.includes("\n")) {
return "Looks like you have a newline problem. Consider using the | replace: '\n', '\\n' }} filter in Liquid."
} else {
return "The result looks like it should be JSON, but is invalid. Look for potential"
return "The result looks like it should be JSON, but is invalid. Look for potential single quotes instead of double quotes, missing commas or newlines"
}
}
}
+49 -3
View File
@@ -178,6 +178,51 @@ func getThisContainerId() {
}
func cleanupExistingNodes(ctx context.Context) error {
if isKubernetes == "true" {
if kubernetesNamespace == "" {
kubernetesNamespace = "default"
}
clientset, _, err := shuffle.GetKubernetesClient()
if err != nil {
log.Printf("[ERROR] Error getting kubernetes client:", err)
return err
}
// Delete all pods
pods, err := clientset.CoreV1().Pods(kubernetesNamespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
log.Printf("[ERROR] Failed listing pods: %s", err)
return err
}
for _, pod := range pods.Items {
err := clientset.CoreV1().Pods(kubernetesNamespace).Delete(context.Background(), pod.Name, metav1.DeleteOptions{})
if err != nil {
log.Printf("[ERROR] Failed deleting pod %s: %s", pod.Name, err)
}
}
// Delete all services
services, err := clientset.CoreV1().Services(kubernetesNamespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
log.Printf("[ERROR] Failed listing services: %s", err)
return err
}
for _, service := range services.Items {
err := clientset.CoreV1().Services(kubernetesNamespace).Delete(context.Background(), service.Name, metav1.DeleteOptions{})
if err != nil {
log.Printf("[ERROR] Failed deleting service %s: %s", service.Name, err)
}
}
log.Printf("[INFO] Cleaned up all pods and services in namespace %s", kubernetesNamespace)
return nil
}
serviceListOptions := types.ServiceListOptions{}
services, err := dockercli.ServiceList(
context.Background(),
@@ -1662,11 +1707,12 @@ func main() {
if swarmConfig == "run" || swarmConfig == "swarm" || isKubernetes == "true" {
if isKubernetes != "true" {
checkSwarmService(ctx)
log.Printf("[DEBUG] Cleaning up containers from previous run")
cleanupExistingNodes(ctx)
time.Sleep(time.Duration(5) * time.Second)
}
log.Printf("[DEBUG] Cleaning up containers from previous run")
cleanupExistingNodes(ctx)
time.Sleep(time.Duration(5) * time.Second)
log.Printf("[DEBUG] Deploying worker image %s to swarm", workerImage)
runString := "Run: \"docker service ls\" for more info"