import React, { useState, useEffect } from "react";
import { useInterval } from "react-powerhooks";
import { toast } from 'react-toastify';
import theme from "../theme.jsx";
import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"
import {
InputAdornment,
Tooltip,
TextField,
CircularProgress,
ButtonGroup,
Button,
Avatar,
ListItemAvatar,
Typography,
List,
ListItem,
ListItemText,
Collapse,
IconButton,
} from "@mui/material";
import {
FavoriteBorder as FavoriteBorderIcon,
Error as ErrorIcon,
CheckCircleRounded as CheckCircleRoundedIcon,
ExpandMore as ExpandMoreIcon,
ExpandLess as ExpandLessIcon,
Check as CheckIcon,
Visibility as VisibilityIcon,
} from "@mui/icons-material";
import { FixName } from "../views/Apps.jsx";
import aa from 'search-insights'
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
// Handles workflow updates on first open to highlight the issues of the workflow
// Variables
// Action (exists, missing fields)
// Action auth
// Triggers
//
// Specifically used for UNSAVED workflows only?
const ConfigureWorkflow = (props) => {
const {
apps,
isCloud,
workflow,
userdata,
globalUrl,
newWebhook,
referenceUrl,
saveWorkflow,
showTriggers,
submitSchedule,
setSelectedApp,
selectedAction,
appAuthentication,
setSelectedAction,
workflowExecutions,
getWorkflowExecution,
setAuthenticationType,
setAuthenticationModalOpen,
setConfigureWorkflowModalOpen,
setConfigurationFinished,
} = props;
const [requiredActions, setRequiredActions] = React.useState([]);
const [requiredVariables, setRequiredVariables] = React.useState([]);
const [requiredTriggers, setRequiredTriggers] = React.useState([]);
const [previousAuth, setPreviousAuth] = React.useState(appAuthentication);
const [itemChanged, setItemChanged] = React.useState(false);
const [firstLoad, setFirstLoad] = React.useState("");
const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false);
const [loopRunning, setLoopRunning] = useState(false)
const [checkStarted, setCheckStarted] = React.useState(false);
useEffect(() => {
if (requiredActions.length === 0) {
if (setConfigurationFinished !== undefined) {
setConfigurationFinished(true)
}
}
}, [requiredActions])
const stop = () => {
setLoopRunning(false)
}
const start = () => {
setLoopRunning(true)
}
useEffect(() => {
if (loopRunning) {
const intervalId = setInterval(() => {
if (!loopRunning) {
clearInterval(intervalId);
}
if (getWorkflowExecution !== undefined && workflowExecutions !== undefined) {
const paramkey = workflow.id
getWorkflowExecution(paramkey)
} else {
console.log("Executions or getWorkflowExecutions not defined")
}
}, 3000)
return () => clearInterval(intervalId);
}
}, [loopRunning])
/*
const { start, stop } = useInterval({
duration: 3000,
startImmediate: false,
callback: () => {
if (getWorkflowExecution !== undefined && workflowExecutions !== undefined) {
const paramkey = workflow.id
getWorkflowExecution(paramkey)
} else {
console.log("Executions or getWorkflowExecutions not defined")
}
},
});
*/
// ONLY when component is being unloaded, run stop() function
// This is to prevent the interval from running when the component is not being used
/*
useEffect(() => {
return () => {
stop()
}
}, [])
*/
// Where is this from?
if (workflow === undefined || workflow === null || workflow.id === undefined) {
//console.log("Workflow is undefined or null: ", workflow)
return null
}
if (apps === undefined || apps === null) {
//console.log("Apps is undefined or null: ", apps)
return null
}
if (appAuthentication === undefined || appAuthentication === null) {
//console.log("App authentication is undefined or null: ", appAuthentication)
return null
}
const getApp = (actionId, appId) => {
fetch(globalUrl + "/api/v1/apps/" + appId + "/config?openapi=false", {
headers: {
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status === 200) {
//toast("Successfully GOT app "+appId)
} else {
toast("Failed getting app");
}
return response.json();
})
.then((responseJson) => {
if (
responseJson.actions !== undefined &&
responseJson.actions !== null
) {
}
})
.catch((error) => {
toast(error.toString());
});
};
if (firstLoad.length === 0 || firstLoad !== workflow.id) {
if (apps === undefined || apps === null || apps.length === 0) {
console.log("No apps loaded: ", apps);
if (setConfigureWorkflowModalOpen !== undefined) {
setConfigureWorkflowModalOpen(false)
}
return null;
}
setFirstLoad(workflow.id)
const newactions = [];
for (let [key, keyval] in Object.entries(workflow.actions)) {
var action = JSON.parse(JSON.stringify(workflow.actions[key]))
var newaction = {
large_image: action.large_image,
app_name: action.app_name,
app_version: action.app_version,
activation_done: false,
must_activate: false,
must_authenticate: false,
auth_done: false,
action_ids: [],
action: action,
update_version: action.app_version,
app: {},
steps: [],
show_steps: false,
}
if (action.app_name.toLowerCase().endsWith("_api")) {
action.app_name = action.app_name.slice(0, -4)
}
if (action.app_name === "Integration Framework") {
var selected_app = ""
for (var paramkey in action.parameters) {
const param = action.parameters[paramkey]
if (param.name === "app_name") {
selected_app = param.value
break
}
}
for (var appauth in appAuthentication) {
if (appAuthentication[appauth].app.name.toLowerCase() === selected_app.toLowerCase()) {
newaction.auth_done = true
break
}
}
if (newaction.auth_done) {
continue
}
for (var appkey in apps) {
if (apps[appkey].name.toLowerCase() === selected_app.toLowerCase()) {
newaction.app_name = apps[appkey].name
newaction.app_version = apps[appkey].app_version
newaction.app = apps[appkey]
newaction.app_id = apps[appkey].id
newaction.must_activate = false
newaction.must_authenticate = true
newaction.steps.push({
"title": "Authenticate app",
"type": "authenticate",
"required": true,
})
action.app_id = apps[appkey].id
action.authentication_id = ""
action.authentication = {
"required": true,
}
break
}
}
if (newaction.app.id === undefined) {
console.log("Failed to find app: ", selected_app)
continue
}
newaction.update_version = "1.1.0"
}
// ID match OR name match + version match
//const app = apps.find((app) => app.id === action.app_id || (app.name === action.app_name && (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version)))))
// without version match
const newappname = action.app_name.toLowerCase().replaceAll(" ", "_")
var app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname)
if (app === undefined || app === null) {
const subapp = apps.find(app => app.name === action.app_name)
if (subapp !== undefined && subapp !== null) {
newaction.update_version = "1.1.0"
}
newaction.must_activate = true;
newaction.steps.push({
"title": "Activate app",
"type": "activate",
"required": true,
})
} else {
if (action.authentication_id !== "" && app.authentication.required === true) {
var authFound = false
for (var authkey in appAuthentication) {
if (appAuthentication[authkey].id === action.authentication_id) {
authFound = true
break
}
}
if (!authFound) {
action.authentication_id = ""
}
}
if (action?.authentication_id === "" && app?.authentication?.required === true && action.parameters !== undefined && action.parameters !== null) {
// Check if configuration is filled or not
var filled = true;
for (let [key,keyval] in Object.entries(action.parameters)) {
if (action.parameters[key].configuration) {
if (action.parameters[key].value === null || action.parameters[key].value.length === 0) {
filled = false;
break;
}
}
}
if (app?.authentication?.type === "oauth2" || app?.authentication?.type === "oauth2-app") {
filled = false
action.auth_type = "oauth2"
}
newaction.steps.push({
"title": "Authenticate app",
"type": "authenticate",
"required": true,
"auth_type": app.authentication.type,
})
if (!filled) {
newaction.must_authenticate = true;
newaction.action_ids.push(action.id);
}
} else if (action.authentication_id !== undefined && action.authentication_id !== null && action.authentication_id !== "" && app.authentication.required === true) {
console.log("FIXME: Should verify authentication ID ", action.authentication_id)
}
newaction.app = app;
}
if (newaction.errors !== undefined && newaction.errors !== null && newaction.errors.length > 0) {
//console.log("Node has errors!: ", action.errors)
}
//console.log(newaction.app_name,"AUTH: ", newaction.must_authenticate, " ACTIVATE: ", newaction.must_activate)
if (newaction.must_authenticate) {
var authenticationOptions = []
for (let [key,keyval] in Object.entries(appAuthentication)) {
const auth = appAuthentication[key]
if (auth.app.name === app.name && auth.active) {
//console.log("Found auth: ", auth)
authenticationOptions.push(auth);
newaction.authenticationId = auth.id;
break;
}
}
if (
newaction.authenticationId === null ||
newaction.authenticationId === undefined ||
newaction.authenticationId.length === ""
) {
//console.log("FAILED to authenticate node!")
if (
newactions.find(
(tmpaction) =>
tmpaction.app_id === newaction.app_id &&
tmpaction.app_name === newaction.app_name
) !== undefined
) {
//console.log("Action already found.");
} else {
newactions.push(newaction);
}
} else {
//console.log("Skipping node as it's already authenticated.")
newaction.authentication = authenticationOptions;
workflow.actions[key] = newaction;
}
} else if (newaction.must_activate) {
if (
newactions.find(
(tmpaction) =>
tmpaction.app_id === newaction.app_id &&
tmpaction.app_name === newaction.app_name
) !== undefined
) {
console.log("Action already found.");
} else {
newactions.push(newaction);
}
}
}
if (workflow.workflow_variables !== undefined && workflow.workflow_variables !== null && workflow.workflow_variables.length !== 0) {
for (let [key,keyval] in Object.entries(workflow.workflow_variables)) {
const variable = workflow.workflow_variables[key];
if (variable.value === undefined || variable.value === undefined || variable.value.length === 0) {
variable.value = "";
requiredVariables.push(variable);
}
variable.index = key;
}
}
if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length !== 0) {
for (let [key,keyval] in Object.entries(workflow.triggers)) {
var trigger = workflow.triggers[key];
trigger.index = key;
if (trigger.trigger_type === "WEBHOOK") {
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
const findapp = trigger.app_association.name.toLowerCase()
const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
// Adding webhook to start of it
if (foundindex >= 0) {
const tmpsteps = newactions[foundindex].steps
newactions[foundindex].steps = [
{
"title": "Configure Webhook",
"type": "webhook",
"required": true,
}
]
for (let [subkey,subkeyval] in Object.entries(tmpsteps)) {
newactions[foundindex].steps.push(tmpsteps[subkey])
}
newactions[foundindex].show_steps = true
continue
}
}
}
if (trigger.status === "running") {
continue;
}
if (
trigger.trigger_type === "SUBFLOW" ||
trigger.trigger_type === "USERINPUT"
) {
continue;
}
requiredTriggers.push(trigger)
}
}
if (setConfigureWorkflowModalOpen !== undefined && requiredTriggers.length === 0 && requiredVariables.length === 0 && newactions.length === 0 ) {
console.log("No required triggers, variables or actions. Closing modal.")
setConfigureWorkflowModalOpen(false)
}
//setRequiredTriggers(requiredTriggers)
setRequiredVariables(requiredVariables)
setRequiredActions(newactions)
}
if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) {
var newactions = []
for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) {
var newaction = requiredActions[actionkey];
const app = newaction.app;
for (let [key,keyval] in Object.entries(appAuthentication)) {
const auth = appAuthentication[key];
// Does this account for all the different ones of the same?
if (auth.app.name === app.name && auth.active === true) {
newaction.auth_done = true;
break;
}
}
newactions.push(newaction);
}
setRequiredActions(newactions);
setPreviousAuth(appAuthentication);
// Set auth done to true
//"auth_done": false
}
const TriggerSection = (props) => {
const { trigger } = props
return (
{
console.log("Img loaded.")
setTimeout(() => {
console.log("Img closing.")
setConfigureWorkflowModalOpen(false);
}, 1250)
}}/>
: