diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 1c850813..5678f61e 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -187,7 +187,7 @@ const ConfigureWorkflow = (props) => { console.log("No apps loaded: ", apps); if (setConfigureWorkflowModalOpen !== undefined) { - setConfigureWorkflowModalOpen(false); + setConfigureWorkflowModalOpen(false) } return null; @@ -198,7 +198,7 @@ const ConfigureWorkflow = (props) => { const newactions = []; for (let [key, keyval] in Object.entries(workflow.actions)) { - const action = workflow.actions[key]; + var action = JSON.parse(JSON.stringify(workflow.actions[key])) var newaction = { large_image: action.large_image, app_name: action.app_name, @@ -220,17 +220,67 @@ const ConfigureWorkflow = (props) => { } if (action.app_name === "Integration Framework") { - console.log("Skipping integration framework: ", action) - continue + 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(" ", "_") - const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname) + var app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname) if (app === undefined || app === null) { @@ -246,6 +296,19 @@ const ConfigureWorkflow = (props) => { "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 @@ -292,9 +355,11 @@ const ConfigureWorkflow = (props) => { //console.log(newaction.app_name,"AUTH: ", newaction.must_authenticate, " ACTIVATE: ", newaction.must_activate) if (newaction.must_authenticate) { - var authenticationOptions = []; + + var authenticationOptions = [] for (let [key,keyval] in Object.entries(appAuthentication)) { - const auth = appAuthentication[key]; + const auth = appAuthentication[key] + if (auth.app.name === app.name && auth.active) { //console.log("Found auth: ", auth) authenticationOptions.push(auth); @@ -403,17 +468,18 @@ const ConfigureWorkflow = (props) => { continue; } - requiredTriggers.push(trigger); + requiredTriggers.push(trigger) } } - if (requiredTriggers.length === 0 && requiredVariables.length === 0 && newactions.length === 0 && setConfigureWorkflowModalOpen !== undefined) { - setConfigureWorkflowModalOpen(false); + 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); + setRequiredTriggers(requiredTriggers) + setRequiredVariables(requiredVariables) + setRequiredActions(newactions) } if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) { diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index f1ca7520..7104f873 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -202,12 +202,12 @@ const Header = (props) => { removeCookie("__session", { path: "/" }); window.location.pathname = "/"; - localStorage.setItem("globalUrl", "") + localStorage.setItem("globalUrl", "") - // Delete userinfo from localstorage - localStorage.removeItem("apps") - localStorage.removeItem("workflows") - localStorage.removeItem("userinfo") + // Delete userinfo from localstorage + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") }) .catch((error) => { console.log(error); @@ -374,13 +374,13 @@ const Header = (props) => { setAnchorEl(event.currentTarget); }} > - {/* n.read === false).length} color="primary">*/} - + {/* n.read === false).length} color="primary">*/} + { }} >
- + Notifications ({notifications.filter((data) => !data.read).length}) - - {notifications.length > 1 ? ( - - ) : null} - - + + {notifications.length > 1 ? ( + + ) : null} + +
Notifications generated made by Shuffle to help you discover issues or @@ -481,10 +481,10 @@ const Header = (props) => { if (response.status !== 200) { console.log("Error in response"); } else { - localStorage.removeItem("apps") - localStorage.removeItem("workflows") - localStorage.removeItem("userinfo") - } + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") + } return response.json(); }) @@ -496,6 +496,10 @@ const Header = (props) => { localStorage.setItem("globalUrl", responseJson.region_url); //globalUrl = responseJson.region_url } + if (responseJson["reason"] === "SSO_REDIRECT") { + window.location.href = responseJson["url"] + return + } setTimeout(() => { window.location.reload(); @@ -567,7 +571,7 @@ const Header = (props) => { alt="Your username here" src={parsedAvatar} /> - + { handleClose(); }} > - Notifications + Notifications - {/*notificationMenu*/} + {/*notificationMenu*/} { {userdata === undefined || - userdata.orgs === undefined || - userdata.orgs === null || - userdata.orgs.length <= 0 ? null : ( + userdata.orgs === undefined || + userdata.orgs === null || + userdata.orgs.length <= 0 ? null : ( )} @@ -1444,100 +1448,100 @@ const Header = (props) => { : */ - const topbarHeight = showTopbar ? 40 : 0 - const topbar = !showTopbar ? null : - curpath === "/" || curpath.includes("/docs/") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" ? - -
- - Shuffle 1.4 is out! Read more about  - - { - ReactGA.event({ - category: "landingpage", - action: "click_header_features", - label: "", - }) + const topbarHeight = showTopbar ? 40 : 0 + const topbar = !showTopbar ? null : + curpath === "/" || curpath.includes("/docs/") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" ? + +
+ + Shuffle 1.4 is out! Read more about  + + { + ReactGA.event({ + category: "landingpage", + action: "click_header_features", + label: "", + }) - //if (window.drift !== undefined) { - // window.drift.api.startInteraction({ interactionId: 341911 }) - //} else { - // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) - //} - }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> - Features - - - ,  - - { - ReactGA.event({ - category: "landingpage", - action: "click_header_pricing", - label: "", - }) + //if (window.drift !== undefined) { + // window.drift.api.startInteraction({ interactionId: 341911 }) + //} else { + // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) + //} + }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> + Features + + + ,  + + { + ReactGA.event({ + category: "landingpage", + action: "click_header_pricing", + label: "", + }) - navigate("/pricing") + navigate("/pricing") - //if (window.drift !== undefined) { - // window.drift.api.startInteraction({ interactionId: 341911 }) - //} else { - // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) - //} - }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> - Pricing - - -  and  - - { - ReactGA.event({ - category: "landingpage", - action: "click_header_creators", - label: "", - }) + //if (window.drift !== undefined) { + // window.drift.api.startInteraction({ interactionId: 341911 }) + //} else { + // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) + //} + }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> + Pricing + + +  and  + + { + ReactGA.event({ + category: "landingpage", + action: "click_header_creators", + label: "", + }) - navigate("/creators") + navigate("/creators") + + //if (window.drift !== undefined) { + // window.drift.api.startInteraction({ interactionId: 341911 }) + //} else { + // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) + //} + }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> + Earning as a Creator + + + + { setShowTopbar(false) }}> + + +
+ + : + null - //if (window.drift !== undefined) { - // window.drift.api.startInteraction({ interactionId: 341911 }) - //} else { - // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) - //} - }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> - Earning as a Creator - -
-
- { setShowTopbar(false) }}> - - -
-
- : - null - return !isMobile ? -
- +
+ - {topbar} + {topbar} -
- {loginTextBrowser} -
-
-
+
+ {loginTextBrowser} +
+
+
: {loginTextMobile} }; diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 19352b66..2efae52a 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -8,27 +8,27 @@ import Stack from '@mui/material/Stack'; import SubflowSuggestions from "../components/SubflowSuggestions.jsx"; import { - FormControl, - InputLabel, - Paper, - OutlinedInput, - Checkbox, - Card, - Tooltip, - FormControlLabel, - Typography, - Switch, - Select, - MenuItem, - Divider, - TextField, - Button, - Tabs, - Tab, - Grid, - IconButton, - Autocomplete, - Dialog, + FormControl, + InputLabel, + Paper, + OutlinedInput, + Checkbox, + Card, + Tooltip, + FormControlLabel, + Typography, + Switch, + Select, + MenuItem, + Divider, + TextField, + Button, + Tabs, + Tab, + Grid, + IconButton, + Autocomplete, + Dialog, DialogTitle, DialogActions, DialogContent, @@ -36,218 +36,223 @@ import { } from "@mui/material"; import { - ExpandLess as ExpandLessIcon, - ExpandMore as ExpandMoreIcon, + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, Save as SaveIcon, } from "@mui/icons-material"; const useStyles = makeStyles({ - notchedOutline: { - borderColor: "#f85a3e !important", - }, + notchedOutline: { + borderColor: "#f85a3e !important", + }, }) const OrgHeaderexpanded = (props) => { - const { - userdata, - selectedOrganization, - setSelectedOrganization, - globalUrl, - isCloud, + const { + userdata, + selectedOrganization, + setSelectedOrganization, + globalUrl, + isCloud, adminTab, - } = props; + } = props; - const classes = useStyles(); - const defaultBranch = "master"; + const classes = useStyles(); + const defaultBranch = "master"; - const [orgName, setOrgName] = React.useState(selectedOrganization.name); - const [orgDescription, setOrgDescription] = React.useState( - selectedOrganization.description - ); + const [orgName, setOrgName] = React.useState(selectedOrganization.name); + const [orgDescription, setOrgDescription] = React.useState( + selectedOrganization.description + ); - const [appDownloadUrl, setAppDownloadUrl] = React.useState( - selectedOrganization.defaults === undefined - ? "https://github.com/frikky/shuffle-apps" - : selectedOrganization.defaults.app_download_repo === undefined || - selectedOrganization.defaults.app_download_repo.length === 0 - ? "https://github.com/frikky/shuffle-apps" - : selectedOrganization.defaults.app_download_repo - ); - const [appDownloadBranch, setAppDownloadBranch] = React.useState( - selectedOrganization.defaults === undefined - ? defaultBranch - : selectedOrganization.defaults.app_download_branch === undefined || - selectedOrganization.defaults.app_download_branch.length === 0 - ? defaultBranch - : selectedOrganization.defaults.app_download_branch - ); - const [workflowDownloadUrl, setWorkflowDownloadUrl] = React.useState( - selectedOrganization.defaults === undefined - ? "https://github.com/frikky/shuffle-apps" - : selectedOrganization.defaults.workflow_download_repo === undefined || - selectedOrganization.defaults.workflow_download_repo.length === 0 - ? "https://github.com/frikky/shuffle-workflows" - : selectedOrganization.defaults.workflow_download_repo - ); - const [workflowDownloadBranch, setWorkflowDownloadBranch] = React.useState( - selectedOrganization.defaults === undefined - ? defaultBranch - : selectedOrganization.defaults.workflow_download_branch === undefined || - selectedOrganization.defaults.workflow_download_branch.length === 0 - ? defaultBranch - : selectedOrganization.defaults.workflow_download_branch - ); - const [ssoEntrypoint, setSsoEntrypoint] = React.useState( - selectedOrganization.sso_config === undefined - ? "" - : selectedOrganization.sso_config.sso_entrypoint === undefined || - selectedOrganization.sso_config.sso_entrypoint.length === 0 - ? "" - : selectedOrganization.sso_config.sso_entrypoint - ); - const [ssoCertificate, setSsoCertificate] = React.useState( - selectedOrganization.sso_config === undefined - ? "" - : selectedOrganization.sso_config.sso_certificate === undefined || - selectedOrganization.sso_config.sso_certificate.length === 0 - ? "" - : selectedOrganization.sso_config.sso_certificate - ); + const [appDownloadUrl, setAppDownloadUrl] = React.useState( + selectedOrganization.defaults === undefined + ? "https://github.com/frikky/shuffle-apps" + : selectedOrganization.defaults.app_download_repo === undefined || + selectedOrganization.defaults.app_download_repo.length === 0 + ? "https://github.com/frikky/shuffle-apps" + : selectedOrganization.defaults.app_download_repo + ); + const [appDownloadBranch, setAppDownloadBranch] = React.useState( + selectedOrganization.defaults === undefined + ? defaultBranch + : selectedOrganization.defaults.app_download_branch === undefined || + selectedOrganization.defaults.app_download_branch.length === 0 + ? defaultBranch + : selectedOrganization.defaults.app_download_branch + ); + const [workflowDownloadUrl, setWorkflowDownloadUrl] = React.useState( + selectedOrganization.defaults === undefined + ? "https://github.com/frikky/shuffle-apps" + : selectedOrganization.defaults.workflow_download_repo === undefined || + selectedOrganization.defaults.workflow_download_repo.length === 0 + ? "https://github.com/frikky/shuffle-workflows" + : selectedOrganization.defaults.workflow_download_repo + ); + const [workflowDownloadBranch, setWorkflowDownloadBranch] = React.useState( + selectedOrganization.defaults === undefined + ? defaultBranch + : selectedOrganization.defaults.workflow_download_branch === undefined || + selectedOrganization.defaults.workflow_download_branch.length === 0 + ? defaultBranch + : selectedOrganization.defaults.workflow_download_branch + ); + const [ssoEntrypoint, setSsoEntrypoint] = React.useState( + selectedOrganization.sso_config === undefined + ? "" + : selectedOrganization.sso_config.sso_entrypoint === undefined || + selectedOrganization.sso_config.sso_entrypoint.length === 0 + ? "" + : selectedOrganization.sso_config.sso_entrypoint + ); + const [ssoCertificate, setSsoCertificate] = React.useState( + selectedOrganization.sso_config === undefined + ? "" + : selectedOrganization.sso_config.sso_certificate === undefined || + selectedOrganization.sso_config.sso_certificate.length === 0 + ? "" + : selectedOrganization.sso_config.sso_certificate + ); + const [SSORequired, setSSORequired] = React.useState(selectedOrganization.sso_config === undefined + ? false + : selectedOrganization.sso_config.SSORequired === undefined + ? false + : selectedOrganization.sso_config.SSORequired); - const [notificationWorkflow, setNotificationWorkflow] = React.useState( - selectedOrganization.defaults === undefined - ? "" - : selectedOrganization.defaults.notification_workflow === undefined || - selectedOrganization.defaults.notification_workflow.length === 0 - ? "" - : selectedOrganization.defaults.notification_workflow - ) + const [notificationWorkflow, setNotificationWorkflow] = React.useState( + selectedOrganization.defaults === undefined + ? "" + : selectedOrganization.defaults.notification_workflow === undefined || + selectedOrganization.defaults.notification_workflow.length === 0 + ? "" + : selectedOrganization.defaults.notification_workflow + ); - const [documentationReference, setDocumentationReference] = React.useState( - selectedOrganization.defaults === undefined - ? "" - : selectedOrganization.defaults.documentation_reference === undefined || - selectedOrganization.defaults.documentation_reference.length === 0 - ? "" - : selectedOrganization.defaults.documentation_reference - ); - const [openidClientId, setOpenidClientId] = React.useState( - selectedOrganization.sso_config === undefined - ? "" - : selectedOrganization.sso_config.client_id === undefined || - selectedOrganization.sso_config.client_id.length === 0 - ? "" - : selectedOrganization.sso_config.client_id - ); - const [openidClientSecret, setOpenidClientSecret] = React.useState( - selectedOrganization.sso_config === undefined - ? "" - : selectedOrganization.sso_config.client_secret === undefined || - selectedOrganization.sso_config.client_secret.length === 0 - ? "" - : selectedOrganization.sso_config.client_secret - ); - const [openidAuthorization, setOpenidAuthorization] = React.useState( - selectedOrganization.sso_config === undefined - ? "" - : selectedOrganization.sso_config.openid_authorization === undefined || - selectedOrganization.sso_config.openid_authorization.length === 0 - ? "" - : selectedOrganization.sso_config.openid_authorization - ); - const [openidToken, setOpenidToken] = React.useState( - selectedOrganization.sso_config === undefined - ? "" - : selectedOrganization.sso_config.openid_token === undefined || - selectedOrganization.sso_config.openid_token.length === 0 - ? "" - : selectedOrganization.sso_config.openid_token + const [documentationReference, setDocumentationReference] = React.useState( + selectedOrganization.defaults === undefined + ? "" + : selectedOrganization.defaults.documentation_reference === undefined || + selectedOrganization.defaults.documentation_reference.length === 0 + ? "" + : selectedOrganization.defaults.documentation_reference + ); + const [openidClientId, setOpenidClientId] = React.useState( + selectedOrganization.sso_config === undefined + ? "" + : selectedOrganization.sso_config.client_id === undefined || + selectedOrganization.sso_config.client_id.length === 0 + ? "" + : selectedOrganization.sso_config.client_id + ); + const [openidClientSecret, setOpenidClientSecret] = React.useState( + selectedOrganization.sso_config === undefined + ? "" + : selectedOrganization.sso_config.client_secret === undefined || + selectedOrganization.sso_config.client_secret.length === 0 + ? "" + : selectedOrganization.sso_config.client_secret + ); + const [openidAuthorization, setOpenidAuthorization] = React.useState( + selectedOrganization.sso_config === undefined + ? "" + : selectedOrganization.sso_config.openid_authorization === undefined || + selectedOrganization.sso_config.openid_authorization.length === 0 + ? "" + : selectedOrganization.sso_config.openid_authorization + ); + const [openidToken, setOpenidToken] = React.useState( + selectedOrganization.sso_config === undefined + ? "" + : selectedOrganization.sso_config.openid_token === undefined || + selectedOrganization.sso_config.openid_token.length === 0 + ? "" + : selectedOrganization.sso_config.openid_token ) - const [workflows, setWorkflows] = React.useState([]) - const [workflow, setWorkflow] = React.useState({}) + const [workflows, setWorkflows] = React.useState([]) + const [workflow, setWorkflow] = React.useState({}) - const getAvailableWorkflows = (trigger_index) => { - fetch(globalUrl + "/api/v1/workflows", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for workflows :O!"); - return; - } - return response.json(); - }) - .then((responseJson) => { - if (responseJson !== undefined) { - setWorkflows(responseJson) + const getAvailableWorkflows = (trigger_index) => { + fetch(globalUrl + "/api/v1/workflows", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for workflows :O!"); + return; + } + return response.json(); + }) + .then((responseJson) => { + if (responseJson !== undefined) { + setWorkflows(responseJson) - if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) { + if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) { - const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow) - if (workflow !== undefined && workflow !== null) { - setWorkflow(workflow) - } - } - } - }) - .catch((error) => { - console.log("Error getting workflows: " + error); - }) - } + const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow) + if (workflow !== undefined && workflow !== null) { + setWorkflow(workflow) + } + } + } + }) + .catch((error) => { + console.log("Error getting workflows: " + error); + }) + } - useEffect(() => { - getAvailableWorkflows() - }, []) + useEffect(() => { + getAvailableWorkflows() + }, []) - const handleEditOrg = ( - name, - description, - orgId, - image, - defaults, - sso_config - ) => { + const handleEditOrg = ( + name, + description, + orgId, + image, + defaults, + sso_config + ) => { - const data = { - name: name, - description: description, - org_id: orgId, - image: image, - defaults: defaults, - sso_config: sso_config, - }; + const data = { + name: name, + description: description, + org_id: orgId, + image: image, + defaults: defaults, + sso_config: sso_config, + }; - 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 { - toast("Successfully edited org!"); - } - }) - ) - .catch((error) => { - toast("Err: " + error.toString()); - }); - }; + 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 { + toast("Successfully edited org!"); + } + }) + ) + .catch((error) => { + toast("Err: " + error.toString()); + }); + }; const handleWorkflowSelectionUpdate = (e, isUserinput) => { @@ -258,217 +263,55 @@ const OrgHeaderexpanded = (props) => { setWorkflow(e.target.value) setNotificationWorkflow(e.target.value.id) - toast("Updated notification workflow. Don't forget to save!") + toast("Updated notification workflow. Don't forget to save!") } - const orgSaveButton = ( - -
- -
-
- ); - - const getAppIDs = async (appList) => { - fetch(globalUrl + "/api/v1/apps", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }).then((response) => { - if (response.status !== 200) { - toast("Failed getting app ids: ", response.reason); - console.log("Status not 200 for app ids :O!"); - return; - } - return response.json(); - }).then((responseJson) => { - if (responseJson !== undefined) { - // console.log("App ids: ", responseJson) - // console.log("App list: ", appList) - const filteredApps = responseJson.filter(app => appList.includes(app.name)); - const appDetails = filteredApps.map(app => ({ name: app.name, id: app.id })); - return appDetails - // console.log("App IDs: ", appDetails) - } - }).catch((error) => { - console.log("Error getting app ids: " + error); - }) -} - - - - - - - - -const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId,issuetype) => { - //currently only supports JIRA figure out a way to support more apps - var workflowName = `[GENERATED] ${appname} notification workflow` - var workflowDescription = "Generated by Shuffle for sending info/error notifications." - var data = { - "name": workflowName, - "description": workflowDescription, - } - - fetch(globalUrl + "/api/v1/workflows", { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(data), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - toast("Failed setting notification workflow: ", response.reason); - console.log("Status not 200 for workflows :O!"); - return; - } - return response.json(); - }).then((responseJson)=>{ - if (responseJson !== undefined) { - console.log("Notification workflow created successfully") - var workflow_id = responseJson.id - if (appname.toLowerCase() === "jira"){ - console.log("updating workflow for JIRA") - var workflowBody = { - "name": workflowName, - "Description": workflowDescription, - "id": workflow_id, - "actions": [ - { - "app_name": "Jira", - "name": "post_create_issue", - "authentication_id":appAuthId, - "large_image":appImage, - "isStartNode": true, - "label": "create_issue", - "app_version": "1.1.0", - "parameters": [ - { - "name": "body", - "value": `{"fields": { "project": {"key": "${projectId}"},"summary": "$exec.title","issuetype": {"name": "${issuetype}"},"description": {"content": [{"content":[{"type":"text","text":"$exec.description"}],"type": "paragraph"}],"type": "doc","version": 1}}}` - }, - { - "name": "username_basic", - "value": "" - }, - { - "name": "password_basic", - "value": "" - }, - { - "name": "url", - "value": "" - }, - { - "name": "headers", - "value": "Content-type=application/json \nAccept=application/json" - }, - { - "name": "queries", - "value": "" - }, - { - "name": "ssl_verify", - "value": "False" - } - ] - } - ] + const orgSaveButton = ( + +
+ +
+
+ ); - + const toggleBetweenRequiredOrOptional = (event) => { + setSSORequired(event.target.checked); + }; return (
@@ -488,145 +331,145 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId */} -
+
{workflows !== undefined && workflows !== null && workflows.length > 0 ? { - if ( - option === undefined || - option === null || - option.name === undefined || - option.name === null - ) { - return "No Workflow Selected"; - } - - const newname = ( - option.name.charAt(0).toUpperCase() + option.name.substring(1) - ).replaceAll("_", " "); - return newname; - }} - options={workflows} - fullWidth - style={{ - backgroundColor: theme.palette.inputColor, - height: 50, - borderRadius: theme.palette.borderRadius, - }} - onChange={(event, newValue) => { - console.log("Found value: ", newValue) - - var parsedinput = { target: { value: newValue } } - - // For variables - if (typeof newValue === 'string' && newValue.startsWith("$")) { - parsedinput = { - target: { - value: { - "name": newValue, - "id": newValue, - "actions": [], - "triggers": [], - } - } + id="notification_workflow_search" + autoHighlight + freeSolo + //autoSelect + value={workflow} + classes={{ inputRoot: classes.inputRoot }} + ListboxProps={{ + style: { + backgroundColor: theme.palette.inputColor, + color: "white", + }, + }} + getOptionLabel={(option) => { + if ( + option === undefined || + option === null || + option.name === undefined || + option.name === null + ) { + return "No Workflow Selected"; } - } - handleWorkflowSelectionUpdate(parsedinput) - }} - renderOption={(props, data, state) => { - if (data.id === workflow.id) { - data = workflow; - } + const newname = ( + option.name.charAt(0).toUpperCase() + option.name.substring(1) + ).replaceAll("_", " "); + return newname; + }} + options={workflows} + fullWidth + style={{ + backgroundColor: theme.palette.inputColor, + height: 50, + borderRadius: theme.palette.borderRadius, + }} + onChange={(event, newValue) => { + console.log("Found value: ", newValue) - return ( - - {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.name} - : null} - - Choose {data.name} - - - } placement="bottom"> - { - var parsedinput = { target: { value: data } } - handleWorkflowSelectionUpdate(parsedinput) - }} - > - {data.name} - - - ) - }} - renderInput={(params) => { - return ( - - ); - }} - /> - : - { - setNotificationWorkflow(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - } -
- {orgSaveButton} -
+ var parsedinput = { target: { value: newValue } } + + // For variables + if (typeof newValue === 'string' && newValue.startsWith("$")) { + parsedinput = { + target: { + value: { + "name": newValue, + "id": newValue, + "actions": [], + "triggers": [], + } + } + } + } + + handleWorkflowSelectionUpdate(parsedinput) + }} + renderOption={(props, data, state) => { + if (data.id === workflow.id) { + data = workflow; + } + + return ( + + {data.image !== undefined && data.image !== null && data.image.length > 0 ? + {data.name} + : null} + + Choose {data.name} + + + } placement="bottom"> + { + var parsedinput = { target: { value: data } } + handleWorkflowSelectionUpdate(parsedinput) + }} + > + {data.name} + + + ) + }} + renderInput={(params) => { + return ( + + ); + }} + /> + : + { + setNotificationWorkflow(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + } +
+ {orgSaveButton} +
- + Org Documentation reference @@ -659,10 +502,25 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId /> - {isCloud ? null : - - OpenID connect - + {/* {isCloud ? null : */} +
+
+ + {SSORequired ? 'Required' : 'Optional'} +
+ Make SAML SSO or OpenID Authentication Required or Optional for Your Organization. +
+
+
+ + OpenID connect + Client ID @@ -742,7 +600,7 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId - + Authorization URL @@ -813,11 +671,11 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId - } + {/* } */} {/*isCloud ? null : */} - - SAML SSO (v1.1) - + + SAML SSO (v1.1) + SSO Entrypoint (IdP) @@ -892,11 +750,11 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId - {isCloud ? - + {isCloud ? + IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso - : null} + : null} {isCloud ? null : ( @@ -1052,4 +910,4 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId ) } -export default OrgHeaderexpanded; +export default OrgHeaderexpanded; diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 5b8737cf..48f99556 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1485,15 +1485,15 @@ const ParsedAction = (props) => { ); } - // Added autofill to make this ALOT simpler - if (isCloud && (selectedAction.app_name === "Shuffle Tools" || selectedAction.app_name === "email") && (selectedAction.name === "send_email_shuffle" || selectedAction.name === "send_sms_shuffle") && data.name === "apikey") { - if (selectedActionParameters[count].length === 0) { - selectedAction.parameters[count].value = "TMP: Will be replaced during execution if cloud" - setSelectedAction(selectedAction) - } + // Added autofill to make this ALOT simpler + if (isCloud && (selectedAction.app_name === "Shuffle Tools" || selectedAction.app_name === "email") && (selectedAction.name === "send_email_shuffle" || selectedAction.name === "send_sms_shuffle") && data.name === "apikey") { + if (selectedActionParameters[count].length === 0) { + selectedAction.parameters[count].value = "TMP: Will be replaced during execution if cloud" + setSelectedAction(selectedAction) + } - return null - } + return null + } var staticcolor = "inherit"; var actioncolor = "inherit"; @@ -1558,6 +1558,17 @@ const ParsedAction = (props) => { */ } + if (selectedAction.name === "custom_action" && data.name === "body") { + for (var key in selectedActionParameters) { + const param = selectedActionParameters[key] + if (param.name === "method") { + if (param.value === "GET") { + return null + } + } + } + } + if (data.name.startsWith("${") && data.name.endsWith("}")) { const paramcheck = selectedAction.parameters.find((param) => param.name === "body"); @@ -2678,8 +2689,6 @@ const ParsedAction = (props) => { /*
*/ } - //console.log(data.configuration) - const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}` const hasAutocomplete = data.autocompleted === true return ( @@ -2840,8 +2849,8 @@ const ParsedAction = (props) => { setUpdate(Math.random()); }} onClick={() => { - setShowAutocomplete(true) - }} + setShowAutocomplete(true) + }} fullWidth open={showAutocomplete} style={{ @@ -3007,7 +3016,9 @@ const ParsedAction = (props) => { a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label")) ).sort(sortByCategoryLabel)) - var baselabel = selectedAction.label; + + const selectedAppIcon = selectedAction.large_image + var baselabel = selectedAction.label return (
@@ -3015,13 +3026,29 @@ const ParsedAction = (props) => {
-

- {( - selectedAction.app_name.charAt(0).toUpperCase() + - selectedAction.app_name.substring(1) - ).replaceAll("_", " ")} -

-
+
{ + //window.open("/apps/${selectedAction.app_id}", "_blank") + }} + > + + + +

+ {( + selectedAction.app_name.charAt(0).toUpperCase() + + selectedAction.app_name.substring(1) + ).replaceAll("_", " ")} +

+
+
{ const [showDismissed, setShowDismissed] = React.useState(false); const [showRead, setShowRead] = React.useState(false); const [appFramework, setAppFramework] = React.useState({}); - const [selectedWorkflow, setSelectedWorkflow] = React.useState(""); - const [selectedExecutionId, setSelectedExecutionId] = React.useState(""); + + const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT"); + const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT"); let navigate = useNavigate(); useEffect(() => { @@ -86,6 +87,44 @@ const Priorities = (props) => { }) } + const clearNotifications = () => { + // Don't really care about the logout + + toast("Clearing notifications") + fetch(`${globalUrl}/api/v1/notifications/clear`, { + credentials: "include", + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + // Reload the UI + const newNotifications = notifications.map((notification) => { + notification.read = true + return notification + }) + + setNotifications(newNotifications) + setShowRead(true) + } else { + toast("Failed dismissing notifications. Please try again later."); + } + }) + .catch((error) => { + console.log("error in notification dismissal: ", error); + //removeCookie("session_token", {path: "/"}) + }); + }; + const dismissNotification = (alert_id, disabled) => { var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread` if (disabled === true) { @@ -178,7 +217,7 @@ const Priorities = (props) => { var orgId = ""; - const highlighted = data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow) + const highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow) if (userdata.orgs !== undefined) { const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]); @@ -351,12 +390,27 @@ const Priorities = (props) => {
- { - setShowRead(!showRead); - }} - />  Show read +
+ { + setShowRead(!showRead); + }} + />  Show read + {notifications !== undefined && notifications !== null && notifications.length > 1 ? ( + + ) : null} +
{notifications === null || notifications === undefined || notifications.length === 0 ? null :
{notifications.map((notification, index) => { diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 54261718..dc5f9179 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -23,6 +23,7 @@ import { DialogTitle, DialogContent, } from '@mui/material'; + import Mousetrap from 'mousetrap'; import { @@ -31,7 +32,6 @@ import { import { Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material' import KeyboardCommandKeyIcon from '@mui/icons-material/KeyboardCommandKey'; -import algoliasearch from 'algoliasearch/lite'; import aa from 'search-insights' import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; //import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; @@ -42,7 +42,6 @@ const chipStyle = { backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white", } -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const SearchField = props => { const { serverside, userdata, isMobile, isLoaded, globalUrl, isHeader, isLoggedIn, small, rounded } = props diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index 6cb8a814..d36c8e8d 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -48,8 +48,8 @@ const data = [ css: { label: "data(label)", shape: "roundrectangle", - "height": "16px", - "width": "120px", + "height": "18px", + "width": "145px", "background-color": "#212121", "border-color": "#81c784", "z-index": 10000, diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 5c240f4b..6e66e28c 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -34,7 +34,7 @@ const theme = createTheme(adaptV4Theme({ //jsonTheme: "tomorrow", jsonIconStyle: "round", jsonTheme: "summerfruit", - jsonCollapseStringsAfterLength: 75, + jsonCollapseStringsAfterLength: 100, reactJsonStyle: { padding: 5, @@ -65,7 +65,7 @@ const theme = createTheme(adaptV4Theme({ defaultImage: "/images/no_image.png", }, typography: { - fontFamily: `"Roboto", "Helvetica", "Arial", sans-serif`, + fontFamily: `"Roboto", "Helvetica", "Arial", "inter", sans-serif`, useNextVariants: true, h1: { fontSize: 40, diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 0d47a9de..13a9323d 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1430,6 +1430,8 @@ If you're interested, please let me know a time that works for you, or set up a } // Just use this one? + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { method: "GET", @@ -1440,7 +1442,11 @@ If you're interested, please let me know a time that works for you, or set up a }) .then((response) => { if (response.status === 401) { - } + } else { + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") + } return response.json(); }) @@ -1728,7 +1734,7 @@ If you're interested, please let me know a time that works for you, or set up a // Horrible frontend fix for environments const setDefaultEnvironment = (environment) => { // FIXME - add more checks to this - toast("Setting default env to " + environment.name); + toast("Changing default env") var newEnv = []; for (var key in environments) { if (environments[key].id == environment.id) { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index a9c2f825..10d76509 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -9,7 +9,7 @@ import { makeStyles, } from "@mui/styles"; import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx" import { v4 as uuidv4 } from "uuid"; import { useNavigate, Link, useParams } from "react-router-dom"; -import { useBeforeunload } from "react-beforeunload"; +import { useBeforeunload } from "react-beforeunload" import ReactJson from "react-json-view"; import { NestedMenuItem } from 'mui-nested-menu'; import Markdown from "react-markdown"; @@ -642,6 +642,72 @@ const AngularWorkflow = (defaultprops) => { "field_id": "", }) + const [loadedApps, setLoadedApps] = React.useState([]) + + const loadAppConfig = (appId, select) => { + if (appId === undefined || appId === null || appId.length === 0) { + return + } + + if (loadedApps.includes(appId)) { + return + } + + loadedApps.push(appId) + setLoadedApps(loadedApps) + + const appUrl = `${globalUrl}/api/v1/apps/${appId}/config?openapi=false` + fetch(appUrl, { + headers: { + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + 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) { + + if (select) { + setSelectedApp(foundapp) + } + + if (apps === undefined || apps === null || apps.length === 0) { + console.log("LOAD APPS!") + } + + for (var i = 0; i < apps.length; i++) { + if (apps[i].id !== foundapp.id) { + continue + } + + apps[i] = foundapp + setApps(apps) + setFilteredApps(apps) + + // Update the local storage + localStorage.setItem("apps", JSON.stringify(apps)) + break + } + } + + // FIXME: Add it to the existing list AND update the selected app + } + }) + .catch((error) => { + console.log(`Failed side-loading app ${appId}: ${error}`) + }) + } + // Event for making sure app is correct useEffect(() => { if (selectedApp === undefined || selectedApp === null && selectedApp.app_name === undefined) { @@ -657,45 +723,7 @@ const AngularWorkflow = (defaultprops) => { return } else { if (selectedApp.id !== undefined && selectedApp.id !== null && selectedApp.id.length > 0) { - const appUrl = `${globalUrl}/api/v1/apps/${selectedApp.id}/config?openapi=false` - fetch(appUrl, { - headers: { - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - return response.json() - }) - .then((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)) - if (foundapp.actions !== undefined && foundapp.actions !== null && foundapp.actions.length > selectedApp.actions.length) { - setSelectedApp(foundapp) - - for (var i = 0; i < apps.length; i++) { - if (apps[i].id !== foundapp.id) { - continue - } - - apps[i] = foundapp - setApps(apps) - setFilteredApps(apps) - - // Update the local storage - localStorage.setItem("apps", JSON.stringify(apps)) - break - } - } - - // FIXME: Add it to the existing list AND update the selected app - } - }) - .catch((error) => { - console.log(`Failed side-loading app ${selectedApp.name}`) - }) - + loadAppConfig(selectedApp.id, true) } } @@ -705,7 +733,6 @@ const AngularWorkflow = (defaultprops) => { continue } - console.log("Found app: ", curapp) if (curapp.actions !== undefined && curapp.actions !== null && curapp.actions.length > selectedApp.actions.length) { var foundActionIndex = -1 for (let actionkey in curapp.actions) { @@ -1128,6 +1155,8 @@ const AngularWorkflow = (defaultprops) => { "autoClose": false, }) + + } else { if (refresh === true) { getAppAuthentication(true, true, true) @@ -1138,6 +1167,13 @@ const AngularWorkflow = (defaultprops) => { setAuthenticationModalOpen(false) // Needs a refresh with the new authentication.. //toast("Successfully saved new app auth") + if (configureWorkflowModalOpen === true) { + setConfigureWorkflowModalOpen(false) + + setTimeout(() => { + setConfigureWorkflowModalOpen(true) + }, 1000) + } } }) .catch((error) => { @@ -1179,7 +1215,6 @@ const AngularWorkflow = (defaultprops) => { if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { // Don't clean up if it's already open if (executionModalOpen === true) { - console.log("Execution modal already open, not cleaning up") return } @@ -3221,8 +3256,9 @@ const AngularWorkflow = (defaultprops) => { responseJson.errors.length > 0 ) { console.log("Setting configure Modal to open") - setConfigureWorkflowModalOpen(true); } + + setConfigureWorkflowModalOpen(true) } } }) @@ -3777,8 +3813,21 @@ const AngularWorkflow = (defaultprops) => { if (data.buttonType == "ACTIONSUGGESTION") { const attachedToId = data.attachedTo - const parentitem = cy.getElementById(data.attachedTo).data() + const parentitemRaw = cy.getElementById(data.attachedTo) + const parentitem = parentitemRaw.data() if (parentitem !== null && parentitem !== undefined) { + setTimeout(() => { + parentitemRaw.select() + + const allNodes = cy.nodes().jsons() + for (var _key in allNodes) { + const currentNode = allNodes[_key] + + if (currentNode.data.buttonType === "ACTIONSUGGESTION") { + cy.getElementById(currentNode.data.id).remove() + } + } + }, 100) const findaction = data.label console.log("CLICKED: ", findaction, apps.length) @@ -4262,20 +4311,31 @@ const AngularWorkflow = (defaultprops) => { curaction.app_id = curapp.id - setAuthenticationType( - curapp.authentication.type === "oauth2-app" || (curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null) ? { - type: curapp.authentication.type, - redirect_uri: curapp.authentication.redirect_uri, - refresh_uri: curapp.authentication.refresh_uri, - token_uri: curapp.authentication.token_uri, - scope: curapp.authentication.scope, - client_id: curapp.authentication.client_id, - client_secret: curapp.authentication.client_secret, - grant_type: curapp.authentication.grant_type, - } : { - type: "", - } - ) + if (curapp.authentication === undefined || curapp.authentication === null) { + setAuthenticationType({ + type: "", + }) + + curapp.authentication = { + type: "", + required: false, + } + } else { + setAuthenticationType( + curapp.authentication.type === "oauth2-app" || (curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null) ? { + type: curapp.authentication.type, + redirect_uri: curapp.authentication.redirect_uri, + refresh_uri: curapp.authentication.refresh_uri, + token_uri: curapp.authentication.token_uri, + scope: curapp.authentication.scope, + client_id: curapp.authentication.client_id, + client_secret: curapp.authentication.client_secret, + grant_type: curapp.authentication.grant_type, + } : { + type: "", + } + ) + } const requiresAuth = curapp.authentication.required; //&& ((curapp.authentication.parameters !== undefined && curapp.authentication.parameters !== null) || (curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null)) setRequiresAuthentication(requiresAuth); @@ -5855,15 +5915,15 @@ const AngularWorkflow = (defaultprops) => { }; const addActionSuggestions = (nodedata, event) => { - console.log("App Action suggestions disabled for now") - return - + console.log("App Action suggestions being added") if (nodedata.type !== "ACTION") { return } - var parentNode = cy.$("#" + event.target.data("id")); - if (parentNode.data("isButton") || parentNode.data("buttonId")) return; + var parentNode = cy.$("#" + event.target.data("id")) + if (parentNode.data("isButton") || parentNode.data("buttonId")) { + return + } const px = parentNode.position("x") + 0; const py = parentNode.position("y") + 100; @@ -5887,6 +5947,8 @@ const AngularWorkflow = (defaultprops) => { // 1. Find the app // 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 for (let appKey in apps) { const curapp = apps[appKey] @@ -5898,6 +5960,7 @@ 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] @@ -5907,6 +5970,7 @@ const AngularWorkflow = (defaultprops) => { } if (curaction.category_label !== undefined && curaction.category_label !== null && curaction.category_label.length > 0) { + console.log("IN NODE ADD") cy.add({ group: "nodes", @@ -5925,7 +5989,7 @@ const AngularWorkflow = (defaultprops) => { }) added += 1 - if (added >= 2) { + if (added >= 3) { break } } @@ -6182,7 +6246,6 @@ const AngularWorkflow = (defaultprops) => { cytoscapeElement.style.cursor = "pointer" } - sendStreamRequest({ "item": "node", "type": "hover", @@ -6268,7 +6331,8 @@ const AngularWorkflow = (defaultprops) => { for (var _key in allNodes) { const currentNode = allNodes[_key]; // console.log("CURRENT NODE: ", currentNode) - if ((currentNode.data.isButton || currentNode.data.isSuggestion) && currentNode.data.attachedTo !== nodedata.id) { + + if ((currentNode.data.buttonType === "ACTIONSUGGESTION" || currentNode.data.isButton || currentNode.data.isSuggestion) && currentNode.data.attachedTo !== nodedata.id) { cy.getElementById(currentNode.data.id).remove(); } @@ -6320,6 +6384,10 @@ const AngularWorkflow = (defaultprops) => { //"cursor": "pointer", } + if (nodedata.buttonType === "ACTIONSUGGESTION") { + parsedStyle["font-size"] = "18px" + } + const typeIds = cy.elements('node:selected').jsons(); for (var idkey in typeIds) { const item = typeIds[idkey] @@ -7149,6 +7217,7 @@ const AngularWorkflow = (defaultprops) => { cy.edgehandles({ handleNodes: (el) => { if (el.isNode() && + el.data("buttonType") != "ACTIONSUGGESTION" && !el.data("isButton") && !el.data("isDescriptor") && !el.data("isSuggestion") && @@ -8342,7 +8411,6 @@ const AngularWorkflow = (defaultprops) => { newAppStyle.borderLeft = `${pixelSize} solid ${yellow}`; } - return ( { @@ -8361,8 +8429,17 @@ const AngularWorkflow = (defaultprops) => { { - setHover(true); + onMouseOver={(e) => { + e.preventDefault() + e.stopPropagation() + + setHover(true) + + if (app.actions !== undefined && app.actions !== null && app.actions.length === 1) { + console.log("HOVERING: ", app.id) + loadAppConfig(app.id, false) + } + }} onMouseOut={() => { setHover(false); @@ -8784,6 +8861,7 @@ const AngularWorkflow = (defaultprops) => { const CustomSearchBox = connectSearchBox(SearchBox) const CustomAppHits = connectHits(AppHits) + var viewedApps = [] return (
@@ -8839,6 +8917,12 @@ const AngularWorkflow = (defaultprops) => { return null } + if (viewedApps.includes(app.id)) { + return null + } + + viewedApps.push(app.id) + var extraMessage = "" if (index == 2) { extraMessage =
@@ -14217,7 +14301,8 @@ const AngularWorkflow = (defaultprops) => { right: 0, left: isMobile ? 20 : leftBarSize + 20, top: isMobile ? 30 : appBarSize + 20, - }; + pointerEvents: "none", + } @@ -14235,11 +14320,16 @@ const AngularWorkflow = (defaultprops) => { return (
-
+
{ Workflows -

{workflow.name}

+

{workflow.name}

{isCorrectOrg ? null : @@ -14282,7 +14375,9 @@ const AngularWorkflow = (defaultprops) => { if (response.status !== 200) { console.log("Error in response"); } else { - localStorage.setItem("apps", []) + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") } return response.json(); @@ -15939,7 +16034,6 @@ const AngularWorkflow = (defaultprops) => { }; const handleReactJsonClipboard = (copy) => { - console.log("COPY: ", copy); const elementName = "copy_element_shuffle"; var copyText = document.getElementById(elementName); @@ -17270,10 +17364,9 @@ const AngularWorkflow = (defaultprops) => { ? "red" : yellow; - const validate = ! codeModalOpen? "" : validateJson(selectedResult.result.trim()); - + const validate = !codeModalOpen ? "" : validateJson(selectedResult.result.trim()) if (validate.valid && typeof validate.result === "string") { - validate.result = JSON.parse(validate.result); + validate.result = JSON.parse(validate.result) } const AppResultVariable = ({ data }) => { @@ -17381,7 +17474,6 @@ const AngularWorkflow = (defaultprops) => { if (stringbody.length > 1000) { return "Body looks to be big in a standard format. Consider using the 'To File' parameter to automatically make it into a file." } - } else { } } @@ -17405,10 +17497,19 @@ const AngularWorkflow = (defaultprops) => { return "Authorization failed (403). The API user most likely doesn't have the correct permissions. Check the body of the result for more information." } + if (result.status === 404) { + return "The URL, or content of the URL is incorrect. Check it and try again." + } + if (result.status === 400) { return "The queries or data sent to the API is most likely wrong (400). Check the body of the result for more information." } + if (result.status === 200 || result.status === 201 || result.status === 204) { + return "It looks like the result was successful! If it didn't work, make sure to check if the body you are sending was correct." + } + + // Validate and check for newlines if (result.success !== false) { @@ -17425,7 +17526,7 @@ const AngularWorkflow = (defaultprops) => { } } - return "" + //return "" } @@ -17447,13 +17548,14 @@ const AngularWorkflow = (defaultprops) => { return "Consider whether your Orborus environment can connect to a local IP or not." } + if (stringjson.includes("invalidurl")) { + // IF count of "http" is more than one, 1, it's prolly invalid + var additionalinfo = "" + if (stringjson.includes("http") && stringjson.match(/http/g).length > 1) { + additionalinfo = "You may be using multiple 'http' in the URL. " + } - if (stringjson.includes("connectionerror")) { - if (stringjson.includes("kms")) { - return "KMS authentication failed. Check your notifications for more details." - } - - return "Your URL is incorrect." + return "The URL is invalid. Change the URL to a valid one, and try again. "+additionalinfo } if (stringjson.includes("result too large to handle")) { @@ -17469,6 +17571,15 @@ const AngularWorkflow = (defaultprops) => { } + if (stringjson.includes("connectionerror")) { + if (stringjson.includes("kms")) { + return "KMS authentication failed. Check your notifications for more details." + } + + return "The URL is incorrect, or Shuffle can't reach it. Set up a Shuffle Environment in the same VLAN, or whitelist Shuffle's IPs." + } + + return "" } @@ -17706,7 +17817,7 @@ const AngularWorkflow = (defaultprops) => { {currentSuggestion.length > 0 ?
- Debug Info: {currentSuggestion} + Debug: {currentSuggestion}
:
@@ -18381,9 +18492,14 @@ const AngularWorkflow = (defaultprops) => { selectedAction.authentication === undefined || selectedAction.authentication === null ) { - selectedAction.authentication = [authenticationOption]; + selectedAction.authentication = [authenticationOption] } else { - selectedAction.authentication.push(authenticationOption); + + try { + selectedAction.authentication.push(authenticationOption) + } catch (e) { + //console.log("Error: ", e) + } } setSelectedAction(selectedAction); @@ -19647,7 +19763,7 @@ const AngularWorkflow = (defaultprops) => { } } - const foundusecase.name === undefined || foundusecase.name === null || foundusecase.name === "" ? null : + const templatePopup = foundusecase.name === undefined || foundusecase.name === null || foundusecase.name === "" ? null :
{ />
- */ + */ const loadedCheck = isLoaded && workflowDone ? ( diff --git a/frontend/src/views/Usecases.jsx b/frontend/src/views/Usecases.jsx index c913ecc5..f131455d 100644 --- a/frontend/src/views/Usecases.jsx +++ b/frontend/src/views/Usecases.jsx @@ -427,7 +427,7 @@ const UsecaseListComponent = (props) => { return ( { - if (fixedName === "increase authentication") { + if (fixedName === "reporting") { getUsecase(subcase, index, subindex) return } @@ -1192,8 +1192,7 @@ const Dashboard = (props) => { navigate(curpath + newitem) } - /* - const baseItem = document.getElementById("increase authentication") + const baseItem = document.getElementById("reporting") if (baseItem !== undefined && baseItem !== null) { baseItem.click() @@ -1206,7 +1205,6 @@ const Dashboard = (props) => { // Scroll back to top window.scrollTo(0, 0) } - */ const foundQuery2 = params["selected_object"] if (foundQuery2 !== null && foundQuery2 !== undefined) { @@ -1231,7 +1229,7 @@ const Dashboard = (props) => { } else { //console.log("Couldn't find item with name ", queryName) } - }, 1000); + }, 1000) } } diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index e83dbd95..829c5ceb 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1186,7 +1186,7 @@ const Workflows = (props) => { } } - if (newarray.length > 0 && storageWorkflows.length <= newarray.length) { + if (newarray.length > 0) { try { localStorage.setItem("workflows", JSON.stringify(newarray)) } catch (e) {