+
{modalView}
{cloudSyncModal}
{editUserModal}
diff --git a/frontend/src/views/Admin2.jsx b/frontend/src/views/Admin2.jsx
new file mode 100644
index 00000000..222ea44b
--- /dev/null
+++ b/frontend/src/views/Admin2.jsx
@@ -0,0 +1,338 @@
+import React, { useEffect, useState } from 'react';
+import AdminNavBar from '../components/AdminNavBar.jsx';
+import { toast } from "react-toastify";
+
+const Admin2 = (props) => {
+ // Destructure props if needed
+ const { userdata, globalUrl, serverside, checkLogin, notifications, setNotifications, stripeKey, isLoaded, isLoggedIn} = props;
+ const [selectedTab, setSelectedTab] = useState('editdetails');
+ const [selectedStatus, setSelectedStatus] = React.useState([]);
+ const [selectedOrganization, setSelectedOrganization] = useState({});
+ const [organizationFeatures, setOrganizationFeatures] = useState({});
+ const [orgRequest, setOrgRequest] = React.useState(true);
+ const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
+ const handleGetOrg = (orgId) => {
+ if (
+ serverside !== true &&
+ window.location.search !== undefined &&
+ window.location.search !== null
+ ) {
+ const urlSearchParams = new URLSearchParams(window.location.search);
+ const params = Object.fromEntries(urlSearchParams.entries());
+ const foundorgid = params["org_id"];
+ if (foundorgid !== undefined && foundorgid !== null) {
+ orgId = foundorgid;
+ }
+ }
+ console.log("getting organization details for: ", orgId);
+
+ // if (orgId === undefined) {
+ // toast(
+ // "Organization ID not defined. Please contact us on https://shuffler.io if this persists logout.",
+ // );
+ // return;
+ // }
+
+ // Just use this one?
+
+ fetch(`${globalUrl}/api/v1/orgs/${orgId}`, {
+ method: "GET",
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then((response) => {
+ if (response.status === 401) {
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson["success"] === false) {
+ toast(
+ "Failed getting your org. If this persists, please contact support. Redirecting to workflows...",
+ );
+ setTimeout(() => {
+ window.location.href = "/workflows";
+ }, 3000);
+ } else {
+ if (
+ responseJson.sync_features === undefined ||
+ responseJson.sync_features === null
+ ) {
+ responseJson.sync_features = {};
+ }
+
+ if (
+ responseJson.lead_info !== undefined &&
+ responseJson.lead_info !== null
+ ) {
+ var leads = [];
+ if (responseJson.lead_info.contacted) {
+ leads.push("contacted");
+ }
+
+ if (responseJson.lead_info.customer) {
+ leads.push("customer");
+ }
+
+ if (responseJson.lead_info.old_customer) {
+ leads.push("old customer");
+ }
+
+ if (responseJson.lead_info.old_lead) {
+ leads.push("old lead");
+ }
+
+ if (responseJson.lead_info.tech_partner) {
+ leads.push("tech partner");
+ }
+
+ if (responseJson.lead_info.creator) {
+ leads.push("creator");
+ }
+
+ if (responseJson.lead_info.opensource) {
+ leads.push("open source");
+ }
+
+ if (responseJson.lead_info.demo_done) {
+ leads.push("demo done");
+ }
+
+ if (responseJson.lead_info.pov) {
+ leads.push("pov");
+ }
+
+ if (responseJson.lead_info.lead) {
+ leads.push("lead");
+ }
+
+ if (responseJson.lead_info.student) {
+ leads.push("student");
+ }
+
+ if (responseJson.lead_info.internal) {
+ leads.push("internal");
+ }
+
+ if (responseJson.lead_info.sub_org) {
+ leads.push("sub_org");
+ }
+
+ setSelectedStatus(leads);
+ }
+
+ setSelectedOrganization(responseJson);
+ var lists = {
+ active: {
+ triggers: [],
+ features: [],
+ sync: [],
+ },
+ inactive: {
+ triggers: [],
+ features: [],
+ sync: [],
+ },
+ };
+
+ // FIXME: Set up features
+ //Object.keys(responseJson.sync_features).map(function(key, index) {
+ // //console.log(responseJson.sync_features[key])
+ //})
+
+ //setOrgName(responseJson.name)
+ //setOrgDescription(responseJson.description)
+ setOrganizationFeatures(lists);
+ }
+ })
+ .catch((error) => {
+ console.log("Error getting org: ", error);
+ toast("Error getting current organization");
+ });
+ };
+
+ const urlSearchParams = new URLSearchParams(window.location.search);
+ const params = Object.fromEntries(urlSearchParams.entries());
+ const foundOrgID = params["org_id"]
+
+ useEffect(() => {
+
+ if(foundOrgID !== null && foundOrgID !== undefined && userdata?.support && foundOrgID?.length > 0) {
+ handleClickChangeOrg(foundOrgID)
+ }
+ }, [foundOrgID]);
+
+ const handleClickChangeOrg = (orgId) => {
+ // Don't really care about the logout
+ //name: org.name,
+ //orgId = "asd"
+ const data = {
+ org_id: orgId,
+ };
+
+ localStorage.setItem("globalUrl", "");
+ localStorage.setItem("getting_started_sidebar", "open");
+
+ fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, {
+ mode: "cors",
+ credentials: "include",
+ crossDomain: true,
+ method: "POST",
+ body: JSON.stringify(data),
+ withCredentials: true,
+ headers: {
+ "Content-Type": "application/json; charset=utf-8",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ } else {
+ localStorage.removeItem("apps")
+ localStorage.removeItem("workflows")
+ localStorage.removeItem("userinfo")
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ if (responseJson.success === true) {
+ if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) {
+ localStorage.setItem("globalUrl", responseJson.region_url)
+ //globalUrl = responseJson.region_url
+ }
+
+ setTimeout(() => {
+ window.location.reload()
+ }, 3000);
+ toast("Successfully changed active organization - refreshing!");
+ } else {
+ if (responseJson.reason !== undefined && responseJson.reason !== null) {
+ if (!responseJson.reason.includes("already")) {
+ toast("Failed changing org: " + responseJson.reason);
+ }
+ } else {
+ toast("Failed changing org")
+ }
+ }
+ })
+ .catch((error) => {
+ console.log("error changing: ", error);
+ //removeCookie("session_token", {path: "/"})
+ });
+ };
+
+ const handleEditOrg = (
+ name,
+ description,
+ orgId,
+ image,
+ defaults,
+ sso_config,
+ lead_info,
+ { mfa_required } = {}
+ ) => {
+ const data = {
+ name: name,
+ description: description,
+ org_id: orgId,
+ image: image,
+ defaults: defaults,
+ sso_config: sso_config,
+ lead_info: lead_info,
+ mfa_required: mfa_required !== undefined ? mfa_required : selectedOrganization?.mfa_required,
+ };
+
+ const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
+ fetch(url, {
+ mode: "cors",
+ method: "POST",
+ body: JSON.stringify(data),
+ credentials: "include",
+ crossDomain: true,
+ withCredentials: true,
+ headers: {
+ "Content-Type": "application/json; charset=utf-8",
+ },
+ })
+ .then((response) =>
+ response.json().then((responseJson) => {
+ if (responseJson["success"] === false) {
+ toast("Failed updating org: ", responseJson.reason);
+ } else {
+ if (
+ lead_info === undefined ||
+ lead_info === null ||
+ lead_info === []
+ ) {
+ toast("Successfully edited org!");
+ }
+ }
+ }),
+ )
+ .catch((error) => {
+ toast("Err: " + error.toString());
+ });
+ };
+
+
+ const handleStatusChange = (event) => {
+ const { value } = event.target;
+ setSelectedStatus(value);
+
+ handleEditOrg(
+ selectedOrganization?.name,
+ selectedOrganization?.description,
+ selectedOrganization.id,
+ selectedOrganization?.image,
+ {
+ app_download_repo: selectedOrganization?.defaults?.app_download_repo,
+ app_download_branch: selectedOrganization?.defaults?.app_download_branch,
+ workflow_download_repo: selectedOrganization?.defaults?.workflow_download_repo,
+ workflow_download_branch: selectedOrganization?.defaults?.workflow_download_branch,
+ notification_workflow: selectedOrganization?.defaults?.notification_workflow,
+ documentation_reference: selectedOrganization?.defaults?.documentation_reference,
+ workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo,
+ workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch,
+ workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username,
+ workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token,
+ newsletter: !selectedOrganization?.defaults?.newsletter,
+ weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations,
+ },
+ {
+ sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint,
+ sso_certificate: selectedOrganization?.sso_config?.sso_certificate,
+ client_id: selectedOrganization?.sso_config?.client_id,
+ client_secret: selectedOrganization?.sso_config?.client_secret,
+ openid_authorization: selectedOrganization?.sso_config?.openid_authorization,
+ openid_token: selectedOrganization?.sso_config?.openid_token,
+ SSORequired: selectedOrganization?.sso_config?.SSORequired,
+ auto_provision: selectedOrganization?.sso_config?.auto_provision,
+ },
+ value.length === 0 ? ["none"] : value,
+ );
+ };
+
+ if (
+ selectedOrganization.id === undefined &&
+ userdata !== undefined &&
+ userdata.active_org !== undefined &&
+ orgRequest
+ ) {
+ const orgId = userdata.active_org.id
+
+ setOrgRequest(false);
+ handleGetOrg(orgId);
+ }
+
+ return (
+
+ );
+};
+
+export default Admin2;
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx
index 510dcbcf..f14fbdff 100755
--- a/frontend/src/views/AngularWorkflow.jsx
+++ b/frontend/src/views/AngularWorkflow.jsx
@@ -1,5 +1,5 @@
/* eslint-disable react/no-multi-comp */
-import React, { useState, useEffect, useLayoutEffect } from "react";
+import React, { useState, useEffect, useLayoutEffect, memo, useMemo, useRef, useContext } from "react";
import ReactDOM from "react-dom"
import theme from "../theme.jsx";
@@ -7,10 +7,10 @@ import { useInterval } from "react-powerhooks";
import { makeStyles, } from "@mui/styles";
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"
-import { v4 as uuidv4 } from "uuid";
+import { v4 as uuidv4, v5 as uuidv5, validate as isUUID, } from "uuid";
import { useNavigate, Link, useParams } from "react-router-dom";
import { useBeforeunload } from "react-beforeunload"
-import ReactJson from "react-json-view";
+import ReactJson from "react-json-view-ssr";
import { NestedMenuItem } from 'mui-nested-menu';
import Markdown from "react-markdown";
//import { useAlert
@@ -22,7 +22,6 @@ import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx";
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
import algoliasearch from 'algoliasearch/lite';
-
import {
Zoom,
Fade,
@@ -70,11 +69,15 @@ import {
AvatarGroup,
Autocomplete,
Radio,
+ ButtonGroup,
} from "@mui/material";
+import CodeIcon from '@mui/icons-material/Code';
+
import {
Folder as FolderIcon,
VerifiedUser as VerifiedUserIcon,
+ CheckCircle as CheckCircleIcon,
Insights as InsightsIcon,
LibraryBooks as LibraryBooksIcon,
OpenInNew as OpenInNewIcon,
@@ -88,6 +91,7 @@ import {
Error as ErrorIcon,
Warning as WarningIcon,
ArrowLeft as ArrowLeftIcon,
+ ArrowRight as ArrowRightIcon,
Cached as CachedIcon,
DirectionsRun as DirectionsRunIcon,
FormatListNumbered as FormatListNumberedIcon,
@@ -123,8 +127,10 @@ import {
Add as AddIcon,
ErrorOutline as ErrorOutlineIcon,
-} from "@mui/icons-material";
+ ArrowForward as ArrowForwardIcon,
+} from "@mui/icons-material";
+import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
//import * as cytoscape from "cytoscape";
import cytoscape from "cytoscape";
@@ -137,7 +143,8 @@ import Draggable from "react-draggable";
import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
-import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
+import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"
+import { validateJson, collapseField, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
@@ -146,6 +153,7 @@ import PaperComponent from "../components/PaperComponent.jsx"
import ExtraApps from "../components/ExtraApps.jsx"
import EditWorkflow from "../components/EditWorkflow.jsx"
import { act } from "react";
+import { Context } from "../context/ContextApi.jsx";
// import AppStats from "../components/AppStats.jsx";
const noImage = "/public/no_image.png";
@@ -181,6 +189,20 @@ export const triggers = [
description: "Schedule time trigger",
long_description: "Create a schedule based on cron",
id: "",
+ },
+ {
+ name: "Pipelines",
+ type: "TRIGGER",
+ status: "uninitialized",
+ description: "Run a pipeline trigger",
+ trigger_type: "PIPELINE",
+ errors: null,
+ is_valid: true,
+ label: "Pipeline",
+ environment: "onprem",
+ large_image: "/images/workflows/tenzir2.png",
+ long_description: "Controls a pipeline to run things",
+ id: "",
},
{
name: "Shuffle Workflow",
@@ -211,20 +233,6 @@ export const triggers = [
long_description: "Take user input to continue execution",
id: "",
},
- {
- name: "Pipelines",
- type: "TRIGGER",
- status: "uninitialized",
- description: "Run a pipeline trigger",
- trigger_type: "PIPELINE",
- errors: null,
- is_valid: true,
- label: "Pipeline",
- environment: "onprem",
- large_image: "/images/workflows/tenzir2.png",
- long_description: "Controls a pipeline to run things",
- id: "",
- },
];
// Adds specific text to items
@@ -315,8 +323,12 @@ export function SetJsonDotnotation(jsonInput, inputKey) {
return jsonInput;
}
-export const green = "#86c142";
+//export const green = "#86c142";
+export const green = "#02CB70"
export const yellow = "#FECC00";
+//export const red = "#ff3632";
+export const red = "#F53434";
+export const grey = "#b0b0b0";
export function removeParam(key, sourceURL) {
if (sourceURL === undefined) {
@@ -348,11 +360,11 @@ export function removeParam(key, sourceURL) {
const useStyles = makeStyles({
notchedOutline: {
- borderColor: "#f85a3e !important",
+ borderColor: "#FF8544 !important",
},
root: {
"& .MuiAutocomplete-listbox": {
- border: "2px solid #f85a3e",
+ border: "2px solid #FF8544",
color: "white",
fontSize: 18,
"& li:nth-child(even)": {
@@ -378,7 +390,7 @@ const svgSize = 24;
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const AngularWorkflow = (defaultprops) => {
- const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id } = defaultprops;
+ const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id, ReactGA, } = defaultprops;
const referenceUrl = globalUrl + "/api/v1/hooks/";
//const alert = useAlert()
let navigate = useNavigate();
@@ -387,6 +399,9 @@ const AngularWorkflow = (defaultprops) => {
props.match = {}
props.match.params = params
+ const { leftSideBarOpenByClick, windowWidth } = useContext(Context)
+ const [workflowAsCode, setWorkflowAsCode] = useState(false);
+
var to_be_copied = "";
const [firstrequest, setFirstrequest] = React.useState(true);
const [cystyle] = useState(cytoscapestyle);
@@ -396,7 +411,6 @@ const AngularWorkflow = (defaultprops) => {
const [toolsApp, setToolsApp] = React.useState({});
const [currentView, setCurrentView] = React.useState(0);
const [triggerAuthentication, setTriggerAuthentication] = React.useState({});
- const [triggerFolders, setTriggerFolders] = React.useState([]);
const [workflows, setWorkflows] = React.useState([]);
const [parentWorkflows, setParentWorkflows] = React.useState([]);
const [showEnvironment, setShowEnvironment] = React.useState(false);
@@ -408,7 +422,7 @@ const AngularWorkflow = (defaultprops) => {
const [subworkflow, setSubworkflow] = React.useState({});
const [subworkflowStartnode, setSubworkflowStartnode] = React.useState("");
const [leftViewOpen, setLeftViewOpen] = React.useState(isMobile ? false : true);
- const [leftBarSize, setLeftBarSize] = React.useState(isMobile ? 0 : 325)
+ const [leftBarSize, setLeftBarSize] = React.useState(isMobile ? 0 : 235)
const [creatorProfile, setCreatorProfile] = React.useState({});
const [usecases, setUsecases] = React.useState([]);
const [files, setFiles] = React.useState({
@@ -430,7 +444,7 @@ const AngularWorkflow = (defaultprops) => {
const [history, setHistory] = React.useState([]);
const [historyIndex, setHistoryIndex] = React.useState(history.length);
const [variableInfo, setVariableInfo] = React.useState({})
-
+ const [selectedVersion, setSelectedVersion] = React.useState(null)
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
const [variablesModalOpen, setVariablesModalOpen] = React.useState(false);
const [aiQueryModalOpen, setAiQueryModalOpen] = React.useState(false)
@@ -450,6 +464,10 @@ const AngularWorkflow = (defaultprops) => {
const [showSkippedActions, setShowSkippedActions] = React.useState(false);
const [lastExecution, setLastExecution] = React.useState("");
const [configureWorkflowModalOpen, setConfigureWorkflowModalOpen] = React.useState(false);
+ const [autoCompleting, setAutocompleting] = React.useState(false);
+
+ const [authgroupModalOpen, setAuthgroupModalOpen] = React.useState(false);
+ const [authGroups, setAuthGroups] = React.useState([])
const curpath = typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname;
@@ -467,7 +485,6 @@ const AngularWorkflow = (defaultprops) => {
const [conditionValue, setConditionValue] = React.useState({});
const [dragging, setDragging] = React.useState(false);
const [showWorkflowRevisions, setShowWorkflowRevisions] = React.useState(false);
- const [selectedRevision, setSelectedRevision] = useState({})
const [dragPosition, setDragPosition] = React.useState({
x: 0,
y: 0,
@@ -479,7 +496,7 @@ const AngularWorkflow = (defaultprops) => {
const [selectedTriggerIndex, setSelectedTriggerIndex] = React.useState({});
const [selectedEdge, setSelectedEdge] = React.useState({});
const [selectedEdgeIndex, setSelectedEdgeIndex] = React.useState({});
-
+ const [activeDialog, setActiveDialog] = React.useState("");
const [visited, setVisited] = React.useState([]);
const [allRevisions, setAllRevisions] = useState([])
@@ -496,6 +513,7 @@ const AngularWorkflow = (defaultprops) => {
const [selectedApp, setSelectedApp] = React.useState({});
const [selectedAction, setSelectedAction] = React.useState({});
const [selectedActionEnvironment, setSelectedActionEnvironment] = React.useState({});
+ const [selectedMeta, setSelectedMeta] = React.useState(undefined);
// Disabled streaming for now
const [streamDisabled, setStreamDisabled] = React.useState(true)
@@ -518,17 +536,22 @@ const AngularWorkflow = (defaultprops) => {
// eslint-disable-next-line no-unused-vars
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
+ const [executionFilter, setExecutionFilter] = React.useState("ALL")
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
const [workflowExecutionCount, setWorkflowExecutionCount] = React.useState(0);
const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0);
const [workflowRecommendations, setWorkflowRecommendations] = React.useState(undefined);
const [showErrors, setShowErrors] = React.useState(true);
const [highlightedApp, setHighlightedApp] = React.useState("")
-
const [listCache, setListCache] = React.useState([]);
-
const [selectedOption, setSelectedOption] = React.useState("");
const [tenzirConfigModalOpen, setTenzirConfigModalOpen] = React.useState(false);
+ const [rules, setRules] = React.useState([]);
+ const [sigmaFilesNames, setSigmaFileNames] = React.useState("")
+
+ const [distributedFromParent, setDistributedFromParent] = React.useState("")
+ const [suborgWorkflows, setSuborgWorkflows] = React.useState([])
+ const [allTriggers, setAllTriggers] = React.useState(undefined)
const [suggestionBox, setSuggestionBox] = React.useState({
"position": {
@@ -539,11 +562,17 @@ const AngularWorkflow = (defaultprops) => {
"attachedTo": "",
})
+ useEffect(() => {
+ if (!firstrequest && isLoaded && isLoggedIn && editWorkflowModalOpen === false) {
+ saveWorkflow(workflow)
+ }
+ }, [editWorkflowModalOpen])
+
// New for generated stuff
- const releaseToConnectLabel = "Release to Connect"
+const releaseToConnectLabel = "Release to Connect"
const integrationApps = [{
"id": "integration",
- "name": "Integration Framework",
+ "name": "Singul",
"type": "ACTION",
"app_version": "1.0.0",
"loop_versions": ["1.0.0"],
@@ -650,12 +679,19 @@ const AngularWorkflow = (defaultprops) => {
"field_number": -1,
"actionlist": [],
"field_id": "",
+
+ "example": "",
})
const [loadedApps, setLoadedApps] = React.useState([])
const loadAppConfig = (appId, select) => {
if (appId === undefined || appId === null || appId.length === 0) {
+ console.log("No appId to load")
+ return
+ }
+
+ if (appId === "integration") {
return
}
@@ -677,13 +713,10 @@ const AngularWorkflow = (defaultprops) => {
return response.json()
})
.then((responseJson) => {
- console.log("Loaded app config: ", responseJson)
if (responseJson.success === true && responseJson.app !== undefined && responseJson.app !== null && responseJson.app.length > 0) {
// Base64 decode into json
const foundapp = JSON.parse(atob(responseJson.app))
- console.log("Checked app: ", foundapp)
-
const selectedAppActions = selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions
if (foundapp.actions !== undefined && foundapp.actions !== null && foundapp.actions.length > selectedAppActions.length) {
@@ -710,14 +743,78 @@ const AngularWorkflow = (defaultprops) => {
}
}
+ if (cy !== undefined && cy !== null) {
+
+ // Check if any apps in the workflow has
+ cy.nodes().forEach((node) => {
+ const data = node.data()
+ if (data.app_id === foundapp.id) {
+
+ if (data.name === "tmp" && data.parameters !== undefined && data.parameters !== null && data.parameters.length === 1 && data.parameters[0].name === "tmp" && foundapp.actions !== undefined && foundapp.actions !== null && foundapp.actions.length > 0) {
+ const startIndex = foundapp.actions.findIndex((action) => action.category_label !== undefined && action.category_label !== null && action.category_label.length > 0)
+ const actionIndex = startIndex < 0 ? 0 : startIndex
+
+ node.data("name", foundapp.actions[actionIndex].name)
+ node.data("large_image", foundapp.large_image)
+ node.data("parameters", foundapp.actions[actionIndex].parameters)
+ node.data("finished", true)
+ node.data("category", foundapp.categories !== null && foundapp.categories !== undefined && foundapp.categories.length > 0 ? foundapp.categories[0] : "")
+
+ /*
+ name: app.actions[actionIndex].name,
+ label: actionLabel,
+ app_name: app.name,
+ app_version: app.app_version,
+ app_id: app.id,
+ sharing: app.sharing,
+ private_id: app.private_id,
+ description: description,
+ environment: parsedEnvironments,
+ errors: [],
+ finished: false,
+ id_: newNodeId,
+ _id_: newNodeId,
+ id: newNodeId,
+ is_valid: true,
+ type: actionType,
+ parameters: parameters,
+ isStartNode: false,
+ large_image: app.large_image,
+ run_magic_output: false,
+ authentication: [],
+ execution_variable: undefined,
+ example: example,
+ required_body_fields: app.actions[actionIndex].required_body_fields,
+ authentication_id: authId,
+ finished: false,
+ template: app.template === true ? true : false,
+ */
+
+ toast("REPLACING ACTIONS")
+ }
+ }
+ })
+ //if (action.app_id === same && app.actions.length === 1 && app.actions[0].parameters.length === 1 && app.actions[0].parameters[0].name === "tmp") {
+ }
+
+
// FIXME: Add it to the existing list AND update the selected app
}
+
})
.catch((error) => {
console.log(`Failed side-loading app ${appId}: ${error}`)
})
}
+ useEffect(() => {
+ if (workflow.actions?.length == 1) {
+ if (workflow.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") {
+ setWorkflowAsCode(true);
+ }
+ }
+ }, [workflow]);
+
// Event for making sure app is correct
useEffect(() => {
if (selectedApp === undefined || selectedApp === null && selectedApp.app_name === undefined) {
@@ -732,6 +829,7 @@ const AngularWorkflow = (defaultprops) => {
if (selectedApp.actions === undefined || selectedApp.actions === null || selectedApp.actions.length > 1) {
return
} else {
+
if (selectedApp.id !== undefined && selectedApp.id !== null && selectedApp.id.length > 0) {
loadAppConfig(selectedApp.id, true)
}
@@ -828,6 +926,75 @@ const AngularWorkflow = (defaultprops) => {
loopRunning2 = true
}
+ useEffect(() => {
+ if (workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 && workflow.id === originalWorkflow.id && workflow.parentorg_workflow === "") {
+ setOriginalWorkflow(workflow)
+ }
+
+ // Special multi-workflow edgecase handler for events
+ if (distributedFromParent === "" && suborgWorkflows === []) {
+ } else {
+ if (cy !== undefined) {
+ cy.removeListener("select");
+ cy.removeListener("unselect");
+ cy.removeListener("add");
+ cy.removeListener("remove");
+ cy.removeListener("mouseover");
+ cy.removeListener("mouseout");
+ cy.removeListener("drag");
+ cy.removeListener("free");
+ cy.removeListener("cxttap");
+
+ setTimeout(() => {
+ setupGraph(workflow)
+
+ cy.on("select", "node", (e) => {
+ onNodeSelect(e, appAuthentication);
+ });
+ cy.on("select", "edge", (e) => onEdgeSelect(e));
+
+ cy.on("unselect", (e) => onUnselect(e));
+
+ cy.on("add", "node", (e) => onNodeAdded(e));
+ cy.on("add", "edge", (e) => onEdgeAdded(e));
+ cy.on("remove", "node", (e) => onNodeRemoved(e));
+ cy.on("remove", "edge", (e) => onEdgeRemoved(e));
+
+ cy.on("mouseover", "edge", (e) => onEdgeHover(e));
+ cy.on("mouseout", "edge", (e) => onEdgeHoverOut(e));
+ cy.on("mouseover", "node", (e) => onNodeHover(e));
+ cy.on("mouseout", "node", (e) => onNodeHoverOut(e));
+
+ // Handles dragging
+ cy.on("drag", "node", (e) => onNodeDrag(e, selectedAction));
+ cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction));
+
+ cy.on("cxttap", "node", (e) => onCtxTap(e));
+
+ cy.edgehandles({
+ handleNodes: (el) => {
+ if (el.isNode() &&
+ el.data("buttonType") != "ACTIONSUGGESTION" &&
+ !el.data("isButton") &&
+ !el.data("isDescriptor") &&
+ !el.data("isSuggestion") &&
+ el.data("type") !== "COMMENT") {
+ return true
+ }
+
+ return false
+ },
+ preview: true,
+ toggleOffOnLeave: true,
+ loopAllowed: function (node) {
+ return false;
+ },
+ })
+ }, 50)
+ }
+ }
+ }, [workflow])
+
useEffect(() => {
// Current variable + future state controlled
// This is so that the loop can stop itself as well
@@ -871,9 +1038,12 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
if (responseJson.success === true) {
+ if (responseJson.meta !== undefined && responseJson.meta !== null && Object.getOwnPropertyNames(responseJson.meta).length > 0) {
+ setSelectedMeta(responseJson.meta)
+ }
+
if (responseJson.reason !== undefined && responseJson.reason !== undefined && responseJson.reason.length > 0) {
if (!responseJson.reason.includes("404: Not Found") && responseJson.reason.length > 25) {
- // Translate
into markdown ![]()
const imgRegex = /
{
useEffect(() => {
if (authenticationModalOpen === true && selectedAction.app_name !== undefined) {
- console.log(`Should get app docs for: ${selectedAction.app_name}`)
//console.log(selectedAction)
//console.log("APP: ", selectedApp)
@@ -906,13 +1075,19 @@ const AngularWorkflow = (defaultprops) => {
}, [authenticationModalOpen])
const listOrgCache = (orgId) => {
+ var headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ headers["Org-Id"] = orgId
+ }
+
fetch(`${globalUrl}/api/v1/orgs/${orgId}/list_cache`, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
+ method: "GET",
+ headers: headers,
+ credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
@@ -931,6 +1106,7 @@ const AngularWorkflow = (defaultprops) => {
};
const getWorkflowExecutionCount = (workflowId) => {
+
fetch(`${globalUrl}/api/v1/workflows/${workflowId}/executions/count`, {
method: "GET",
headers: {
@@ -958,7 +1134,7 @@ const AngularWorkflow = (defaultprops) => {
};
const getAvailableWorkflows = (trigger_index) => {
- fetch(globalUrl + "/api/v1/workflows", {
+ fetch(globalUrl + "/api/v1/workflows?subflow=true", {
method: "GET",
headers: {
"Content-Type": "application/json",
@@ -987,22 +1163,20 @@ const AngularWorkflow = (defaultprops) => {
// User Input & Subflow nodes
if (param.name === "workflow" || param.name === "subflow") {
- const paramIndex = param.name === "workflow" ? 0 : 5
- console.log("Current vs new: ", workflow.triggers[trigger_index].parameters[paramIndex].value, subworkflow.id)
-
- if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) {
- if (param.value === workflow.id) {
- setSubworkflow(workflow);
- baseSubflow = workflow
- } else {
- const sub = responseJson.find((data) => data.id === param.value);
- if (sub !== undefined && subworkflow.id !== sub.id) {
- baseSubflow = sub
- setSubworkflow(sub);
- }
- }
- }
- }
+ const paramIndex = param.name === "workflow" ? 0 : 5
+ if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) {
+ if (param.value === workflow.id) {
+ setSubworkflow(workflow);
+ baseSubflow = workflow
+ } else {
+ const sub = responseJson.find((data) => data.id === param.value);
+ if (sub !== undefined && subworkflow.id !== sub.id) {
+ baseSubflow = sub
+ setSubworkflow(sub);
+ }
+ }
+ }
+ }
if (param.name === "startnode" && param.value !== undefined && param.value !== null) {
@@ -1142,19 +1316,31 @@ const AngularWorkflow = (defaultprops) => {
};
const setNewAppAuth = (appAuthData, refresh) => {
+ var headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
+ headers["Org-Id"] = workflow.org_id
+ }
+
fetch(globalUrl + "/api/v1/apps/authentication", {
method: "PUT",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
+ headers: headers,
body: JSON.stringify(appAuthData),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
- console.log("Status not 200 for setting app auth :O!");
- }
+ console.log("Status not 200 for setting app auth :O!");
+
+ if (response.status === 400) {
+ toast.error("Failed setting new auth. Please try again", {
+ "autoClose": true,
+ })
+ }
+ }
return response.json();
})
@@ -1165,8 +1351,6 @@ const AngularWorkflow = (defaultprops) => {
"autoClose": false,
})
-
-
} else {
if (refresh === true) {
getAppAuthentication(true, true, true)
@@ -1192,15 +1376,41 @@ const AngularWorkflow = (defaultprops) => {
});
};
- const getWorkflowExecution = (id, execution_id) => {
- fetch(`${globalUrl}/api/v2/workflows/${id}/executions`, {
- method: "GET",
+ const getWorkflowExecution = (id, execution_id, filter) => {
+ var url = `${globalUrl}/api/v2/workflows/${id}/executions`
+ var method = "GET"
+ if (filter === undefined || filter === null || filter.toUpperCase() === "ALL") {
+
+ // Check for
+ if (executionFilter !== undefined && executionFilter !== null && executionFilter.length > 0) {
+ filter = executionFilter
+ } else {
+ filter = "ALL"
+ }
+ }
+
+ var formattedBody = {
+ method: method,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
- })
+ }
+
+ if (filter !== "ALL") {
+ formattedBody.method = "POST"
+
+ formattedBody.body = JSON.stringify({
+ "status": filter,
+ "workflow_id": id,
+ })
+
+ url = `${globalUrl}/api/v1/workflows/search`
+
+ }
+
+ fetch(url, formattedBody)
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
@@ -1209,6 +1419,10 @@ const AngularWorkflow = (defaultprops) => {
return response.json();
})
.then((responseJson) => {
+ if (responseJson !== undefined && responseJson !== null && responseJson.runs !== undefined && responseJson.runs !== null) {
+ responseJson.executions = responseJson.runs
+ }
+
if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null) {
// - means it's opposite
@@ -1273,8 +1487,6 @@ const AngularWorkflow = (defaultprops) => {
} else {
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
var tmpView = new URLSearchParams(cursearch).get("execution_id");
- console.log("Alertnative execution id check: ", tmpView)
-
if (tmpView === undefined || tmpView === null || tmpView.length === 0) {
const execution_id = tmpView;
setExecutionModalView(1);
@@ -1307,7 +1519,7 @@ const AngularWorkflow = (defaultprops) => {
if (response.status !== 200) {
stop();
setExecutionModalView(0);
- toast("Failed loading the workflow run")
+ //toast("Failed loading the workflow run")
console.log("Status not 200 for stream results :O!");
//const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
@@ -1352,7 +1564,7 @@ const AngularWorkflow = (defaultprops) => {
});
};
- const handleKafkaSubmit = (trigger) => {
+ const handleCommandSubmit = (trigger) => {
if (trigger.trigger_type !== "PIPELINE") {
toast("Unable to save the configuration");
return;
@@ -1360,38 +1572,18 @@ const AngularWorkflow = (defaultprops) => {
trigger.parameters = []
- const topic = document.getElementById('topic')?.value
- const bootstrapServers = document.getElementById('bootstrap_servers')?.value
- const groupId = document.getElementById('group_id')?.value
- //const autoOffsetReset = document.getElementById('auto_offset_reset')?.value;
+ const command = document.getElementById('sigma')?.value
- if(topic) {
+ if(command) {
trigger.parameters.push({
- name: "topic",
- value: topic
+ name: "command",
+ value: command
})
} else {
- toast("Please enter the topic name");
+ toast("Please enter the comamnd");
return;
}
- if (bootstrapServers) {
- trigger.parameters.push({
- name: "bootstrap_servers",
- value: bootstrapServers
- });
- } else {
- toast("please enter bootstrap server details");
- return;
- }
-
- if (groupId) {
- trigger.parameters.push({
- name: "group_id",
- value: groupId
- });
- }
-
// if (autoOffsetReset) {
// trigger.parameters.push({
// name: "auto_offset_reset",
@@ -1401,6 +1593,36 @@ const AngularWorkflow = (defaultprops) => {
setTenzirConfigModalOpen(false);
};
+
+ const handleSubmit = (trigger) => {
+ if (trigger.trigger_type !== "PIPELINE") {
+ toast("Unable to save the configuration");
+ return;
+ }
+ if (selectedOption === "Kafka Queue") {
+ trigger.parameters = []
+
+ const pipeline = document.getElementById('pipeline')?.value
+
+ setTenzirConfigModalOpen(false);
+ } else if (selectedOption === "Syslog listener") {
+ trigger.parameters = []
+
+ const endpoint = document.getElementById('endpoint')?.value
+
+ if(endpoint) {
+ trigger.parameters.push({
+ name: "endpoint",
+ value: endpoint
+ })
+ } else {
+ toast("Please enter your endpoint");
+ return;
+ }
+ }
+
+ };
+
const handleColoring = (actionId, status, label) => {
if (cy === undefined) {
@@ -1558,7 +1780,6 @@ const AngularWorkflow = (defaultprops) => {
setExecutionData(responseJson)
} else {
if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING" || responseJson.status === "FINISHED") {
- console.log("DONE!")
stop()
}
@@ -1675,12 +1896,18 @@ const AngularWorkflow = (defaultprops) => {
})
}
- const saveWorkflow = (curworkflow, executionArgument, startNode) => {
+ const saveWorkflow = (curworkflow, executionArgument, startNode, duplicationOrg) => {
var success = false;
if (isCloud && !isLoggedIn) {
console.log("Should redirect to register with redirect.")
- window.location.href = `/register?view=/workflows/${props.match.params.key}&message=You need sign up to use workflows with Shuffle`
+
+ setTimeout(() => {
+ toast("You may not have access to this workflow.")
+ //window.location.href = `/register?view=/workflows/${props.match.params.key}&message=You need sign up to use workflows with Shuffle`
+ window.location.href = `/workflows`
+ }, 2500)
+
return
}
@@ -1721,28 +1948,47 @@ const AngularWorkflow = (defaultprops) => {
continue;
}
- var type = cyelements[cyelementsKey].data()["type"];
+ var type = cyelements[cyelementsKey].data()["type"]
if (type === undefined) {
- if (
- cyelements[cyelementsKey].data().source === undefined ||
- cyelements[cyelementsKey].data().target === undefined
- ) {
- continue;
+ if (cyelements[cyelementsKey].data().source === undefined || cyelements[cyelementsKey].data().target === undefined) {
+ continue
}
+ // Get the parent item
+ var source_attachment = ""
+ const branchSource = cy.getElementById(cyelements[cyelementsKey].data().source)
+ if (branchSource === undefined || branchSource === null) {
+ } else {
+ const branchSourceData = branchSource.data()
+ if (branchSourceData !== undefined && branchSourceData !== null && branchSourceData.attachedTo !== undefined) {
+ source_attachment = branchSourceData.attachedTo
+
+ // Check if it's the 'else' or not based on uuidv5
+ const else_attachment = uuidv5(source_attachment, uuidv5.URL)
+ if (else_attachment === branchSourceData.id) {
+ source_attachment = source_attachment+"-else"
+ }
+
+ console.log("Source parent: ", source_attachment)
+ }
+ }
+
var parsedElement = {
id: cyelements[cyelementsKey].data().id,
source_id: cyelements[cyelementsKey].data().source,
destination_id: cyelements[cyelementsKey].data().target,
conditions: cyelements[cyelementsKey].data().conditions,
decorator: cyelements[cyelementsKey].data().decorator,
- };
+
+ source_parent: source_attachment,
+ }
if (parsedElement.decorator) {
- newVBranches.push(parsedElement);
+ newVBranches.push(parsedElement)
} else {
- newBranches.push(parsedElement);
+ newBranches.push(parsedElement)
}
+
} else {
if (type === "ACTION") {
const cyelement = cyelements[cyelementsKey].data();
@@ -1910,32 +2156,56 @@ const AngularWorkflow = (defaultprops) => {
useworkflow.id = props.match.params.key
}
+ var headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ if (useworkflow.org_id !== undefined && useworkflow.org_id !== null && useworkflow.org_id.length > 0) {
+ headers["Org-Id"] = useworkflow.org_id
+ }
+
+ // Realtime makes the workflow if it doesn't exist
+ /*
+ if (duplicationOrg !== undefined && duplicationOrg !== null && duplicationOrg.length > 0) {
+ headers["Org-Id"] = duplicationOrg
+ }
+ */
+
setLastSaved(true);
fetch(`${globalUrl}/api/v1/workflows/${useworkflow.id}`, {
method: "PUT",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
+ headers: headers,
body: JSON.stringify(useworkflow),
credentials: "include",
})
.then((response) => {
- setSavingState(0);
if (response.status !== 200) {
console.log("Status not 200 for setting workflows :O!");
- }
+ } else {
+ if (distributedFromParent === "" && suborgWorkflows === []) {
+ } else {
+ getChildWorkflows(useworkflow.id)
+ }
+ }
return response.json();
})
.then((responseJson) => {
+ if (useworkflow.id === originalWorkflow.id && duplicationOrg !== undefined && duplicationOrg !== null && duplicationOrg.length > 0) {
+ //duplicateParentWorkflow(useworkflow, duplicationOrg, true)
+ duplicateParentWorkflow(useworkflow, duplicationOrg, true)
+ }
+
if (executionArgument !== undefined && startNode !== undefined) {
//console.log("Running execution AFTER saving");
+ setSavingState(0);
executeWorkflow(executionArgument, startNode, true);
return;
}
if (!responseJson.success) {
+ setSavingState(0);
console.log(responseJson);
if (responseJson.reason !== undefined && responseJson.reason !== null) {
toast("Failed to save: " + responseJson.reason);
@@ -1943,6 +2213,7 @@ const AngularWorkflow = (defaultprops) => {
toast("Failed to save. Please contact your support@shuffler.io or your local admin if this is unexpected.")
}
} else {
+ setSavingState(1);
sendStreamRequest({
"item": "workflow",
@@ -1976,25 +2247,88 @@ const AngularWorkflow = (defaultprops) => {
workflow.actions[actionkey].is_valid = true;
workflow.actions[actionkey].errors = [];
}
+ } else {
+ responseJson.errors.map((error) => {
+ // Find the word itself
+ const wordsplit = error.split(" ")
+ if (wordsplit.length < 2) {
+ return
+ }
+
+ var word = ""
+ var isaction = false
+
+ for (var mapkey in wordsplit) {
+ const inword = wordsplit[mapkey]
+ if (isaction === true) {
+ word = inword
+ break
+ }
+
+ if (inword.toLowerCase() === "action") {
+ isaction = true
+ }
+ }
+
+ if (word === "") {
+ return
+ }
+
+ const foundnode = cy.nodes().find((node) => {
+ const nodelabel = node.data("label")
+ if (nodelabel === undefined || nodelabel === null) {
+ return false
+ }
+
+ return nodelabel.toLowerCase() === word.toLowerCase()
+ })
+
+ if (foundnode !== undefined && foundnode !== null) {
+ foundnode.data("is_valid", false)
+
+ // FIXME: Maybe append?
+ foundnode.data("errors", [error])
+
+ const parsedStyle = {
+ "border-width": "10px",
+ "border-opacity": ".9",
+ "border-color": red,
+ }
+
+ const animationDuration = 150
+ foundnode.animate(
+ {
+ style: parsedStyle,
+ },
+ {
+ duration: animationDuration,
+ }
+ )
+ }
+ })
}
- setWorkflow(workflow);
+ setWorkflow(workflow)
}
- setSavingState(1);
setTimeout(() => {
setSavingState(0);
}, 1500);
+ getRevisionHistory(useworkflow.id)
}
})
.catch((error) => {
setSavingState(0);
- //toast(error.toString());
+ setExecutionRequestStarted(false)
console.log("Save workflow error: ", error.toString());
+ toast.warn("Failed to save the workflow. Is the network down?")
});
- setOriginalWorkflow(useworkflow)
- return success;
+ if (originalWorkflow.id === undefined || originalWorkflow.id === null || originalWorkflow.id.length === 0 || useworkflow.id === originalWorkflow.id && workflow.parentorg_workflow === "") {
+ setOriginalWorkflow(useworkflow)
+ }
+
+ return success
};
const monitorUpdates = () => {
@@ -2026,23 +2360,25 @@ const AngularWorkflow = (defaultprops) => {
const executeWorkflow = (executionArgument, startNode, hasSaved) => {
+ if (hasSaved === false) {
+ setExecutionRequestStarted(true)
+ saveWorkflow(workflow, executionArgument, startNode);
+ //console.log("FIXME: Might have forgotten to save before executing.");
+ return;
+ }
+
+ if (workflow.public) {
+ toast("Save it to get a new version");
+ }
+
+ var returncheck = monitorUpdates();
+ if (!returncheck) {
+ toast("No startnode set.");
+ return;
+ }
+
ReactDOM.unstable_batchedUpdates(() => {
- if (hasSaved === false) {
- setExecutionRequestStarted(true);
- saveWorkflow(workflow, executionArgument, startNode);
- //console.log("FIXME: Might have forgotten to save before executing.");
- return;
- }
- if (workflow.public) {
- toast("Save it to get a new version");
- }
-
- var returncheck = monitorUpdates();
- if (!returncheck) {
- toast("No startnode set.");
- return;
- }
setVisited([])
setExecutionRequest({})
@@ -2050,51 +2386,68 @@ const AngularWorkflow = (defaultprops) => {
// FIXME: Check if any node contains $exec in a param
// If they do, show a popup asking if they want to execute it without an execution argument, or to use a previous one
- if (executionArgument === undefined || executionArgument === null || executionArgument.length === 0 && workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
- var foundmissing = false
- for (let actionkey in workflow.actions) {
- if (workflow.actions[actionkey].parameters === undefined || workflow.actions[actionkey].parameters === null || workflow.actions[actionkey].parameters.length === 0) {
- continue
- }
+ if (executionArgument === undefined || executionArgument === null || executionArgument.length === 0)
- for (let paramkey in workflow.actions[actionkey].parameters) {
- const param = workflow.actions[actionkey].parameters[paramkey]
- if (param.value === undefined || param.value === null || param.value.length === 0) {
+ if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
+ var foundmissing = false
+ for (let actionkey in workflow.actions) {
+ if (workflow.actions[actionkey].parameters === undefined || workflow.actions[actionkey].parameters === null || workflow.actions[actionkey].parameters.length === 0) {
continue
}
- if (param.value.indexOf("$exec") !== -1) {
- foundmissing = true
+ for (let paramkey in workflow.actions[actionkey].parameters) {
+ const param = workflow.actions[actionkey].parameters[paramkey]
+ if (param.value === undefined || param.value === null || param.value.length === 0) {
+ continue
+ }
+
+ if (param.value.indexOf("$exec") !== -1) {
+ foundmissing = true
+ break
+ }
+ }
+
+ if (foundmissing) {
break
}
}
if (foundmissing) {
- break
+ //toast("This workflow contains a node that requires an execution argument. Please provide one.")
+ if (workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0) {
+ setExecutionRequestStarted(false)
+ setExecutionArgumentModalOpen(true)
+ return
+ }
+
+ if (workflowExecutions.length > 0) {
+ setExecutionRequestStarted(false)
+ setExecutionArgumentModalOpen(true)
+
+ return
+ }
}
}
- if (foundmissing) {
- //toast("This workflow contains a node that requires an execution argument. Please provide one.")
- setExecutionRequestStarted(false)
- setExecutionArgumentModalOpen(true)
- return
- }
- }
-
var curelements = cy.elements();
for (let i = 0; i < curelements.length; i++) {
curelements[i].addClass("not-executing-highlight");
}
+ var headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
+ headers["Org-Id"] = workflow.org_id
+ }
+
const data = { execution_argument: executionArgument, start: startNode };
fetch(`${globalUrl}/api/v1/workflows/${props.match.params.key}/execute`,
{
method: "POST",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
+ headers: headers,
credentials: "include",
body: JSON.stringify(data),
}
@@ -2160,6 +2513,7 @@ const AngularWorkflow = (defaultprops) => {
//toast(error.toString());
setExecutionRequestStarted(false)
console.log("Execute workflow err: ", error.toString());
+ toast.warn("Failed to run the workflow. Is the network down?")
});
})
};
@@ -2167,137 +2521,182 @@ const AngularWorkflow = (defaultprops) => {
// This can be used to only show prioritzed ones later
// Right now, it can prioritize authenticated ones
//"Testing",
+ //
+ //
- const getAppAuthentication = (reset, updateAction, closeMenu) => {
- fetch(globalUrl + "/api/v1/apps/authentication", {
+ const getAuthGroups = (orgId) => {
+ setAuthGroups([])
+ var headers = {
+ "content-type": "application/json",
+ "accept": "application/json",
+ }
+
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ headers["Org-Id"] = orgId
+ }
+
+ fetch(globalUrl + "/api/v1/authentication/groups", {
method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
+ headers: headers,
credentials: "include",
})
- .then((response) => {
- if (response.status !== 200) {
- console.log("Status not 200 for app auth :O!");
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for app auth :O!");
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson.success === true) {
+ setAuthGroups(responseJson.data)
+ } else {
+ console.log("AppAuth group loading error: " + responseJson.reason);
+ }
+ })
+ .catch((error) => {
+ setAuthGroups([]);
+ console.log("AppAuth group loading error: " + error.toString());
+ })
+ }
+
+ const getAppAuthentication = (reset, updateAction, closeMenu, orgId) => {
+ var headers = {
+ "content-type": "application/json",
+ "accept": "application/json",
+ }
+
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ headers["Org-Id"] = orgId
+ }
+
+ fetch(globalUrl + "/api/v1/apps/authentication", {
+ method: "GET",
+ headers: headers,
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for app auth :O!");
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ var shouldClose = false
+ if (responseJson.success) {
+ getAuthGroups(orgId)
+
+ var newauth = [];
+ for (let authkey in responseJson.data) {
+ if (responseJson.data[authkey].defined === false) {
+ continue;
+ }
+
+ newauth.push(responseJson.data[authkey]);
+ }
+
+ setAppAuthentication(newauth);
+
+ if (cy !== undefined) {
+ // Remove the old listener for select, run with new one
+ cy.removeListener("select");
+
+ cy.on("select", "node", (e) => onNodeSelect(e, newauth));
+ cy.on("select", "edge", (e) => onEdgeSelect(e));
+ }
+
+ if (updateAction === true) {
+ if (selectedApp.authentication.required) {
+ // Setup auth here :)
+ var appUpdates = false;
+ const authenticationOptions = [];
+
+ var tmpAuth = JSON.parse(JSON.stringify(newauth));
+ var latest = 0;
+ for (let authkey in tmpAuth) {
+ var item = tmpAuth[authkey];
+
+ //console.log("Got auth: ", item);
+
+ const newfields = {};
+ for (let filterkey in item.fields) {
+ newfields[item.fields[filterkey].key] = item.fields[filterkey].value;
}
- return response.json();
- })
- .then((responseJson) => {
- var shouldClose = false
- if (responseJson.success) {
- var newauth = [];
- for (let authkey in responseJson.data) {
- if (responseJson.data[authkey].defined === false) {
- continue;
- }
+ item.fields = newfields;
- newauth.push(responseJson.data[authkey]);
- }
+ const appname = selectedApp.name.toLowerCase().replaceAll(" ", "_", -1)
+ const itemname = item.app.name.toLowerCase().replaceAll(" ", "_", -1)
+ if (itemname === appname) {
+ authenticationOptions.push(item);
- setAppAuthentication(newauth);
+ // Always becoming the last one
+ if (item.edited > latest) {
+ latest = item.edited;
+ selectedAction.selectedAuthentication = item;
- if (cy !== undefined) {
- // Remove the old listener for select, run with new one
- cy.removeListener("select");
-
- cy.on("select", "node", (e) => onNodeSelect(e, newauth));
- cy.on("select", "edge", (e) => onEdgeSelect(e));
- }
-
- if (updateAction === true) {
- if (selectedApp.authentication.required) {
- // Setup auth here :)
- var appUpdates = false;
- const authenticationOptions = [];
-
- var tmpAuth = JSON.parse(JSON.stringify(newauth));
- var latest = 0;
- for (let authkey in tmpAuth) {
- var item = tmpAuth[authkey];
-
- //console.log("Got auth: ", item);
-
- const newfields = {};
- for (let filterkey in item.fields) {
- newfields[item.fields[filterkey].key] = item.fields[filterkey].value;
- }
-
- item.fields = newfields;
-
- const appname = selectedApp.name.toLowerCase().replaceAll(" ", "_", -1)
- const itemname = item.app.name.toLowerCase().replaceAll(" ", "_", -1)
- if (itemname === appname) {
- authenticationOptions.push(item);
-
- // Always becoming the last one
- if (item.edited > latest) {
- latest = item.edited;
- selectedAction.selectedAuthentication = item;
-
- for (let actionkey in workflow.actions) {
- const actionAppname = workflow.actions[actionkey].app_name.toLowerCase().replaceAll(" ", "_", -1)
- if (actionAppname === appname) {
- workflow.actions[actionkey].selectedAuthentication = item;
- workflow.actions[actionkey].authentication_id = item.id;
- appUpdates = true;
- }
- }
- } else {
- //console.log("Not newer: ", item.edited, " vs ", latest)
- }
- } else {
- //console.log("Appname is wrong: ", appname, " vs ", itemname)
- }
- }
-
- selectedAction.authentication = authenticationOptions;
- if (
- selectedAction.selectedAuthentication === null ||
- selectedAction.selectedAuthentication === undefined ||
- selectedAction.selectedAuthentication.length === ""
- ) {
- selectedAction.selectedAuthentication = {};
- }
-
- if (appUpdates === true) {
- console.log("Closing auth modal: Success")
-
- setAuthenticationModalOpen(false);
- setSelectedAction(selectedAction);
- setWorkflow(workflow);
- saveWorkflow(workflow);
-
- toast("Added and updated authentication!");
- shouldClose = true
- } else {
- console.log("Closing auth modal? FAIL")
-
- toast("Failed to find new authentication. See details in Oauth2 popup window where auth was attempted.");
- shouldClose = false
- }
- } else {
- toast("No authentication to update");
- }
- } else {
- shouldClose = true
- }
- } else {
- setAppAuthentication([]);
- shouldClose = true
- }
-
- // Auto-closing if changes were made
- if (closeMenu === true && shouldClose === true) {
- setAuthenticationModalOpen(false);
+ for (let actionkey in workflow.actions) {
+ const actionAppname = workflow.actions[actionkey].app_name.toLowerCase().replaceAll(" ", "_", -1)
+ if (actionAppname === appname) {
+ workflow.actions[actionkey].selectedAuthentication = item;
+ workflow.actions[actionkey].authentication_id = item.id;
+ appUpdates = true;
+ }
}
- })
- .catch((error) => {
- setAppAuthentication([]);
- //toast("Auth loading error: " + error.toString());
- console.log("AppAuth error: " + error.toString());
- });
+ } else {
+ //console.log("Not newer: ", item.edited, " vs ", latest)
+ }
+ } else {
+ //console.log("Appname is wrong: ", appname, " vs ", itemname)
+ }
+ }
+
+ console.log("auth options: ", authenticationOptions)
+
+ selectedAction.authentication = authenticationOptions
+ if (selectedAction.selectedAuthentication === null || selectedAction.selectedAuthentication === undefined || selectedAction.selectedAuthentication.length === "") {
+ selectedAction.selectedAuthentication = {}
+ }
+
+ if (appUpdates === true) {
+ console.log("Closing auth modal: Success")
+
+ setAuthenticationModalOpen(false);
+ setSelectedAction(selectedAction);
+ setWorkflow(workflow);
+ saveWorkflow(workflow);
+
+ toast("Added and updated authentication!");
+ shouldClose = true
+ } else {
+ console.log("Closing auth modal? FAIL")
+
+ toast("Failed to find new authentication. See details in Oauth2 popup window where auth was attempted.");
+ shouldClose = false
+ }
+ } else {
+ toast("No authentication to update");
+ }
+ } else {
+ shouldClose = true
+ }
+
+ } else {
+ setAppAuthentication([])
+ shouldClose = true
+ }
+
+ // Auto-closing if changes were made
+ if (closeMenu === true && shouldClose === true) {
+ setAuthenticationModalOpen(false);
+ }
+ })
+ .catch((error) => {
+ setAppAuthentication([]);
+ //toast("Auth loading error: " + error.toString());
+ console.log("AppAuth error: " + error.toString());
+ });
};
const getApps = () => {
@@ -2335,7 +2734,7 @@ const AngularWorkflow = (defaultprops) => {
return
}
- console.log("Apps loaded. JSON decoding next")
+ //console.log("Apps loaded. JSON decoding next")
return response.json()
})
@@ -2451,13 +2850,19 @@ const AngularWorkflow = (defaultprops) => {
})
}
- const getFiles = () => {
+ const getFiles = (orgId) => {
+ var headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ headers["Org-Id"] = orgId
+ }
+
fetch(globalUrl + "/api/v1/files", {
method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
+ headers: headers,
credentials: "include",
})
.then((response) => {
@@ -2835,6 +3240,10 @@ const AngularWorkflow = (defaultprops) => {
}
cy.add(nodeData)
+
+ if (workflowAsCode) {
+ setWorkflowAsCode(false)
+ }
// Wait 100ms then add a style for it
setTimeout(() => {
@@ -3082,6 +3491,45 @@ const AngularWorkflow = (defaultprops) => {
return apps
};
+ const getChildWorkflows = (parentWorkflowId) => {
+ //toast("Loading child workflows 1 (should be 2)")
+
+ /*
+ if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) {
+ return
+ }
+ */
+
+ const orgId = originalWorkflow.org_id === undefined || originalWorkflow.org_id === null || originalWorkflow.org_id === "" ? "" : originalWorkflow.org_id
+
+ //toast("Loading child workflows 2: " + orgId)
+
+ fetch(`${globalUrl}/api/v1/workflows/${parentWorkflowId}/child_workflows`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Org-Id": orgId,
+ },
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for workflows :O!");
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson.success !== false) {
+ setSuborgWorkflows(responseJson)
+ }
+ })
+ .catch((error) => {
+ console.log("Get child workflows error: ", error);
+ })
+ }
+
const getWorkflow = (workflow_id, sourcenode) => {
fetch(`${globalUrl}/api/v1/workflows/${workflow_id}`, {
method: "GET",
@@ -3103,19 +3551,20 @@ const AngularWorkflow = (defaultprops) => {
// don't redirect if it exists
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
var execFound = new URLSearchParams(cursearch).get("execution_id");
- var sessionToken = new URLSearchParams(cursearch).get("session_token");
+ var sessionToken = new URLSearchParams(cursearch).get("session_token");
if (execFound === null && sessionToken === null) {
- toast(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds..`)
- setTimeout(() => {
- window.location.pathname = "/workflows";
- }, 2000);
- } else if (sessionToken !== null && workflow_id === "3abdfb21-b40f-4e50-b855-ac0d62f83cbe") {
- toast(`Injecting session token and reloading workflow..`)
- setTimeout(() => {
- setCookie("session_token", sessionToken, { path: "/" });
- window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe";
- }, 2000);
- }
+ toast(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds..`)
+ setTimeout(() => {
+ window.location.pathname = "/workflows";
+ }, 2000);
+
+ } else if (sessionToken !== null && workflow_id === "3abdfb21-b40f-4e50-b855-ac0d62f83cbe") {
+ toast(`Injecting session token and reloading workflow..`)
+ setTimeout(() => {
+ setCookie("session_token", sessionToken, { path: "/" });
+ window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe";
+ }, 2000)
+ }
}
}
@@ -3125,9 +3574,20 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
// Load as JSON
- //console.log("Got workflow TXT: ", responseText)
- //const responseJson = JSON.parse(responseText)
- //console.log("Got workflow JSON: ", responseJson)
+ if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id.length > 0 && responseJson.id !== workflow_id) {
+ toast("Workflow ID mismatch. Redirecting to your workflow")
+ navigate(`/workflows/${responseJson.id}`)
+ }
+
+ if (responseJson.parentorg_workflow !== undefined && responseJson.parentorg_workflow !== null && responseJson.parentorg_workflow !== "") {
+ setDistributedFromParent(responseJson.parentorg_workflow)
+ }
+
+ if (responseJson.childorg_workflow_ids !== undefined && responseJson.childorg_workflow_ids !== null && responseJson.childorg_workflow_ids.length > 0) {
+ getChildWorkflows(responseJson.id)
+ } else if (responseJson.parentorg_workflow !== undefined && responseJson.parentorg_workflow !== null && responseJson.parentorg_workflow.length > 0) {
+ getChildWorkflows(responseJson.parentorg_workflow)
+ }
// Not sure why this is necessary.
if (responseJson.isValid === undefined) {
@@ -3149,6 +3609,13 @@ const AngularWorkflow = (defaultprops) => {
if (responseJson.org_id !== undefined && responseJson.org_id !== null) {
listOrgCache(responseJson.org_id)
}
+
+ if (responseJson.sharing !== undefined && responseJson.sharing !== null && (responseJson.sharing === "form" || responseJson.sharing === "forms")) {
+ if (responseJson.actions === undefined || responseJson.actions === null || responseJson.actions.length === 0) {
+ navigate("/forms/" + responseJson.id)
+ toast("Redirecting to Form from Workflow")
+ }
+ }
// Wait for this to finish
fetchRecommendations(responseJson)
@@ -3346,11 +3813,14 @@ const AngularWorkflow = (defaultprops) => {
});
}
- cy.fit(null, 100);
+ cy.fit(null, 400);
cy.on("add", "node", (e) => onNodeAdded(e));
cy.on("add", "edge", (e) => onEdgeAdded(e));
} else {
- setOriginalWorkflow(responseJson);
+ if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id.length > 0 && responseJson.parentorg_workflow === "") {
+ setOriginalWorkflow(responseJson)
+ }
+
setWorkflow(responseJson);
setWorkflowDone(true);
@@ -3400,6 +3870,20 @@ const AngularWorkflow = (defaultprops) => {
}
}
+ // Ensuring overwriting
+ if (nodedata?.type === "ACTION") {
+
+ if (nodedata?.parameters !== undefined && nodedata.parameters !== null && nodedata.parameters.length > 0 && workflow?.actions !== undefined && workflow?.actions !== null && workflow?.actions.length > 0) {
+ for (var actionkey in workflow.actions) {
+ const action = workflow.actions[actionkey]
+ if (action.id === nodedata.id) {
+ workflow.actions[actionkey].parameters = nodedata.parameters
+ break
+ }
+ }
+ }
+ }
+
// Unselecting all
//cy.elements().unselect()
@@ -3461,13 +3945,9 @@ const AngularWorkflow = (defaultprops) => {
setSelectedAction({});
setSelectedApp({});
setSelectedComment({})
- setSelectedEdge({});
-
-
setSelectedEdge({})
- setSelectedActionEnvironment({})
+ //setSelectedActionEnvironment({})
setTriggerAuthentication({})
- setTriggerFolders([])
setLocalFirstrequest(true)
setSelectedTrigger({});
@@ -3481,7 +3961,6 @@ const AngularWorkflow = (defaultprops) => {
left: 0,
selected: "",
});
- //console.timeEnd("UNSELECT");
})
sendStreamRequest({
@@ -3573,20 +4052,19 @@ const AngularWorkflow = (defaultprops) => {
.then(() => {
console.log("DONE: ", workflow_id);
getWorkflow(workflow_id.value, nodedata);
- cy.fit(null, 50);
+ cy.fit(null, 300);
});
}
};
const onNodeDragStop = (event, selectedAction) => {
- const nodedata = event.target.data();
+ const nodedata = event.target.data()
if (nodedata.id === selectedAction.id) {
- //console.log("Same node, return")
+ //console.log("Same node, return")
return
}
if (nodedata.finished === false) {
- //console.log("Node is not finished, return")
return
}
@@ -3686,6 +4164,7 @@ const AngularWorkflow = (defaultprops) => {
nodedata.app_name !== "Testing" &&
nodedata.app_name !== "Shuffle Workflow" &&
nodedata.app_name !== "Integration Framework" &&
+ nodedata.app_name !== "Singul" &&
nodedata.app_name !== "User Input") ||
nodedata.isStartNode)
) {
@@ -3693,25 +4172,18 @@ const AngularWorkflow = (defaultprops) => {
var found = false;
for (let nodekey in allNodes) {
const currentNode = allNodes[nodekey];
- if (
- currentNode.data.attachedTo === nodedata.id &&
- currentNode.data.isDescriptor
- ) {
- found = true;
- console.log("FOUND THE NODE!");
- break;
+ if (currentNode.data.attachedTo === nodedata.id && currentNode.data.isDescriptor) {
+ found = true
+ break
}
}
if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") {
- console.log("Found triggers. Add!")
-
if (!found) {
- console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions)
+ //console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions)
// 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: {
@@ -3783,6 +4255,7 @@ const AngularWorkflow = (defaultprops) => {
const onNodeDrag = (event, selectedAction) => {
const nodedata = event.target.data();
+
if (nodedata.finished === false) {
console.log("NOT FINISHED - ADD EXAMPLE BRANCHES TO CLOSEST!!")
return
@@ -3820,147 +4293,163 @@ const AngularWorkflow = (defaultprops) => {
}
if (nodedata.id === selectedAction.id) {
- return
+ return;
}
-
- if ((nodedata.trigger_type === "SUBFLOW" || nodedata.trigger_type === "USERINPUT" || nodedata.type === "ACTION") && !nodedata.isStartNode) {
- // Check if it already has any non-decorator branches attached to it
- const branches = cy.elements('edge').jsons()
- var branchFound = false
- var decoratorIds = []
- for (var branchkey in branches) {
- if (branches[branchkey].data.source === nodedata.id || branches[branchkey].data.target === nodedata.id) {
+ if ((nodedata.trigger_type === "SUBFLOW" || nodedata.trigger_type === "USERINPUT" || nodedata.type === "ACTION") && !nodedata.isStartNode) {
+ // Check if it already has any non-decorator branches attached to it
+ const branches = cy.elements('edge').jsons()
+ var branchFound = false
+ var decoratorIds = []
+ for (var branchkey in branches) {
+ if (branches[branchkey].data.source === nodedata.id || branches[branchkey].data.target === nodedata.id) {
+
+ if (branches[branchkey].data.decorator === true) {
+
+ // Add the source/destination
+ if (branches[branchkey].data.source === nodedata.id) {
+ decoratorIds.push(branches[branchkey].data.target)
+ } else {
+ decoratorIds.push(branches[branchkey].data.source)
+ }
+
+ continue
+ }
+
+ branchFound = true
+ break
+ }
+ }
+
+ if (!branchFound) {
+ //console.log("Found action during drag. Checking closest nodes as it doesn't have a valid branch")
+ var closestNode = null
+ var minDistance = 300
+
+ const draggedNode = event.target
+ const allnodes = cy.nodes().jsons()
+ for (var nodekey in allnodes) {
+ const node = allnodes[nodekey]
+ if (node.data.id === nodedata.id) {
+ continue
+ }
+
+ // Decorators
+ if (node.data.attachedTo !== undefined) {
+ continue
+ }
+
+ if (node.position === undefined || node.position === null || node.position.x === undefined || node.position.y === undefined) {
+ continue
+ }
+
+ if (node.data.type !== "ACTION" && node.data.type !== "TRIGGER") {
+ continue
+ }
+
+ const distance = Math.sqrt(
+ Math.pow(draggedNode.position('x') - node.position.x, 2) +
+ Math.pow(draggedNode.position('y') - node.position.y, 2)
+ )
+
+ if (decoratorIds.includes(node.data.id)) {
+ //console.log("Found existing decorator for: ", node.data.app_name, "Distance: ", distance)
+
+ if (distance > 300) {
+ // Remove the branch
+ const edgeToRemove = cy.getElementById(branches[branchkey].data.id)
+ if (edgeToRemove !== null && edgeToRemove !== undefined) {
+ //console.log("Removing edge: ", edgeToRemove)
+ edgeToRemove.remove()
+ //decoratorIds.splice(decoratorIds.indexOf(node.data.id), 1)
+ break
+ }
+ }
+ }
+
+
+ if (distance < minDistance) {
+ minDistance = distance
+ closestNode = node
+ }
+ }
+
+ if (closestNode !== null && closestNode !== undefined) {
+ //console.log("Closest node app: ", closestNode.data.app_name, "Distance: ", minDistance)
+
+ /*
+ if (decoratorIds.length > 0) {
+ console.log("Decorators already exists. If within distance of 15 add to existing, otherwise remove old and add new: ", decoratorIds)
+ for (var decoratorkey in decoratorIds) {
+ const decoratorEdge = cy.getElementById(decoratorIds[decoratorkey])
+ if (decoratorEdge === null || decoratorEdge === undefined) {
+ continue
+ }
+
+ const sourceNode = cy.getElementById(decoratorEdge.data.source)
+ const targetNode = cy.getElementById(decoratorEdge.data.target)
+
+ const distance = Math.sqrt(
+ Math.pow(draggedNode.position('x') - sourceNode.position('x'), 2) +
+ Math.pow(draggedNode.position('y') - sourceNode.position('y'), 2)
+ )
+
+ // Check plus minus 15 in distance from mindistance
+ if (distance > minDistance - 15 && distance < minDistance + 15) {
+ console.log("Within distance of 15, add to existing edge")
+ } else {
+ console.log("Outside distance of 15, remove old edge and add new")
+ }
+
+ }
+ }
+ */
+
+ if (decoratorIds.length === 0) {
+ //const edgeCurve = calculateEdgeCurve(draggedNode.position(), closestNode.position)
+ //currentedge.style('control-point-distance', edgeCurve.distance)
+ //currentedge.style('control-point-weight', edgeCurve.weight)
+
+ const newId = uuidv4()
+ cy.add({
+ group: "edges",
+ data: {
+ decorator: true,
+ id: newId,
+ _id: newId,
+ source: closestNode.data.id,
+ target: nodedata.id,
+ label: releaseToConnectLabel,
+ conditions: [],
+ }
+ })
+ }
+ }
+ }
- if (branches[branchkey].data.decorator === true) {
+ /*
+ // FIXME: This is the start of a highlighter for the node
+ // to better match it up with other elements
+ // 1. Get current node's position in X/Y on the screen
+ // 2. Draw a red line on the X and Y axis for positioning
+
+ // Draw a red div line in the HTML
+ const position = event.target.position()
+ const redline = document.getElementById("redline")
+ if (redline !== null && redline !== undefined) {
+ redline.style.display = "block"
+ redline.style.position = "absolute"
+ redline.style.left = position.x + "px"
+ redline.style.top = position.y + "px"
+ redline.style.height = "10000px"
+ redline.style.width = 1
+ console.log("REDLINE!")
+ }
+ */
- // Add the source/destination
- if (branches[branchkey].data.source === nodedata.id) {
- decoratorIds.push(branches[branchkey].data.target)
- } else {
- decoratorIds.push(branches[branchkey].data.source)
- }
-
- continue
- }
-
- branchFound = true
- break
- }
- }
-
- if (!branchFound) {
- //console.log("Found action during drag. Checking closest nodes as it doesn't have a valid branch")
- var closestNode = null
- var minDistance = 300
-
- const draggedNode = event.target
- const allnodes = cy.nodes().jsons()
- for (var nodekey in allnodes) {
- const node = allnodes[nodekey]
- if (node.data.id === nodedata.id) {
- continue
- }
-
- // Decorators
- if (node.data.attachedTo !== undefined) {
- continue
- }
-
- if (node.position === undefined || node.position === null || node.position.x === undefined || node.position.y === undefined) {
- continue
- }
-
- if (node.data.type !== "ACTION" && node.data.type !== "TRIGGER") {
- continue
- }
-
- const distance = Math.sqrt(
- Math.pow(draggedNode.position('x') - node.position.x, 2) +
- Math.pow(draggedNode.position('y') - node.position.y, 2)
- )
-
- if (decoratorIds.includes(node.data.id)) {
- //console.log("Found existing decorator for: ", node.data.app_name, "Distance: ", distance)
-
- if (distance > 300) {
- // Remove the branch
- const edgeToRemove = cy.getElementById(branches[branchkey].data.id)
- if (edgeToRemove !== null && edgeToRemove !== undefined) {
- //console.log("Removing edge: ", edgeToRemove)
- edgeToRemove.remove()
- //decoratorIds.splice(decoratorIds.indexOf(node.data.id), 1)
- break
- }
- }
- }
-
-
- if (distance < minDistance) {
- minDistance = distance
- closestNode = node
- }
- }
-
- if (closestNode !== null && closestNode !== undefined) {
- //console.log("Closest node app: ", closestNode.data.app_name, "Distance: ", minDistance)
-
- /*
- if (decoratorIds.length > 0) {
- console.log("Decorators already exists. If within distance of 15 add to existing, otherwise remove old and add new: ", decoratorIds)
- for (var decoratorkey in decoratorIds) {
- const decoratorEdge = cy.getElementById(decoratorIds[decoratorkey])
- if (decoratorEdge === null || decoratorEdge === undefined) {
- continue
- }
-
- const sourceNode = cy.getElementById(decoratorEdge.data.source)
- const targetNode = cy.getElementById(decoratorEdge.data.target)
-
- const distance = Math.sqrt(
- Math.pow(draggedNode.position('x') - sourceNode.position('x'), 2) +
- Math.pow(draggedNode.position('y') - sourceNode.position('y'), 2)
- )
-
- // Check plus minus 15 in distance from mindistance
- if (distance > minDistance - 15 && distance < minDistance + 15) {
- console.log("Within distance of 15, add to existing edge")
- } else {
- console.log("Outside distance of 15, remove old edge and add new")
- }
-
- }
- }
- */
-
- if (decoratorIds.length === 0) {
- //const edgeCurve = calculateEdgeCurve(draggedNode.position(), closestNode.position)
- //currentedge.style('control-point-distance', edgeCurve.distance)
- //currentedge.style('control-point-weight', edgeCurve.weight)
-
- const newId = uuidv4()
- cy.add({
- group: "edges",
- data: {
- decorator: true,
- id: newId,
- _id: newId,
- source: closestNode.data.id,
- target: nodedata.id,
- label: releaseToConnectLabel,
- conditions: [],
- }
- })
- }
- }
- }
- }
-
- if (
- originalLocation.x === 0 &&
- originalLocation.y === 0 &&
- nodedata.position !== undefined
- ) {
+ }
+
+ if (originalLocation.x === 0 && originalLocation.y === 0 && nodedata.position !== undefined) {
originalLocation.x = nodedata.position.x;
originalLocation.y = nodedata.position.y;
}
@@ -3973,7 +4462,7 @@ const AngularWorkflow = (defaultprops) => {
const elementMouseIsOver = document.elementFromPoint(x, y);
if (elementMouseIsOver !== undefined && elementMouseIsOver !== null) {
- // Color for #f85a3e translated to rgb
+ // Color for #FF8544 translated to rgb
const newBorder = "3px solid rgb(248, 90, 62)";
if (
elementMouseIsOver.style.border !== newBorder &&
@@ -4013,11 +4502,11 @@ const AngularWorkflow = (defaultprops) => {
}
// Ensure it only happens once
- document.removeEventListener("mousemove", onMouseUpdate, false)
- }
+ document.removeEventListener("mousemove", onMouseUpdate, false);
+ };
- document.addEventListener("mousemove", onMouseUpdate, false)
- }
+ document.addEventListener("mousemove", onMouseUpdate, false);
+ };
useBeforeunload(() => {
@@ -4030,7 +4519,334 @@ const AngularWorkflow = (defaultprops) => {
document.removeEventListener("paste", handlePaste, true);
}
}
- })
+ });
+
+ // Should get AI autocompletes
+ const aiSubmit = (value, setResponseMsg, setSuggestionLoading, inputAction) => {
+ if (setResponseMsg !== undefined) {
+ setResponseMsg("")
+ }
+
+ if (value === undefined || value === "") {
+ console.log("No value input!")
+ return
+ }
+
+ if (setSuggestionLoading !== undefined) {
+ setSuggestionLoading(true)
+ }
+
+ console.log("Submit conversation with value: ", value);
+
+ // This is to find sample response and parse it as string
+
+ var AppContext = []
+ var originalParams = []
+ var originalField = ""
+
+ if (inputAction !== undefined && inputAction !== null) {
+ // Reload the data without copying
+ inputAction = JSON.parse(JSON.stringify(inputAction))
+ originalParams = JSON.parse(JSON.stringify(inputAction.parameters))
+ if (originalParams.length > 0) {
+ originalField = originalParams[0].name
+ }
+ const parents = getParents(inputAction)
+
+ var actionlist = []
+ if (parents.length > 1) {
+ for (let [key,keyval] in Object.entries(parents)) {
+ const item = parents[key];
+ if (item.label === "Execution Argument") {
+ continue;
+ }
+
+ var exampledata = item.example === undefined || item.example === null ? "" : item.example;
+ // Find previous execution and their variables
+ //exampledata === "" &&
+ if (workflowExecutions.length > 0) {
+ // Look for the ID
+ const found = false;
+ for (let [key,keyval] in Object.entries(workflowExecutions)) {
+ if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) {
+ continue;
+ }
+
+ var foundResult = workflowExecutions[key].results.find((result) => result.action.id === item.id);
+ if (foundResult === undefined || foundResult === null) {
+ continue;
+ }
+
+ if (foundResult.result !== undefined && foundResult.result !== null) {
+ foundResult = foundResult.result
+ }
+
+ const valid = validateJson(foundResult, true)
+ if (valid.valid) {
+ if (valid.result.success === false) {
+ //console.log("Skipping success false autocomplete")
+ } else {
+ exampledata = valid.result;
+ break;
+ }
+ } else {
+ exampledata = foundResult;
+ }
+ }
+ }
+
+ // 1. Take
+ const itemlabelComplete = item.label === null || item.label === undefined ? "" : item.label.split(" ").join("_");
+
+ const actionvalue = {
+ app_name: item.app_name,
+ action_name: item.name,
+ label: item.label,
+
+ type: "action",
+ id: item.id,
+ name: item.label,
+ autocomplete: itemlabelComplete,
+ example: exampledata,
+ };
+
+ actionlist.push(actionvalue);
+ }
+ }
+
+ var fixedResults = []
+ for (var i = 0; i < actionlist.length; i++) {
+ const item = actionlist[i];
+ const responseFix = SetJsonDotnotation(item.example, "")
+
+ // Check if json
+ const validated = validateJson(responseFix)
+ var exampledata = responseFix;
+ if (validated.valid) {
+ exampledata = JSON.stringify(validated.result)
+ }
+
+ AppContext.push({
+ "app_name": item.app_name,
+ "action_name": item.action_name,
+ "label": item.label,
+ "example": exampledata,
+ //"example_response": exampledata,
+ })
+ }
+
+ var params = []
+ for (var paramkey in inputAction.parameters) {
+ const param = inputAction.parameters[paramkey]
+ if (param.configuration) {
+ continue
+ }
+
+ // Mainly for booleans
+ if (param.options !== undefined && param.options !== null && param.options.length > 0) {
+ continue
+ }
+
+ params.push(param)
+ }
+
+ inputAction.parameters = params
+ }
+
+ var conversationData = {
+ "query": value,
+ "output_format": "action",
+ "app_context": AppContext,
+
+ "workflow_id": workflow.id,
+ }
+
+
+ if (inputAction !== undefined) {
+ console.log("Add app context! This should them get parameters directly")
+ conversationData.output_format = "action_parameters"
+
+ conversationData.app_id = inputAction.app_id
+ conversationData.app_name = inputAction.app_name
+ conversationData.action_name = inputAction.name
+ conversationData.parameters = inputAction.parameters
+ }
+
+ // Onprem not available yet (April 2023)
+ // Should: Make OpenAI work for them with their own key
+ //fetch(`${globalUrl}/api/v1/conversation`, {
+ const url = `${globalUrl}/api/v1/conversation`
+ fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ body: JSON.stringify(conversationData),
+ credentials: "include",
+ })
+ .then((response) => {
+ setAutocompleting(false)
+ if (setSuggestionLoading !== undefined) {
+ setSuggestionLoading(false)
+ }
+
+ if (response.status !== 200) {
+ console.log("Status not 200 for stream results :O!");
+ } else {
+ toast("Completion finished. Please verify the output and run the workflow again!")
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ console.log("Conversation response: ", responseJson)
+ if (responseJson.success === false) {
+ if (responseJson.reason !== undefined) {
+ if (setResponseMsg !== undefined) {
+ setResponseMsg(responseJson.reason)
+ }
+
+ toast.error(responseJson.reason)
+ }
+
+ return
+ } else {
+ setAiQueryModalOpen(false)
+ }
+
+ if (inputAction !== undefined) {
+ console.log("In input action! Should check params if they match, and add suggestions")
+
+ console.log("ORIGINAL PARAMS: ", originalParams)
+ console.log("RESPONSE PARAMS: ", responseJson.parameters)
+
+ if (responseJson.parameters === undefined || responseJson.parameters.length === 0) {
+ return
+ }
+
+ var changed = false
+ var codeeditorfound = false
+ for (let respParamKey in responseJson.parameters) {
+ var respParam = responseJson.parameters[respParamKey]
+ if (respParam.value === undefined || respParam.value === null || respParam.value === "" ) {
+ continue
+ }
+
+ for (var paramkey in selectedAction?.parameters) {
+ const actionParam = selectedAction.parameters[paramkey]
+ if (actionParam.name !== respParam.name) {
+ continue
+ }
+
+ const codeeditor = document.getElementById("shuffle-codeeditor")
+ if (codeeditor !== undefined && codeeditor !== null && actionParam.name === originalField) {
+ const editorInstance = window?.ace?.edit("shuffle-codeeditor")
+ if (editorInstance === undefined || editorInstance === null) {
+ toast.error("Failed to find code editor instance")
+ return
+ } else {
+ codeeditorfound = true
+ editorInstance.setValue(respParam.value)
+ //selectedAction.parameters[paramkey].value = respParam.value
+ changed = true
+ }
+ }
+
+ if (!changed) {
+ console.log("Found match for param: ", respParam)
+ changed = true
+ selectedAction.parameters[paramkey].autocompleted = true
+ selectedAction.parameters[paramkey].value = respParam.value
+ }
+ }
+ }
+
+ if (changed === true && codeeditorfound === false) {
+ //inputAction.parameters = JSON.parse(JSON.stringify(selectedAction.parameters))
+ selectedAction.parameters = JSON.parse(JSON.stringify(selectedAction.parameters))
+ console.log("Setting action! Force update pls :)")
+ setUpdate(Math.random())
+
+ setSelectedAction(selectedAction)
+
+ // Find it in cytoscape and update the action
+ if (cy !== undefined && cy !== null) {
+ const cyAction = cy.getElementById(inputAction.id)
+ if (cyAction !== undefined && cyAction !== null) {
+ cyAction.data("parameters", selectedAction.parameters)
+ }
+ }
+
+ }
+
+ return
+ }
+
+ // Add action
+ console.log("Suggestionbox location: ", suggestionBox)
+ if (responseJson.app_name !== undefined && responseJson.app_name !== null) {
+ // Always added to 0, 0
+ // Should use suggestionBox.position.x, suggestionBox.position.y
+ var newitem = {
+ "data": responseJson,
+ "position": {
+ "x": suggestionBox.node_position.x !== undefined ? suggestionBox.node_position.x : 0,
+ "y": suggestionBox.node_position.y !== undefined ? suggestionBox.node_position.y + 100 : 0,
+ },
+ "group": "nodes",
+ }
+
+ newitem.type = "ACTION"
+ newitem.isStartNode = false
+ newitem.data.id = uuidv4()
+ newitem.data.type = "ACTION"
+ newitem.data.isStartNode = false
+
+ newitem.data.is_valid = true
+ newitem.data.isValid = true
+
+ cy.add({
+ group: newitem.group,
+ data: newitem.data,
+ position: newitem.position,
+ });
+
+ // Add edge
+ const newId = uuidv4()
+ cy.add({
+ group: "edges",
+ data: {
+ id: newId,
+ _id: newId,
+ source: suggestionBox.attachedTo,
+ target: newitem.data.id,
+ }
+ })
+ //label: "Generated",
+
+ setSuggestionBox({
+ "position": {
+ "top": 500,
+ "left": 500,
+ },
+ "open": false,
+ "attachedTo": "",
+ });
+ }
+ })
+ .catch((error) => {
+ setAiQueryModalOpen(false)
+ setAutocompleting(false)
+ if (setSuggestionLoading !== undefined) {
+ setSuggestionLoading(false)
+ }
+
+ console.log("Conv response error: ", error);
+ });
+ }
+
+
// Nodeselectbatching:
// https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once
@@ -4039,17 +4855,14 @@ const AngularWorkflow = (defaultprops) => {
// Forces all states to update at the same time,
// Otherwise everything is SUPER slow
- //const data = JSON.parse(JSON.stringify(event.target.data()))
- const data = event.target.data()
-
- console.log("NODE SELECT: ", data)
-
- if (data.app_name === "Shuffle Workflow") {
- console.log("Shuffle Workflow selected")
- if ((data.parameters !== undefined) && (data.parameters.length > 0)) {
- getWorkflowApps(data.parameters[0].value)
+ // FIXME: Do absolutely NOT use JSON.stringify on the event.target.data()
+ // This causes memory referencing to become a nightmare
+ const data = event.target.data()
+ if (data.app_name === "Shuffle Workflow") {
+ if ((data.parameters !== undefined) && (data.parameters.length > 0)) {
+ getWorkflowApps(data.parameters[0].value)
+ }
}
- }
if (data.buttonType == "ACTIONSUGGESTION") {
const attachedToId = data.attachedTo
@@ -4114,6 +4927,7 @@ const AngularWorkflow = (defaultprops) => {
workflow.actions[foundindex].name = curaction.name
setWorkflow(workflow)
+ console.log(workflow)
}
break
}
@@ -4220,7 +5034,7 @@ const AngularWorkflow = (defaultprops) => {
return
}
- // Inject HTML at a fixed location
+ // Inject HTML at a fixed location?
//const newHtml = "
Do you want to add this suggestion? Yes No "
// Find mouse cursor position on screen
@@ -4314,7 +5128,7 @@ const AngularWorkflow = (defaultprops) => {
newNodeData.position = {
x: newNodeData.position.x + 100,
y: newNodeData.position.y + 100,
- };
+ }
}
newNodeData.isStartNode = false;
@@ -4415,42 +5229,57 @@ const AngularWorkflow = (defaultprops) => {
return;
} else if (data.isDescriptor) {
- console.log("Can't select descriptor");
+ // Find parent
+ event.target.unselect();
+
+ if (data.attachedTo !== undefined && data.attachedTo !== null && data.attachedTo.length > 0) {
+ const parentNode = cy.getElementById(data.attachedTo)
+ if (parentNode !== null && parentNode !== undefined) {
+ setTimeout(() => {
+ parentNode.select()
+ }, 100)
+ }
+ }
+
+ //console.log("Can't select descriptor");
if (data.isTrigger) {
console.log("But maybe we can select trigger descriptor? Maybe open execution tab?")
setExecutionModalOpen(true)
}
- event.target.unselect();
return;
}
- if (data.type === undefined) {
- console.log("No type, automatically setting to action");
- data.type = "ACTION"
- }
+ if (data.type === undefined) {
+ console.log("No type, automatically setting to action");
+ data.type = "ACTION"
+ }
if (data.type === "ACTION") {
setSelectedComment({})
- //var curaction = JSON.parse(JSON.stringify(data))
- // FIXME: Trust it to just work?
- //event.target.data()
+
+ // FIXME: is this what is mapping it an actual action in the workflow? wtf?
var curaction = workflow.actions.find((a) => a.id === data.id)
if (!curaction || curaction === undefined) {
if (data.id !== undefined && data.app_name !== undefined) {
workflow.actions.push(data)
setWorkflow(workflow)
- curaction = data
+
+ // FIXME: Is this necessary?
+ //curaction = JSON.parse(JSON.stringify(data))
} else {
if (workflow.public !== true) {
toast("Action not found. Please remake it.");
}
- event.target.remove();
- return;
+ event.target.remove()
+ return
}
}
+ // FIXME: This change may cause... something
+ curaction = data
+
//var newapps = JSON.parse(JSON.stringify(apps))
var newapps = apps
if (apps === null || apps === undefined || apps.length === 0) {
@@ -4537,6 +5366,10 @@ const AngularWorkflow = (defaultprops) => {
}
*/
+ if (curapp !== undefined && curapp !== null && curapp.id !== undefined && curapp.id !== null && curapp.id.length > 0) {
+ loadAppConfig(curapp.id, true)
+ }
+
if (!curapp || curapp === undefined) {
const tmpapp = {
name: curaction.app_name,
@@ -4647,7 +5480,7 @@ const AngularWorkflow = (defaultprops) => {
authenticationOptions[latestindex].last_modified = true
}
- curaction.authentication = authenticationOptions;
+ curaction.authentication = authenticationOptions
if (
curaction.selectedAuthentication === null ||
curaction.selectedAuthentication === undefined ||
@@ -4656,7 +5489,8 @@ const AngularWorkflow = (defaultprops) => {
curaction.selectedAuthentication = {};
}
} else {
- curaction.authentication = [];
+
+ curaction.authentication = []
curaction.authentication_id = "";
curaction.selectedAuthentication = {};
}
@@ -4676,39 +5510,65 @@ const AngularWorkflow = (defaultprops) => {
curaction.parameters[curActionParamKey].value = curaction.parameters[curActionParamKey].options[0];
}
}
+
} else {
- console.log("Should check APP if it has the same params as ACTION")
- for (let actionKey in curapp.actions) {
- const tmpaction = curapp.actions[actionKey]
- if (tmpaction.name === curaction.name) {
- console.log("Found action - needs change?", tmpaction)
- if (tmpaction.parameters !== undefined && tmpaction.parameters !== null && tmpaction.parameters.length > 0) {
- curaction.parameters = JSON.parse(JSON.stringify(tmpaction.parameters))
- }
- break
- }
+ console.log("Should check APP if it has the same params as ACTION")
+ for (let actionKey in curapp.actions) {
+ const tmpaction = curapp.actions[actionKey]
+ if (tmpaction.name === curaction.name) {
+ console.log("Found action - needs change?", tmpaction)
+ if (tmpaction.parameters !== undefined && tmpaction.parameters !== null && tmpaction.parameters.length > 0) {
+ curaction.parameters = JSON.parse(JSON.stringify(tmpaction.parameters))
+ }
+ break
+ }
+ }
+
+ }
+
+ // Fix authentication fields that may be missing in the UI
+ if (curapp.authentication.required && !curapp?.authentication?.type?.includes("oauth")) {
+ if (curapp.authentication.parameters !== undefined && curapp.authentication.parameters !== null) {
+ var actionChanged = false
+ for (let paramKey in curapp.authentication.parameters) {
+ var param = curapp.authentication.parameters[paramKey]
+
+ if (curaction.parameters === undefined || curaction.parameters === null) {
+ curaction.parameters = []
+ }
+
+ var found = false
+ for (let actionParamKey in curaction.parameters) {
+ if (curaction.parameters[actionParamKey].name === param.name) {
+ found = true
+ break
}
}
- //curaction["authentication"] = []
- //curaction["authentication_id"] = ""
- // Fix parameters that are... Not ideal
- //var paramnames = []
- //var newparams = []
- //for (let paramKey in curaction.parameters) {
- // console.log("Name: ", curaction.parameters[paramKey].name)
- // if (paramnames.includes(curaction.parameters[paramKey].name)) {
- // continue
- // }
+ if (!found) {
+ param.configuration = true
+ curaction.parameters.push(param)
+
+ actionChanged = true
+ }
+ }
- // paramnames.push(curaction.parameters[paramKey].name)
- // newparams.push(curaction.parameters[paramKey])
- //}
+ if (actionChanged && workflow.actions !== undefined && workflow.actions !== null) {
+ // Find it in the workflow and set it
+ for (let wfActionKey in workflow.actions) {
+ if (workflow.actions[wfActionKey].id === curaction.id) {
+ workflow.actions[wfActionKey] = curaction
+ }
+ }
- //curaction.parameters = newparams
+ setWorkflow(workflow)
+
+ }
+ }
+ }
- setSelectedApp(curapp);
- setSelectedAction(curaction);
+ setSelectedApp(curapp)
+ setSelectedAction(curaction)
cy.removeListener("drag");
cy.removeListener("free");
@@ -4738,7 +5598,7 @@ const AngularWorkflow = (defaultprops) => {
var trigger_index = workflow.triggers.findIndex(
(a) => a.id === data.id
- );
+ )
if (trigger_index === -1) {
workflow.triggers.push(data)
@@ -4757,7 +5617,6 @@ const AngularWorkflow = (defaultprops) => {
}
} else if (data.app_name === "Webhook") {
if (workflow.triggers[trigger_index].parameters !== undefined && workflow.triggers[trigger_index].parameters !== null && workflow.triggers[trigger_index].parameters.length > 0) {
- console.log("Can set params here!")
workflow.triggers[trigger_index].parameters[0] = {
name: "url",
value: referenceUrl + "webhook_" + workflow.triggers[trigger_index].id,
@@ -4793,9 +5652,89 @@ const AngularWorkflow = (defaultprops) => {
}
setTimeout(() => {
- setSelectedTriggerIndex(trigger_index);
+ if (trigger_index !== -1) {
+ const trigger = workflow.triggers[trigger_index]
+ if (trigger !== undefined && trigger !== null) {
+
+ // Autofixer
+ if (trigger.trigger_type === "USERINPUT") {
+ const relevantparams = [
+ "alertinfo",
+ "options",
+ "type",
+ "email",
+ "sms",
+ "subflow",
+ ]
+ var foundparams = 0
+ for (var paramkey in trigger.parameters) {
+ if (relevantparams.includes(trigger.parameters[paramkey].name)) {
+ foundparams++
+ }
+ }
+
+ if (foundparams < 6) {
+ trigger.parameters = [{
+ name: "alertinfo",
+ value: "Do you want to continue the workflow? Start parameters: $exec",
+ },{
+ name: "options",
+ value: "boolean",
+ },
+ {
+ name: "type",
+ value: "subflow",
+ },
+ {
+ name: "email",
+ value: "test@test.com",
+ },
+ {
+ name: "sms",
+ value: "0000000",
+ },
+ {
+ name: "subflow",
+ value: "",
+ }]
+
+ workflow.triggers[trigger_index].parameters = trigger.parameters
+ }
+ }
+ }
+ }
+
+ if (allTriggers !== undefined && allTriggers !== null) {
+
+ // Just checking all three. Could just make a new list, but meh
+ if (allTriggers.pipelines !== undefined && allTriggers.pipelines !== null) {
+ for (var pipelineKey in allTriggers.pipelines) {
+ if (allTriggers.pipelines[pipelineKey].id === data.id) {
+ data.status = allTriggers.pipelines[pipelineKey].status
+ }
+ }
+ }
+
+ if (allTriggers.webhooks !== undefined && allTriggers.webhooks !== null) {
+ for (var webhookKey in allTriggers.webhooks) {
+ if (allTriggers.webhooks[webhookKey].id === data.id) {
+ data.status = allTriggers.webhooks[webhookKey].status
+ }
+ }
+ }
+
+ if (allTriggers.schedules !== undefined && allTriggers.schedules !== null) {
+ for (var scheduleKey in allTriggers.schedules) {
+ if (allTriggers.schedules[scheduleKey].id === data.id) {
+ data.status = allTriggers.schedules[scheduleKey].status
+ }
+ }
+ }
+ }
+
+ setSelectedTriggerIndex(trigger_index)
setSelectedTrigger(data)
- setSelectedActionEnvironment(data.env)
+ //setSelectedActionEnvironment(data.env)
}, 25)
} else if (data.type === "COMMENT") {
setSelectedComment(data);
@@ -4805,7 +5744,7 @@ const AngularWorkflow = (defaultprops) => {
}
setRightSideBarOpen(true);
- setLastSaved(false);
+ //setLastSaved(false);
setScrollConfig({
top: 0,
left: 0,
@@ -5106,7 +6045,7 @@ const AngularWorkflow = (defaultprops) => {
: item.label.toLowerCase().trim().replaceAll(" ", "_");
exampledata = GetExampleResult(item);
- if (dstdata.parameters !== undefined && dstdata.parameters !== null) {
+ if (dstdata.parameters !== undefined && dstdata.parameters !== null) {
for (let [paramkey,paramkeyval] in Object.entries(dstdata.parameters)) {
const param = dstdata.parameters[paramkey];
// Skip authentication params
@@ -5132,9 +6071,8 @@ const AngularWorkflow = (defaultprops) => {
//dstdata.parameters[paramkey].value = `$${parentlabel}${foundresult}`;
}
}
- }
- }
- // Check agains every param
+ }
+ }
}
}
@@ -5143,10 +6081,12 @@ const AngularWorkflow = (defaultprops) => {
// Checks for errors in edges when they're added
const onEdgeAdded = (event) => {
- const edge = event.target.data()
- //console.log("EDGE ADDED!: ", edge)
+ setLastSaved(false);
+ const edge = event.target.data();
+
if (edge.source === undefined && edge.target === undefined) {
- console.log("Edge source and target is undefined")
+ //console.log("Edge added without source or target")
+
return
}
@@ -5159,10 +6099,16 @@ const AngularWorkflow = (defaultprops) => {
const sourcenode = cy.getElementById(edge.source)
const destinationnode = cy.getElementById(edge.target)
+
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
- console.log("Source or destination node is undefined")
+ console.log("Source or destination node is undefined or null: ", sourcenode, destinationnode)
} else {
- //console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
+ if (sourcenode.data("name") === "switch") {
+ event.target.remove()
+ return
+ }
+
+ console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
if (sourcenode.data("type") === "TRIGGER") {
if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") {
setTimeout(() => {
@@ -5171,12 +6117,11 @@ const AngularWorkflow = (defaultprops) => {
(data) => data.data.source === edge.source && data.data.id !== edge.id
)
- console.log("Node: ", targetedge)
if (targetedge !== -1) {
+ event.target.remove()
//console.log("Found branch already!")
- toast.error("Triggers can have exactly one target node")
- event.target.remove()
+ toast.error("Triggers can point to exactly one start node")
return
@@ -5197,24 +6142,21 @@ const AngularWorkflow = (defaultprops) => {
}
}
- if (edge.decorator === true) {
- console.log("Doing nothing to branch because decorator")
- return
- }
var targetnode = workflow.triggers.findIndex(
(data) => data.id === edge.target
- );
+ )
if (targetnode !== -1) {
if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") {
+ console.log("User Input or Shuffle Workflow")
} else {
- toast("Can't have triggers as target of branch");
- event.target.remove();
+ toast("Can't have triggers as target of branch")
+ event.target.remove()
}
}
const eventTarget = event.target.target()
- //console.log("BUTTON ADDED! Find parent from: ", eventTarget)
+ console.log("BUTTON ADDED! Find parent from: ", eventTarget)
if (eventTarget.data("isButton") === true) {
const parentNode = cy.getElementById(eventTarget.data("attachedTo"))
event.target.remove()
@@ -5246,60 +6188,54 @@ const AngularWorkflow = (defaultprops) => {
return
}
-
- setLastSaved(false)
targetnode = -1;
// Check if:
// dest == source && source == dest
// dest == dest && source == source
// backend: check all children? to stop recursion
+ //
var found = false;
- const branches = cy.edges().jsons()
-
- const startNode = cy.nodes().jsons().find((node) => node.data.isStartNode === true)
- var startnodeId = workflow.start
- if (startNode !== undefined && startNode !== null) {
- startnodeId = startNode.data.id
- }
-
- //for (let branchkey in workflow.branches) {
- for (let branchkey in branches) {
- const branch = branches[branchkey].data
-
- //if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
- if (branch.target === edge.source && branch.source === edge.target) {
+ for (let branchkey in workflow.branches) {
+ if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
toast("A branch in the opposite direction already exists")
event.target.remove()
found = true
break
+ }
- //} else if (workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source) {
- } else if (branch.target === edge.target && branch.source === edge.source) {
+ if (workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source) {
+
+ console.log("That branch already exists: ", workflow.branches[branchkey])
+ const foundbranch = cy.getElementById(workflow.branches[branchkey].id)
+ if (foundbranch !== undefined && foundbranch !== null && foundbranch.data() !== undefined && foundbranch.data() !== null) {
+ console.log("Removing branch: ", foundbranch.data())
- if (branch.conditions === undefined) {
- // Edgehandles
- } else {
- console.log("Removing because the same branch already exists")
event.target.remove()
found = true
break
+ } else {
+ //console.log("Old branch didn't exist afterall. Remove.")
}
- } else if (edge.target === startnodeId) {
- targetnode = workflow.triggers.findIndex((data) => data.id === edge.source)
+ }
+ if (edge.target === workflow.start) {
+ targetnode = workflow.triggers.findIndex(
+ (data) => data.id === edge.source
+ );
if (targetnode === -1) {
if (targetnode.type !== "TRIGGER") {
- toast("Can't make arrow to starting node")
- event.target.remove()
- break
+ toast("Can't make arrow to starting node");
+ event.target.remove();
+ break;
}
found = true;
}
- //} else if (edge.source === workflow.branches[branchkey].source_id) {
- } else if (edge.source === branch.source) {
+ }
+
+ if (edge.source === workflow.branches[branchkey].source_id) {
// FIXME: Verify multi-target for triggers
// 1. Check if destination exists
// 2. Check if source is a trigger
@@ -5343,6 +6279,7 @@ const AngularWorkflow = (defaultprops) => {
newdst !== null
) {
const dstdata = RunAutocompleter(newdst.data());
+ //console.log("DST Autocompleter: ", dstdata);
}
var newbranch = {
@@ -5372,48 +6309,37 @@ const AngularWorkflow = (defaultprops) => {
const onNodeAdded = (event) => {
const node = event.target;
- const nodedata = event.target.data();
+ const nodedata = JSON.parse(JSON.stringify(event.target.data()))
- //if (Object.keys(nodedata).length === 1) {
- // console.log("Check if another node actually exists before adding")
- //}
- if (nodedata.finished === false || (nodedata.id !== undefined && nodedata.is_valid === undefined)
- ) {
- //if (nodedata.app_id === undefined) {
- //console.log("Returning because node is not valid: ", nodedata)
- return;
+ if (nodedata.finished === false || (nodedata.id !== undefined && nodedata.is_valid === undefined)) {
+ return
}
- // Check for recommendations when a new action is added
- // if (isLoaded === true && firstrequest === false) {
- // fetchRecommendations(workflow)
- // }
-
-
// DONT MOVE THIS LINE RIGHT HERE v
setLastSaved(false)
- // Dont move the line above. May break stuff.
-
-
if (node.isNode() && cy.nodes().size() === 1) {
- workflow.start = node.data("id");
- nodedata.isStartNode = true;
+ workflow.start = node.data("id")
+ nodedata.isStartNode = true
} else {
if (workflow.actions === null) {
console.log("Returning because node has no value")
- return;
+ return
}
// Remove bad startnode
for (let actionkey in workflow.actions) {
const action = workflow.actions[actionkey];
if (action.isStartNode && workflow.start !== action.id) {
- action.isStartNode = false;
+ action.isStartNode = false
}
}
}
+ if (nodedata.app_id == "3e320a20966d33c9b7e6790b2705f0bf") {
+ setWorkflowAsCode(true);
+ }
+
if (nodedata.decorator !== true && nodedata.attachedTo === undefined) {
var newdata = JSON.parse(JSON.stringify(nodedata))
newdata.large_image = ""
@@ -5428,15 +6354,8 @@ const AngularWorkflow = (defaultprops) => {
}
if (nodedata.type === "ACTION") {
- // Should get recommendations to load in for all nodesma
+ // Should get recommendations to load in for all nodesma
- /*
- var curaction = workflow.actions.find((a) => a.id === nodedata.id);
- if (curaction === null || curaction === undefined) {
- toast("Node not found. Please remake it.")
- event.target.remove();
- }
- */
if (workflow.actions.length === 1 && workflow.actions[0].id === workflow.start) {
const newEdgeUuid = uuidv4();
const newcybranch = {
@@ -5475,29 +6394,23 @@ const AngularWorkflow = (defaultprops) => {
}
- if (
- nodedata.parameters !== undefined &&
- nodedata.parameters !== null &&
- !nodedata.label.endsWith("_copy")
- ) {
+ if (nodedata.parameters !== undefined && nodedata.parameters !== null && !nodedata?.label?.endsWith("_copy")) {
var newparameters = [];
for (let [subkey,subkeyval] in Object.entries(nodedata.parameters)) {
- var newparam = JSON.parse(
- JSON.stringify(nodedata.parameters[subkey])
- );
- newparam.id = uuidv4();
+ var newparam = JSON.parse(JSON.stringify(nodedata.parameters[subkey]))
+ newparam.id = uuidv4()
if (newparam.value === undefined || newparam.value === null) {
- newparam.value = "";
+ newparam.value = ""
} else {
- newparam.value = newparam.value;
+ newparam.value = newparam.value
}
newparameters.push(newparam);
}
- nodedata.parameters = newparameters;
+ nodedata.parameters = newparameters
}
if (workflow.actions === undefined || workflow.actions === null) {
@@ -5588,6 +6501,8 @@ const AngularWorkflow = (defaultprops) => {
if (curnode.data.id === edge.data("source")) {
console.log("Found matching trigger source: ", curnode)
if (curnode.data.app_name !== "Shuffle Workflow" && curnode.data.app_name !== "User Input") {
+
+
// If it's started, READD the edge
if (curnode.data.status === "running") {
//console.log("Edge is running - readd it: ", edge.data())
@@ -5602,7 +6517,8 @@ const AngularWorkflow = (defaultprops) => {
data: newdata,
})
- toast.error("You must STOP the trigger before deleting its branches")
+ //toast.error("You must STOP the trigger before deleting its branches")
+ console.log("You must STOP the trigger before deleting its branches")
} catch (e) {
console.log("Failed re-adding edge: ", e)
}
@@ -5701,9 +6617,6 @@ const AngularWorkflow = (defaultprops) => {
}
setWorkflow(workflow);
- //if (data.type === "TRIGGER") {
- // saveWorkflow(workflow);
- //}
}
//var previouskey = 0
@@ -5907,17 +6820,24 @@ const AngularWorkflow = (defaultprops) => {
document.addEventListener("paste", handlePaste);
};
- const getEnvironments = () => {
+ const getEnvironments = (orgId) => {
+ var headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ headers["Org-Id"] = orgId
+ }
+
fetch(globalUrl + "/api/v1/getenvironments", {
method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
+ headers: headers,
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
+
console.log("Status not 200 for envs :O!");
if (isCloud) {
setEnvironments([{ Name: "Cloud", Type: "cloud" }]);
@@ -5931,42 +6851,58 @@ const AngularWorkflow = (defaultprops) => {
return response.json();
})
.then((responseJson) => {
- var found = false;
- var showEnvCnt = 0;
+ var found = false
+ var showEnvCnt = 0
for (let jsonkey in responseJson) {
if (responseJson[jsonkey].default && !found) {
- setDefaultEnvironmentIndex(jsonkey);
- found = true;
+ setDefaultEnvironmentIndex(jsonkey)
+ found = true
}
if (responseJson[jsonkey].archived === false) {
- showEnvCnt += 1;
+ showEnvCnt += 1
}
}
if (showEnvCnt > 1) {
- setShowEnvironment(true);
+ setShowEnvironment(true)
}
if (!found) {
for (let jsonkey in responseJson) {
if (!responseJson[jsonkey].archived) {
- setDefaultEnvironmentIndex(jsonkey);
+ setDefaultEnvironmentIndex(jsonkey)
break;
}
}
}
- // FIXME: Don't allow multiple in cloud yet. Cloud -> Onprem isn't stable.
if (isCloud) {
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) {
- setEnvironments(responseJson);
+ setEnvironments(responseJson)
} else {
- setEnvironments([{ Name: "Cloud", Type: "cloud" }]);
+ setEnvironments([{ Name: "Cloud", Type: "cloud" }])
}
} else {
- setEnvironments(responseJson);
+ setEnvironments(responseJson)
}
+
+ /*
+ setTimeout(() => {
+ console.log("ACTIONS: ", workflow.actions)
+ if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
+ for (var actionkey in workflow.actions) {
+ if (workflow.actions[actionkey].environment !== undefined && workflow.actions[actionkey].environment !== null && workflow.actions[actionkey].environment.length > 0) {
+
+ const env = environments.findIndex((data) => data.Name === workflow.actions[actionkey].environment)
+ if (env !== -1) {
+ setSelectedActionEnvironment(environments[env])
+ }
+ }
+ }
+ }
+ }, 2500)
+ */
})
.catch((error) => {
//toast(error.toString());
@@ -5983,7 +6919,15 @@ const AngularWorkflow = (defaultprops) => {
workflow.id !== null &&
workflow.id.length > 0
) {
- window.location.pathname = "/workflows/" + props.match.params.key;
+
+ // Check if
+ if (distributedFromParent === "" && suborgWorkflows === []) {
+ toast.info("Redirecting as the workflow ID does not match the URL")
+
+ setTimeout(() => {
+ window.location.pathname = "/workflows/" + props.match.params.key;
+ }, 2500)
+ }
}
const animationDuration = 150;
@@ -6015,9 +6959,13 @@ const AngularWorkflow = (defaultprops) => {
}
}
- return;
+ return
}
+ if (nodedata.name === "switch") {
+ return
+ }
+
// console.log("nodedata", nodedata);
// console.log("nodedata.app_name: ", nodedata.app_name);
if (nodedata.app_name !== undefined) {
@@ -6027,10 +6975,12 @@ const AngularWorkflow = (defaultprops) => {
for (var nodekey in allNodes) {
const currentNode = allNodes[nodekey];
// console.log("Current node: ", currentNode);
- if (
- currentNode.data.isButton &&
- currentNode.data.attachedTo !== nodedata.id
- ) {
+ if (currentNode.data.isButton && currentNode.data.attachedTo !== nodedata.id) {
+
+ if (currentNode.data.buttonType === "condition-drag") {
+ continue
+ }
+
cy.getElementById(currentNode.data.id).remove();
}
@@ -6133,6 +7083,146 @@ const AngularWorkflow = (defaultprops) => {
// Maybe it shouldn't be onclick?
}
+ const addConditionDraggers = (event, allElements, branches) => {
+ const nodedata = event.target.data()
+ const position = event.target.position()
+
+ var conditions = []
+ const foundParam = nodedata.parameters.find((param) => param.name.toLowerCase() === "conditions")
+
+ try {
+ conditions = JSON.parse(foundParam.value)
+ } catch (e) {
+ //toast("Failed parsing conditions: ", e)
+ }
+
+ // Test conditions
+ if (conditions === undefined || conditions === null || typeof conditions !== "object") {
+ return
+ }
+
+ // Look for if it has the "Else" condition or not
+ const elseindex = conditions.findIndex((condition) => condition.name.toLowerCase() === "else")
+ const parentId = nodedata.id
+
+ // Force following of Else at the least
+ const newId = uuidv5(parentId, uuidv5.URL)
+ if (elseindex === -1) {
+ conditions.push({
+ name: "Else",
+ check: "Else",
+ id: newId,
+ parent_source: parentId,
+ })
+ } else {
+ conditions[elseindex].id = newId
+ }
+
+ // 4 conditions (with else) = 300px -> 75px each
+ const parentHeight = (conditions.length*75)*0.75
+
+
+ var startheight = -parentHeight/2
+ var newnodes = []
+ for (let conditionkey in conditions) {
+ var circleId = conditions[conditionkey].id === undefined ? (newNodeId = uuidv4()) : conditions[conditionkey].id
+
+ // Check if circleId is a valid uuid or not
+ if (circleId === undefined || circleId === null) {
+ circleId = uuidv4()
+ }
+
+ if (!isUUID(circleId)) {
+ if (conditions[circleId].name !== undefined && conditions[circleId].name !== null) {
+ circleId = uuidv5(conditions[circleId].name, uuidv5.URL)
+ } else {
+ circleId = uuidv4()
+ conditions[conditionkey].name = circleId
+ conditions[conditionkey].id = circleId
+ }
+ }
+
+ // Check if circleId already exists as a node
+ if (cy !== undefined && cy !== null) {
+ const existingNode = cy.getElementById(circleId)
+ if (existingNode !== undefined && existingNode !== null && existingNode.length > 0) {
+ continue
+ }
+ }
+
+ // 1. Create "small" nodes at each point along the section based on the amount of conditions
+ // 2. Make these conditions have edgehandles
+ // 3. Make these conditions have a "drag" handle
+ const px = position.x + 65
+ const py = position.y + startheight
+
+ console.log("Y height: ", startheight)
+
+ const node = {
+ group: "nodes",
+ data: {
+ name: conditions[conditionkey].name,
+ id: circleId,
+ buttonType: "condition-drag",
+ attachedTo: nodedata.id,
+ is_valid: true,
+ },
+ position: {
+ x: px,
+ y: py,
+ },
+ locked: true,
+ }
+
+ newnodes.push(node)
+
+ // Check if ANY of the incoming branches has the id as source
+ if (branches !== undefined && branches !== null && branches.length > 0) {
+ for (let branchkey in branches) {
+ const branch = branches[branchkey]
+ if (branch.source_id !== circleId) {
+ continue
+ }
+
+ const branchid = uuidv4()
+ newnodes.push({
+ group: "edges",
+ data: {
+ id: branchid,
+ _id: branchid,
+
+ source: circleId,
+ target: branch.destination_id,
+ label: branch.label,
+ conditions: branch.conditions,
+ hasErrors: branch.has_errors,
+ decorator: false,
+ parent_source: parentId,
+ }
+ })
+ }
+ }
+
+ startheight = startheight + parentHeight/(conditions.length-1)
+ }
+
+ if (cy !== undefined && cy !== null) {
+ cy.add(newnodes)
+ } else {
+ var newelements = elements
+ if (allElements !== undefined) {
+ newelements = allElements
+ }
+
+ for (let nodekey in newnodes) {
+ newelements.push(newnodes[nodekey])
+ }
+
+ console.log("ELEMENTS: ", newelements)
+ setElements(newelements)
+ }
+ }
+
const addCopyButton = (event) => {
var parentNode = cy.$("#" + event.target.data("id"));
if (parentNode.data("isButton") || parentNode.data("buttonId")) return;
@@ -6171,7 +7261,6 @@ const AngularWorkflow = (defaultprops) => {
};
const addActionSuggestions = (nodedata, event) => {
- console.log("App Action suggestions being added")
if (nodedata.type !== "ACTION") {
return
}
@@ -6184,13 +7273,41 @@ const AngularWorkflow = (defaultprops) => {
const px = parentNode.position("x") + 0;
const py = parentNode.position("y") + 100;
- const parentlabel = parentNode.data("label").toLowerCase().replace(" ", "_")
- const parentname = parentNode.data("app_name").toLowerCase().replace(" ", "_")
- if (!parentlabel.startsWith(parentname)) {
- console.log("Bad startname to start with: ", parentname, parentlabel)
+ const parentlabel = parentNode.data("label")?.toLowerCase().replace(" ", "_")
+ const parentname = parentNode.data("app_name")?.toLowerCase().replace(" ", "_")
+ if (!parentlabel?.startsWith(parentname)+"_") {
return
}
+ // Check if action has changed
+ const parentAppId = parentNode.data("app_id")
+ const parentActionname = parentNode.data("name")
+ for (var appkey in apps) {
+ const curapp = apps[appkey]
+
+ if (curapp.id !== parentAppId) {
+ continue
+ }
+
+ if (curapp.actions === undefined || curapp.actions === null || curapp.actions.length === 0) {
+ continue
+ }
+
+ var startIndex = curapp.actions.findIndex((action) => action.category_label !== undefined && action.category_label !== null && action.category_label.length > 0)
+ if (startIndex === -1) {
+ startIndex = 0
+ }
+
+ if (curapp.actions[startIndex].name !== parentActionname) {
+ console.log("Return 2")
+ return
+ }
+
+ break
+ }
+
+ console.log("CONTINUE EVEN WHEN FIELDS ARE FILLED")
+
const iconInfo = {
icon: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z",
iconColor: buttonColor,
@@ -6204,8 +7321,7 @@ const AngularWorkflow = (defaultprops) => {
// 2. Loop the apps' actions
// 3. Find actions based on category label IF it exists
- console.log("Fidning app match for: ", parentname)
- var added = 0
+ var addedLabels = []
for (let appKey in apps) {
const curapp = apps[appKey]
if (curapp.name.toLowerCase().replace(" ", "_") !== parentname) {
@@ -6216,7 +7332,6 @@ const AngularWorkflow = (defaultprops) => {
continue
}
- console.log("Found matching: ", curapp.name, parentname, curapp.actions.length)
for (let actionKey in curapp.actions) {
const curaction = curapp.actions[actionKey]
@@ -6226,7 +7341,13 @@ const AngularWorkflow = (defaultprops) => {
}
if (curaction.category_label !== undefined && curaction.category_label !== null && curaction.category_label.length > 0) {
- console.log("IN NODE ADD")
+ if (addedLabels.includes(curaction.category_label[0])) {
+ continue
+ }
+
+ if (curaction.category_label[0].replaceAll("_", " ").toLowerCase() === "no label") {
+ continue
+ }
cy.add({
group: "nodes",
@@ -6240,12 +7361,13 @@ const AngularWorkflow = (defaultprops) => {
},
position: {
x: px,
- y: py + (added * 50),
+ y: py + (addedLabels.length * 50),
},
+ locked: true,
})
- added += 1
- if (added >= 3) {
+ addedLabels.push(curaction.category_label[0])
+ if (addedLabels.length >= 2) {
break
}
}
@@ -6495,7 +7617,7 @@ const AngularWorkflow = (defaultprops) => {
};
const onNodeHover = (event) => {
- const nodedata = event.target.data();
+ const nodedata = JSON.parse(JSON.stringify(event.target.data()))
const cytoscapeElement = document.getElementById("cytoscape_view")
if (cytoscapeElement !== undefined && cytoscapeElement !== null) {
@@ -6509,7 +7631,7 @@ const AngularWorkflow = (defaultprops) => {
})
if (nodedata.finished === false) {
- console.log("NODE UNFINISHED (hover in): ", nodedata)
+ console.log("NODE UNFINISHED (hover in): ", JSON.parse(JSON.stringify(nodedata)))
// Should just be 1, so this should be fast enough :3
const incomingEdges = event.target.incomers("edge").jsons()
@@ -6529,10 +7651,11 @@ const AngularWorkflow = (defaultprops) => {
}
}
- return;
+ return
}
+
//var parentNode = cy.$("#" + event.target.data("id"));
//if (parentNode.data("isButton") || parentNode.data("buttonId")) return;
@@ -6540,8 +7663,6 @@ const AngularWorkflow = (defaultprops) => {
const allNodes = cy.nodes().jsons();
if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") {
- console.log("In this :)")
-
var found = false;
for (let nodekey in allNodes) {
const currentNode = allNodes[nodekey];
@@ -6555,11 +7676,9 @@ const AngularWorkflow = (defaultprops) => {
}
if (!found) {
- console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions)
// 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: {
@@ -6589,7 +7708,12 @@ const AngularWorkflow = (defaultprops) => {
// console.log("CURRENT NODE: ", currentNode)
if ((currentNode.data.buttonType === "ACTIONSUGGESTION" || currentNode.data.isButton || currentNode.data.isSuggestion) && currentNode.data.attachedTo !== nodedata.id) {
- cy.getElementById(currentNode.data.id).remove();
+
+ if (currentNode.data.buttonType === "condition-drag") {
+ continue
+ }
+
+ cy.getElementById(currentNode.data.id).remove()
}
/*if (
@@ -6604,8 +7728,13 @@ const AngularWorkflow = (defaultprops) => {
}
}
+ if (nodedata.name === "switch") {
+ addConditionDraggers(event)
+ return
+ }
+
if (!found) {
- addDeleteButton(event);
+ addDeleteButton(event)
if (nodedata.type === "TRIGGER") {
if (nodedata.trigger_type === "SUBFLOW" || nodedata.trigger_type === "USERINPUT") {
@@ -6613,25 +7742,29 @@ const AngularWorkflow = (defaultprops) => {
} else {
// Check how many executions from the source
addRunCountButton(event);
- }
- } else {
- addCopyButton(event);
- addStartnodeButton(event);
- }
+ }
+ } else {
- // autocomplete
- // right click
- // suggestions
- addActionSuggestions(nodedata, event);
-
- if (workflow.actions.length < 4) {
- addSuggestionButtons(nodedata, event);
- } else {
- //console.log("Too many actions to suggest (for now)")
- }
+ addCopyButton(event);
+ addStartnodeButton(event);
}
+
+ // autocomplete
+ // right click
+ // suggestions
+ addActionSuggestions(nodedata, event);
+
+ if (workflow.actions.length < 4) {
+ addSuggestionButtons(nodedata, event);
+ } else {
+ //console.log("Too many actions to suggest (for now)")
+ }
+ }
}
+ if (nodedata.name === "switch") {
+ return
+ }
var parsedStyle = {
"border-width": "7px",
@@ -6649,35 +7782,13 @@ const AngularWorkflow = (defaultprops) => {
const item = typeIds[idkey]
if (item.data.id === nodedata.id) {
//console.log("items: ", item.data.id, nodedata.id)
- parsedStyle["border-width"] = "12px"
+ parsedStyle["border-width"] = "7px"
break
}
}
if (nodedata.type !== "COMMENT") {
parsedStyle.color = "white";
-
- //if (!event.target.data("isButton") && !event.target.data("buttonId")) {
- // const px = event.target.position("x") - 0;
- // const py = event.target.position("y") - 50;
- // const circleId = (newNodeId = uuidv4());
-
- // console.log("Got px, py: ", px, py)
- //
- // cy.add({
- // group: "nodes",
- // data: {
- // weight: 30,
- // id: circleId,
- // isButton: true,
- // attachedTo: event.target.data("id"),
- // buttonType: "edgehandler",
- // is_valid: true,
- // },
- // position: { x: px, y: py },
- // locked: true,
- // })
- //}
}
if (event.target !== undefined && event.target !== null) {
@@ -6894,7 +8005,7 @@ const AngularWorkflow = (defaultprops) => {
// Reset cytoscape nodes and branches
if (cy !== undefined && cy !== null) {
if (inputworkflow.actions !== undefined && inputworkflow.actions !== null && inputworkflow.actions.length > 0) {
- cy.remove('*')
+ //cy.remove('*')
}
}
@@ -6937,9 +8048,8 @@ const AngularWorkflow = (defaultprops) => {
} else {
action.iconBackground = iconInfo.iconBackgroundColor;
}
- } else if (action.app_name === "Integration Framework") {
+ } else if (action.app_name === "Integration Framework" || action.app_name === "Singul") {
const iconInfo = GetIconInfo(action)
- console.log("FOUND INTEGRATION: iconInfo: ", iconInfo)
if (iconInfo !== undefined && iconInfo !== null) {
action.fillGradient = iconInfo.fillGradient
action.iconBackground = iconInfo.iconBackgroundColor
@@ -6955,11 +8065,17 @@ const AngularWorkflow = (defaultprops) => {
node.data.type = "ACTION";
node.isStartNode = action["id"] === inputworkflow.start;
+ if (node.data.errors !== undefined && node.data.errors !== null && node.data.errors.length > 0) {
+ node.data.is_valid = false
+ node.is_valid = false
+ }
+
if (inputworkflow.public === true) {
node.data.is_valid = true
node.is_valid = true
}
+
var example = "";
if (
action.example !== undefined &&
@@ -6971,25 +8087,75 @@ const AngularWorkflow = (defaultprops) => {
node.data.example = example;
- return node;
- });
+ if (inputworkflow?.id !== undefined && originalWorkflow?.id !== undefined && inputworkflow?.id !== originalWorkflow?.id && originalWorkflow.actions !== undefined && originalWorkflow.actions !== null && originalWorkflow.actions.length > 0) {
+ // Find the node in the original workflow
+ var inParent = false
+ for (var i = 0; i < originalWorkflow.actions.length; i++) {
+ const originalAction = originalWorkflow.actions[i]
+ if (originalAction.id === action.id) {
+ inParent = true
+ break
+ }
+ }
+ if (inParent === true) {
+ setTimeout(() => {
+ const foundnode = cy.getElementById(action.id)
+ if (foundnode !== undefined && foundnode !== null) {
+ const parsedStyle = {
+ "border-width": "3px",
+ "border-opacity": "1",
+ "border-color": "#40E0D0",
+ "opacity": "0.4",
+ }
+
+ const animationDuration = 150
+ foundnode.animate(
+ {
+ style: parsedStyle,
+ },
+ {
+ duration: animationDuration,
+ }
+ )
+ }
+ }, 500)
+ }
+ }
+
+ return node
+ })
+
+ // What are these again? Where are they used?
+ const decoratorNodes = []
+
+ /*
+ // Removed for now as it wasn't really that helpful
const decoratorNodes = inputworkflow.actions.map((action) => {
if (!action.isStartNode) {
if (action.app_name === "Testing") {
return null
} else if (action.app_name === "Shuffle Tools") {
return null
- } else if (action.app_name === "Integration Framework") {
+ } else if (action.app_name === "Integration Framework" || action.app_name === "Singul") {
return null
}
}
+ if (action.id === undefined || action.id === null) {
+ return null
+ }
+
+ if (action.position === undefined || action.position === null || action.position.x === undefined || action.position.x === null || action.position.y === undefined || action.position.y === null) {
+ return null
+ }
+
const iconInfo = GetIconInfo(action);
const svg_pin = ` `;
const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin);
const offset = action.isStartNode ? 36 : 44;
+
const decoratorNode = {
position: {
x: action.position.x + offset,
@@ -7005,9 +8171,10 @@ const AngularWorkflow = (defaultprops) => {
imageColor: iconInfo.iconBackgroundColor,
attachedTo: action.id,
},
- };
- return decoratorNode;
- });
+ }
+ return decoratorNode
+ })
+ */
const foundtriggers = inputworkflow.triggers.map((trigger) => {
@@ -7029,6 +8196,42 @@ const AngularWorkflow = (defaultprops) => {
node.data.id = trigger["id"];
node.data.type = "TRIGGER";
+ if (inputworkflow?.id !== undefined && originalWorkflow?.id !== undefined && inputworkflow?.id !== originalWorkflow?.id && originalWorkflow.triggers !== undefined && originalWorkflow.triggers !== null && originalWorkflow.triggers.length > 0) {
+ // Find the node in the original workflow
+ var inParent = false
+ for (var i = 0; i < originalWorkflow.triggers.length; i++) {
+ const originalAction = originalWorkflow.triggers[i]
+ if (originalAction.id === trigger.id) {
+ inParent = true
+ break
+ }
+ }
+
+ if (inParent === true) {
+ setTimeout(() => {
+ const foundnode = cy.getElementById(trigger.id)
+ if (foundnode !== undefined && foundnode !== null) {
+ const parsedStyle = {
+ "border-width": "3px",
+ "border-opacity": "1",
+ "border-color": "#40E0D0",
+ "opacity": "0.4",
+ }
+
+ const animationDuration = 150
+ foundnode.animate(
+ {
+ style: parsedStyle,
+ },
+ {
+ duration: animationDuration,
+ }
+ )
+ }
+ }, 500)
+ }
+ }
+
return node;
});
@@ -7087,6 +8290,11 @@ const AngularWorkflow = (defaultprops) => {
}
*/
+ var parentcontrolled = false
+ if (branch.parent_controlled !== undefined && branch.parent_controlled !== null && branch.parent_controlled === true) {
+ parentcontrolled = true
+ }
+
edge.data = {
id: branch.id,
_id: branch.id,
@@ -7096,7 +8304,8 @@ const AngularWorkflow = (defaultprops) => {
conditions: conditions,
hasErrors: branch.has_errors,
decorator: false,
- };
+ parent_controlled: parentcontrolled,
+ }
// This is an attempt at prettier edges. The numbers are weird to work with.
// Bezier curves
@@ -7125,7 +8334,6 @@ const AngularWorkflow = (defaultprops) => {
return edge;
});
- console.log("VISUAL BRANCHES: ", inputworkflow.visual_branches)
if (inputworkflow.visual_branches !== undefined && inputworkflow.visual_branches !== null && inputworkflow.visual_branches.length > 0) {
const visualedges = inputworkflow.visual_branches.map((branch, index) => {
const edge = {};
@@ -7183,12 +8391,32 @@ const AngularWorkflow = (defaultprops) => {
// Reset view for cytoscape
if (cy !== undefined && cy !== null) {
cy.add(insertedNodes);
- cy.fit(null, 200);
+ cy.fit(null, 400);
} else {
setElements(insertedNodes);
}
- console.log("Setupgraph done 2!")
+ const additionalNodes = inputworkflow.actions.map((action) => {
+ // Looking for: el.data("name") != "switch"
+ if (action.name !== "switch") {
+ return null
+ }
+
+ addConditionDraggers({
+ target: {
+ // Run data() function
+ data: function() {
+ return action
+ },
+ position: function() {
+ return action.position
+ }
+ }
+ },
+ insertedNodes,
+ inputworkflow.branches,
+ )
+ })
}
const removeNode = (nodeId) => {
@@ -7350,7 +8578,6 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
if (responseJson.success !== false) {
- console.log("Usecases: ", usecases)
setUsecases(responseJson)
} else {
}
@@ -7391,18 +8618,47 @@ const AngularWorkflow = (defaultprops) => {
}
setAllRevisions(responseJson)
+ setSelectedVersion(responseJson[0])
})
.catch((error) => {
console.log("Error getting workflow revisions: ", error)
});
}
+ const loadTriggers = () => {
+ const url = `${globalUrl}/api/v1/triggers`
+ fetch(url,
+ {
+ method: "GET",
+ headers: { "content-type": "application/json" },
+ credentials: "include",
+ }
+ )
+ .then((response) => {
+ if (response.status !== 200) {
+ throw new Error("No folders :o!");
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson.success !== false) {
+ setAllTriggers(responseJson)
+ }
+ })
+ .catch((error) => {
+ console.log("Get outlook folders error: ", error.toString());
+ });
+ }
+
// eslint-disable-next-line react-hooks/exhaustive-deps
//useEffect(() => {
if (firstrequest) {
setFirstrequest(false);
getWorkflow(props.match.params.key, {});
+ getChildWorkflows(props.match.params.key)
getRevisionHistory(props.match.params.key)
+ loadTriggers()
getApps()
fetchUsecases()
@@ -7448,8 +8704,6 @@ const AngularWorkflow = (defaultprops) => {
// 2nd load - configures cytoscape
//} else if (!established && cy !== undefined && ((apps !== null && apps !== undefined && apps.length > 0) || workflow.public === true) && Object.getOwnPropertyNames(workflow).length > 0 && appAuthentication !== undefined && workflowRecommendations !== undefined) {
} else if (!established && cy !== undefined && ((apps !== null && apps !== undefined && apps.length > 0) || workflow.public === true) && Object.getOwnPropertyNames(workflow).length > 0 && appAuthentication !== undefined) {
- console.log("In POST graph setup!")
-
//This part has to load LAST, as it's kind of not async.
//This means we need everything else to happen first.
@@ -7457,8 +8711,6 @@ const AngularWorkflow = (defaultprops) => {
setEstablished(true);
// Validate if the node is just a node lol
- console.log("CY grid: ", cy.gridGuide)
-
// https://www.npmjs.com/package/cytoscape-grid-guide
//
if (cy.gridGuide !== undefined) {
@@ -7486,8 +8738,14 @@ const AngularWorkflow = (defaultprops) => {
if (cy.edgehandles !== undefined) {
cy.edgehandles({
handleNodes: (el) => {
+ // Check of length of el.data() is 1
+ if (el.data() === undefined || Object.keys(el.data()).length === 1) {
+ return false
+ }
+
if (el.isNode() &&
el.data("buttonType") != "ACTIONSUGGESTION" &&
+ el.data("name") != "switch" &&
!el.data("isButton") &&
!el.data("isDescriptor") &&
!el.data("isSuggestion") &&
@@ -7502,7 +8760,7 @@ const AngularWorkflow = (defaultprops) => {
loopAllowed: function (node) {
return false;
},
- });
+ })
//cy.edgehandles({
// preview: false,
@@ -7514,8 +8772,7 @@ const AngularWorkflow = (defaultprops) => {
}
// preview: true,
- console.log("In POST graph setup 2")
- cy.fit(null, 200);
+ cy.fit(null, 400)
cy.on("boxselect", "node", (e) => {
if (e.target.data("isButton") || e.target.data("isDescriptor") || e.target.data("isSuggestion")) {
@@ -7566,7 +8823,6 @@ const AngularWorkflow = (defaultprops) => {
document.title = "Workflow - " + workflow.name;
- console.log("In POST graph setup 3")
startWorkflowStream(props.match.params.key);
registerKeys();
@@ -7606,7 +8862,7 @@ const AngularWorkflow = (defaultprops) => {
trigger.status = "stopped";
setSelectedTrigger(trigger);
setWorkflow(workflow);
- saveWorkflow(workflow);
+ saveWorkflow(workflow)
})
.catch((error) => {
console.log("Stop schedule error: ", error.toString())
@@ -7624,7 +8880,6 @@ const AngularWorkflow = (defaultprops) => {
if (alledges !== undefined && alledges !== null && alledges.length > 0) {
for (let edgekey in alledges) {
const tmp = alledges[edgekey];
- console.log("TMP: ", tmp, tmp.data.source);
if (tmp.data.source === trigger.id) {
mappedStartnode = tmp.data.target;
break;
@@ -7663,11 +8918,11 @@ const AngularWorkflow = (defaultprops) => {
toast("Pipeline deleted!")
return
}
-
+ if (trigger.parameters){
trigger.parameters.push({
name: data.name,
value: data.command,
- });
+ });}
if (data.type === "stop") trigger.status = "stopped";
else trigger.status = "running";
@@ -7675,7 +8930,6 @@ const AngularWorkflow = (defaultprops) => {
setSelectedTrigger(trigger);
setWorkflow(workflow);
- console.log("Should set the status to running and save");
saveWorkflow(workflow);
}
})
@@ -7690,12 +8944,11 @@ const AngularWorkflow = (defaultprops) => {
return;
}
- var mappedStartnode = ""
- const alledges = cy.edges().jsons()
+ var mappedStartnode = ""
+ const alledges = cy.edges().jsons()
if (alledges !== undefined && alledges !== null && alledges.length > 0) {
for (let edgekey in alledges) {
const tmp = alledges[edgekey]
- console.log("TMP: ", tmp, tmp.data.source)
if (tmp.data.source === trigger.id) {
mappedStartnode = tmp.data.target
break
@@ -7750,7 +9003,33 @@ const AngularWorkflow = (defaultprops) => {
});
};
- const parsedHeight = isMobile ? bodyHeight - appBarSize * 4 : bodyHeight - appBarSize - 50
+ const getSigmaInfo = () => {
+ const url = globalUrl + "/api/v1/files/detection/sigma_rules";
+
+ fetch(url, {
+ method: "GET",
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then((response) =>
+ response.json().then((responseJson) => {
+ if (responseJson["success"] === false) {
+ toast("Failed to get sigma rules");
+ } else {
+ setRules(responseJson.sigma_info);
+
+ }
+ })
+ )
+ .catch((error) => {
+ console.log("Error in getting sigma files: ", error);
+ toast("An error occurred while fetching sigma rules");
+ });
+ };
+
+ const parsedHeight = isMobile ? bodyHeight - appBarSize * 4 : bodyHeight - 57
const appViewStyle = {
marginLeft: 5,
marginRight: 5,
@@ -7761,9 +9040,9 @@ const AngularWorkflow = (defaultprops) => {
};
const paperAppStyle = {
- borderRadius: theme.palette.borderRadius,
- minHeight: isMobile ? 50 : 70,
- maxHeight: isMobile ? 50 : 70,
+ borderRadius: theme.palette?.borderRadius,
+ minHeight: isMobile ? 50 : 55,
+ maxHeight: isMobile ? 50 : 55,
minWidth: isMobile ? 50 : "100%",
maxWidth: isMobile ? 50 : "100%",
marginTop: "5px",
@@ -7774,9 +9053,9 @@ const AngularWorkflow = (defaultprops) => {
};
const paperVariableStyle = {
- borderRadius: theme.palette.borderRadius,
- minHeight: 50,
- maxHeight: 50,
+ borderRadius: theme.palette?.borderRadius,
+ minHeight: 70,
+ maxHeight: 150,
minWidth: "100%",
maxWidth: "100%",
marginTop: "5px",
@@ -7784,10 +9063,10 @@ const AngularWorkflow = (defaultprops) => {
backgroundColor: theme.palette.surfaceColor,
cursor: "pointer",
display: "flex",
- };
+ }
- const VariableItem = (props) => {
- const { variable, index, type } = props;
+ const VariableItem = (props) => {
+ const { variable, index, type } = props;
const [open, setOpen] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
@@ -7864,7 +9143,7 @@ const AngularWorkflow = (defaultprops) => {
}
}}
>
- Name: {variable.name}
+ {variable.name}