diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index a7e0d4f8..415f1e94 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -97,6 +97,7 @@ def md5_base64(a): a = str(a) foundhash = hashlib.md5(a.encode('utf-8')).hexdigest() return base64.b64encode(foundhash.encode('utf-8')) + @shuffle_filters.register def base64_encode(a): @@ -107,6 +108,17 @@ def base64_encode(a): except: return base64.b64encode(a).decode() +@shuffle_filters.register +def random_element(a): + # Choose a random item from an array + a = list(a) + + if len(a) == 0: + return "" + + return random.choice(a) + + @shuffle_filters.register def base64_decode(a): a = str(a) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index f7d6227d..5f472183 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2011,6 +2011,13 @@ const releaseToConnectLabel = "Release to Connect" 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", @@ -2032,7 +2039,8 @@ const releaseToConnectLabel = "Release to Connect" return response.json(); }) .then((responseJson) => { - if (duplicationOrg !== undefined && duplicationOrg !== null && duplicationOrg.length > 0) { + if (useworkflow.id === originalWorkflow.id && duplicationOrg !== undefined && duplicationOrg !== null && duplicationOrg.length > 0) { + //duplicateParentWorkflow(useworkflow, duplicationOrg, true) duplicateParentWorkflow(useworkflow, duplicationOrg, true) } @@ -3233,15 +3241,19 @@ const releaseToConnectLabel = "Release to Connect" }; const getChildWorkflows = (parentWorkflowId) => { - if (workflow.suborg_distribution === undefined || workflow.suborg_distribution === null || workflow.suborg_distribution.length === 0) { + if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) { + console.log("No suborg distribution") return } + const orgId = originalWorkflow.org_id === undefined || originalWorkflow.org_id === null || originalWorkflow.org_id === "" ? "" : originalWorkflow.org_id + fetch(`${globalUrl}/api/v1/workflows/${parentWorkflowId}/child_workflows`, { method: "GET", headers: { "Content-Type": "application/json", - Accept: "application/json", + "Accept": "application/json", + "Org-Id": orgId, }, credentials: "include", }) @@ -3317,6 +3329,10 @@ const releaseToConnectLabel = "Release to Connect" getChildWorkflows(responseJson.id) } + 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) { responseJson.isValid = true; @@ -8523,6 +8539,525 @@ const releaseToConnectLabel = "Release to Connect" ) } + + + const TriggersView = () => { + const triggersViewStyle = { + marginLeft: 10, + marginRight: 10, + display: "flex", + flexDirection: "column", + } + + // Predefined hurr + return ( +
+
+ {triggers.map((trigger, index) => { + + /* + if (trigger.trigger_type === "PIPELINE") { + if (userdata.support !== true) { + return null + } + } + */ + + // Hiding since March 2024 + if (trigger.trigger_type === "EMAIL") { + return null + } + + const imagesize = isMobile ? 40 : trigger.large_image.includes("svg") ? 50 : 50 + var imageline = trigger.large_image.length === 0 ? + : + + + const title = trigger.trigger_type === "WEBHOOK" ? "Workflow starters" : trigger.trigger_type === "SUBFLOW" ? "Mid-Workflow" : "" + + const color = trigger.is_valid ? green : yellow; + return ( + + {title.length > 0 ? + + {title} + + : null} + + { + handleTriggerDrag(e, trigger); + }} + onStop={(e) => { + handleDragStop(e); + }} + dragging={false} + position={{ + x: 0, + y: 0, + }} + > + { }}> +
+ + + {imageline} + + {isMobile ? null : + + + + {trigger.name} + + + + + {trigger.description} + + + + } + +
+
+
+ ); + })} +
+
+ ); + }; + + var newNodeId = ""; + var parsedApp = {}; + const handleTriggerDrag = (e, data) => { + const cycontainer = cy.container(); + // Chrome lol + if ( + e.pageX > cycontainer.offsetLeft && + e.pageX < cycontainer.offsetLeft + cycontainer.offsetWidth && + e.pageY > cycontainer.offsetTop && + e.pageY < cycontainer.offsetTop + cycontainer.offsetHeight + ) { + if (newNodeId.length > 0) { + var currentnode = cy.getElementById(newNodeId); + if (currentnode.length === 0) { + return; + } + + currentnode[0].renderedPosition("x", e.pageX - cycontainer.offsetLeft); + currentnode[0].renderedPosition("y", e.pageY - cycontainer.offsetTop); + } else { + if (workflow.start === "" || workflow.start === undefined) { + toast("Define a starting action first."); + return; + } + + const triggerLabel = getNextActionName(data.name); + + newNodeId = uuidv4(); + const newposition = { + x: e.pageX - cycontainer.offsetLeft, + y: e.pageY - cycontainer.offsetTop, + }; + + const newAppData = { + app_name: data.name, + app_version: "1.0.0", + environment: isCloud ? "cloud" : data.environment, + description: data.description, + long_description: data.long_description, + errors: [], + id_: newNodeId, + _id_: newNodeId, + id: newNodeId, + finished: false, + label: triggerLabel, + type: data.type, + is_valid: true, + trigger_type: data.trigger_type, + large_image: data.large_image, + status: "uninitialized", + name: data.name, + isStartNode: false, + position: newposition, + }; + + // Can all the data be in here? hmm + const nodeToBeAdded = { + group: "nodes", + data: newAppData, + renderedPosition: newposition, + }; + + cy.add(nodeToBeAdded); + parsedApp = nodeToBeAdded; + return; + } + } + }; + + const handleDragStop = (e, app) => { + var currentnode = cy.getElementById(newNodeId); + if ( + currentnode === undefined || + currentnode === null || + currentnode.length === 0 + ) { + return; + } + + if (parsedApp === undefined || parsedApp === null || parsedApp.data === undefined || parsedApp.data === null) { + toast("Failed to add trigger. Please try again.") + return + } + + // Using remove & replace, as this triggers the function + // onNodeAdded() with this node after it's added + + currentnode.remove(); + parsedApp.data.finished = true; + parsedApp.data.position = currentnode.renderedPosition(); + parsedApp.position = currentnode.renderedPosition(); + parsedApp.renderedPosition = currentnode.renderedPosition(); + + var newAppData = parsedApp.data; + if (newAppData.type === "ACTION") { + + //const activateApp = (appid) => { + if (newAppData.activated === false) { + activateApp(newAppData.app_id, false) + } + + // AUTHENTICATION + if (app.authentication !== undefined && app.authentication !== null && app.authentication.required === true) { + + // Setup auth here :) + const authenticationOptions = []; + var findAuthId = ""; + if ( + newAppData.authentication_id !== null && + newAppData.authentication_id !== undefined && + newAppData.authentication_id.length > 0 + ) { + findAuthId = newAppData.authentication_id; + } + + const tmpAuth = JSON.parse(JSON.stringify(appAuthentication)); + for (let authkey in tmpAuth) { + if (authkey === undefined) { + continue + } + + var item = tmpAuth[authkey]; + const newfields = {}; + for (let fieldkey in item.fields) { + if (item.fields[fieldkey] === undefined) { + console.log("Problem with filterkey in Node select", fieldkey) + continue + } + + const filterkey = item.fields[fieldkey]["key"] + if (filterkey === null || filterkey === undefined) { + console.log("Problem with filterkey 2. Null or undefined 3") + continue + } + + newfields[filterkey] = item.fields[fieldkey]["value"]; + } + + item.fields = newfields; + if (item.app.id === app.id || item.app.name === app.name) { + authenticationOptions.push(item); + + if (item.id === findAuthId) { + newAppData.selectedAuthentication = item + newAppData.authentication_id = item.id + + } else if (findAuthId === "") { + // Will always be set to the last one if one isn't found. + // Last = timestamp too + newAppData.selectedAuthentication = item + newAppData.authentication_id = item.id + } + } + } + + if ( + authenticationOptions !== undefined && + authenticationOptions !== null && + authenticationOptions.length > 0 + ) { + for (let authkey in authenticationOptions) { + const option = authenticationOptions[authkey]; + + if (option.active && newAppData.authentication_id === "") { + newAppData.selectedAuthentication = option; + newAppData.authentication_id = option.id; + break; + } + } + } + } else { + newAppData.authentication = []; + newAppData.authentication_id = ""; + newAppData.selectedAuthentication = {}; + } + + parsedApp.data = newAppData; + cy.add(parsedApp); + } else if (newAppData.type === "TRIGGER") { + cy.add(parsedApp); + } + + newNodeId = ""; + parsedApp = {}; + }; + + const barHeight = bodyHeight - appBarSize - 50; + const appScrollStyle = { + overflow: "scroll", + maxHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight, + minHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight, + marginTop: 1, + overflowY: "auto", + overflowX: "hidden", + } + + const handleAppDrag = (e, app) => { + const cycontainer = cy.container(); + + + if (app.type === "TRIGGER") { + handleTriggerDrag(e, app) + return + } + + //console.log("e: ", e) + //console.log("Offset: ", cycontainer) + + // Chrome lol + if ( + e.pageX > cycontainer.offsetLeft && + e.pageX < cycontainer.offsetLeft + cycontainer.offsetWidth && + e.pageY > cycontainer.offsetTop && + e.pageY < cycontainer.offsetTop + cycontainer.offsetHeight + ) { + if (newNodeId.length > 0) { + var currentnode = cy.getElementById(newNodeId); + if ( + currentnode === undefined || + currentnode === null || + currentnode.length === 0 + ) { + return; + } + + currentnode[0].renderedPosition("x", e.pageX - cycontainer.offsetLeft) + currentnode[0].renderedPosition("y", e.pageY - cycontainer.offsetTop) + } else { + if (workflow.public) { + console.log("workflow is public - not adding") + return; + } + + if (app.actions === undefined || app.actions === null) { + app.actions = [] + } + + /* + if (app.actions === undefined || app.actions === null || app.actions.length === 0) { + toast("App " + app.name + " currently has no actions to perform. Please go to https://shuffler.io/apps to edit it.") + + return + } + */ + + newNodeId = uuidv4(); + const actionType = "ACTION"; + const actionLabel = getNextActionName(app.name); + //console.log("Next action name: ", actionLabel) + var parameters = null; + var example = ""; + var description = "" + + const startIndex = app.actions.findIndex((action) => action.category_label !== undefined && action.category_label !== null && action.category_label.length > 0) + const actionIndex = startIndex < 0 ? 0 : startIndex + + // Make the first action the most relevant one for them based on previous use + if ( + app.actions[actionIndex].parameters !== undefined && + app.actions[actionIndex].parameters !== null && + app.actions[actionIndex].parameters.length > 0 + ) { + parameters = app.actions[actionIndex].parameters; + for (let paramkey in parameters) { + // Check if parameter.name == "headers" and if it includes "=undefined". If it does, set the value to example if it exists, otherwise empty + if (parameters[paramkey].name === "headers" && parameters[paramkey].value.includes("=undefined")) { + if (parameters[paramkey].example !== undefined && parameters[paramkey].example !== null && parameters[paramkey].example.length > 0) { + parameters[paramkey].value = parameters[paramkey].example + } else { + parameters[paramkey].value = "" + } + } + } + + //parameters = app.actions[0].parameters; + } + + if ( + app.actions[actionIndex].returns !== undefined && + app.actions[actionIndex].returns !== null && + app.actions[actionIndex].returns.example !== undefined && + app.actions[actionIndex].returns.example !== null && + app.actions[actionIndex].returns.example.length > 0 + ) { + example = app.actions[actionIndex].returns.example; + } + + if ( + app.actions[actionIndex].description !== undefined && + app.actions[actionIndex].description !== null && + app.actions[actionIndex].description.length > 0 + ) { + description = app.actions[actionIndex].description + } + + var parsedEnvironments = + environments === null || environments === [] + ? "cloud" + : environments[defaultEnvironmentIndex] === undefined + ? "cloud" + : environments[defaultEnvironmentIndex].Name + + // Basic automatic auth mapping + var authId = "" + if (appAuthentication !== undefined && appAuthentication !== null && appAuthentication.length > 0) { + const appname = app.name.toLowerCase().replace(" ", "_") + for (var key in appAuthentication) { + const authKey = appAuthentication[key] + if (authKey.app.id === app.id) { + authId = authKey.id + break + } + + const appauthname = authKey.app.name.toLowerCase().replace(" ", "_") + if (appauthname === appname) { + authId = authKey.id + } + } + } + + // List other nodes in the workflow and see if they have an environment set. If they do, use that as the default + if (cy !== undefined && cy !== null) { + const foundnodes = cy.nodes().jsons() + if (foundnodes !== undefined && foundnodes !== null && foundnodes.length > 0) { + // As they should all be the same, this is just an override + for (let nodekey in foundnodes) { + const curnode = foundnodes[nodekey] + if (curnode.data.environment !== undefined && curnode.data.environment !== null && curnode.data.environment.length > 0) { + parsedEnvironments = curnode.data.environment + break + } + } + } + } + + const newAppData = { + 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, + category: + app.categories !== null && + app.categories !== undefined && + app.categories.length > 0 + ? app.categories[0] + : "", + authentication_id: authId, + finished: false, + template: app.template === true ? true : false, + }; + + // FIXME: overwrite category if the ACTION chosen has a different category + // + + if (!isCloud && (!app.is_valid || (!app.activated && app.generated))) { + console.log("NOT VALID: Activate!") + + activateApp(app.id, false) + } + + // const image = "url("+app.large_image+")" + // FIXME - find the cytoscape offset position + // Can this be done with zoom calculations? + const nodeToBeAdded = { + group: "nodes", + data: newAppData, + renderedPosition: { + x: e.pageX - cycontainer.offsetLeft, + y: e.pageY - cycontainer.offsetTop, + }, + }; + + parsedApp = nodeToBeAdded; + cy.add(nodeToBeAdded); + return; + } + } + }; + const AppView = (props) => { const { allApps, prioritizedApps, filteredApps, extraApps } = props; // console.log("AppView Rendered!") @@ -9181,501 +9716,6 @@ const releaseToConnectLabel = "Release to Connect" ); } - const TriggersView = () => { - console.log("TriggerView Rendered!") - const triggersViewStyle = { - marginLeft: 10, - marginRight: 10, - display: "flex", - flexDirection: "column", - } - - // Predefined hurr - return ( -
-
- {triggers.map((trigger, index) => { - - /* - if (trigger.trigger_type === "PIPELINE") { - if (userdata.support !== true) { - return null - } - } - */ - - // Hiding since March 2024 - if (trigger.trigger_type === "EMAIL") { - return null - } - - const imagesize = isMobile ? 40 : trigger.large_image.includes("svg") ? 50 : 50 - var imageline = trigger.large_image.length === 0 ? - : - - - const title = trigger.trigger_type === "WEBHOOK" ? "Workflow starters" : trigger.trigger_type === "SUBFLOW" ? "Mid-Workflow" : "" - - const color = trigger.is_valid ? green : yellow; - return ( - - {title.length > 0 ? - - {title} - - : null} - - { - handleTriggerDrag(e, trigger); - }} - onStop={(e) => { - handleDragStop(e); - }} - dragging={false} - position={{ - x: 0, - y: 0, - }} - > - { }}> -
- - - {imageline} - - {isMobile ? null : - - - - {trigger.name} - - - - - {trigger.description} - - - - } - -
-
-
- ); - })} -
-
- ); - } - - var newNodeId = ""; - var parsedApp = {}; - const handleTriggerDrag = (e, data) => { - const cycontainer = cy.container(); - // Chrome lol - if ( - e.pageX > cycontainer.offsetLeft && - e.pageX < cycontainer.offsetLeft + cycontainer.offsetWidth && - e.pageY > cycontainer.offsetTop && - e.pageY < cycontainer.offsetTop + cycontainer.offsetHeight - ) { - if (newNodeId.length > 0) { - var currentnode = cy.getElementById(newNodeId); - if (currentnode.length === 0) { - return; - } - - currentnode[0].renderedPosition("x", e.pageX - cycontainer.offsetLeft); - currentnode[0].renderedPosition("y", e.pageY - cycontainer.offsetTop); - } else { - if (workflow.start === "" || workflow.start === undefined) { - toast("Define a starting action first."); - return; - } - - const triggerLabel = getNextActionName(data.name); - - newNodeId = uuidv4(); - const newposition = { - x: e.pageX - cycontainer.offsetLeft, - y: e.pageY - cycontainer.offsetTop, - }; - - const newAppData = { - app_name: data.name, - app_version: "1.0.0", - environment: isCloud ? "cloud" : data.environment, - description: data.description, - long_description: data.long_description, - errors: [], - id_: newNodeId, - _id_: newNodeId, - id: newNodeId, - finished: false, - label: triggerLabel, - type: data.type, - is_valid: true, - trigger_type: data.trigger_type, - large_image: data.large_image, - status: "uninitialized", - name: data.name, - isStartNode: false, - position: newposition, - }; - - // Can all the data be in here? hmm - const nodeToBeAdded = { - group: "nodes", - data: newAppData, - renderedPosition: newposition, - }; - - cy.add(nodeToBeAdded); - parsedApp = nodeToBeAdded; - return; - } - } - }; - - const handleDragStop = (e, app) => { - var currentnode = cy.getElementById(newNodeId); - if ( - currentnode === undefined || - currentnode === null || - currentnode.length === 0 - ) { - return; - } - - // Using remove & replace, as this triggers the function - // onNodeAdded() with this node after it's added - - currentnode.remove(); - parsedApp.data.finished = true; - parsedApp.data.position = currentnode.renderedPosition(); - parsedApp.position = currentnode.renderedPosition(); - parsedApp.renderedPosition = currentnode.renderedPosition(); - - var newAppData = parsedApp.data; - if (newAppData.type === "ACTION") { - - //const activateApp = (appid) => { - if (newAppData.activated === false) { - activateApp(newAppData.app_id, false) - } - - // AUTHENTICATION - if (app.authentication !== undefined && app.authentication !== null && app.authentication.required === true) { - - // Setup auth here :) - const authenticationOptions = []; - var findAuthId = ""; - if ( - newAppData.authentication_id !== null && - newAppData.authentication_id !== undefined && - newAppData.authentication_id.length > 0 - ) { - findAuthId = newAppData.authentication_id; - } - - const tmpAuth = JSON.parse(JSON.stringify(appAuthentication)); - for (let authkey in tmpAuth) { - if (authkey === undefined) { - continue - } - - var item = tmpAuth[authkey]; - const newfields = {}; - for (let fieldkey in item.fields) { - if (item.fields[fieldkey] === undefined) { - console.log("Problem with filterkey in Node select", fieldkey) - continue - } - - const filterkey = item.fields[fieldkey]["key"] - if (filterkey === null || filterkey === undefined) { - console.log("Problem with filterkey 2. Null or undefined 3") - continue - } - - newfields[filterkey] = item.fields[fieldkey]["value"]; - } - - item.fields = newfields; - if (item.app.id === app.id || item.app.name === app.name) { - authenticationOptions.push(item); - - if (item.id === findAuthId) { - newAppData.selectedAuthentication = item - newAppData.authentication_id = item.id - - } else if (findAuthId === "") { - // Will always be set to the last one if one isn't found. - // Last = timestamp too - newAppData.selectedAuthentication = item - newAppData.authentication_id = item.id - } - } - } - - if ( - authenticationOptions !== undefined && - authenticationOptions !== null && - authenticationOptions.length > 0 - ) { - for (let authkey in authenticationOptions) { - const option = authenticationOptions[authkey]; - - if (option.active && newAppData.authentication_id === "") { - newAppData.selectedAuthentication = option; - newAppData.authentication_id = option.id; - break; - } - } - } - } else { - newAppData.authentication = []; - newAppData.authentication_id = ""; - newAppData.selectedAuthentication = {}; - } - - parsedApp.data = newAppData; - cy.add(parsedApp); - } else if (newAppData.type === "TRIGGER") { - cy.add(parsedApp); - } - - newNodeId = ""; - parsedApp = {}; - }; - - const barHeight = bodyHeight - appBarSize - 50; - const appScrollStyle = { - overflow: "scroll", - maxHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight, - minHeight: isMobile ? bodyHeight - appBarSize * 4 : barHeight, - marginTop: 1, - overflowY: "auto", - overflowX: "hidden", - } - - const handleAppDrag = (e, app) => { - const cycontainer = cy.container(); - - - if (app.type === "TRIGGER") { - handleTriggerDrag(e, app) - return - } - - //console.log("e: ", e) - //console.log("Offset: ", cycontainer) - - // Chrome lol - if ( - e.pageX > cycontainer.offsetLeft && - e.pageX < cycontainer.offsetLeft + cycontainer.offsetWidth && - e.pageY > cycontainer.offsetTop && - e.pageY < cycontainer.offsetTop + cycontainer.offsetHeight - ) { - if (newNodeId.length > 0) { - var currentnode = cy.getElementById(newNodeId); - if ( - currentnode === undefined || - currentnode === null || - currentnode.length === 0 - ) { - return; - } - - currentnode[0].renderedPosition("x", e.pageX - cycontainer.offsetLeft) - currentnode[0].renderedPosition("y", e.pageY - cycontainer.offsetTop) - } else { - if (workflow.public) { - console.log("workflow is public - not adding") - return; - } - - if (app.actions === undefined || app.actions === null) { - app.actions = [] - } - - /* - if (app.actions === undefined || app.actions === null || app.actions.length === 0) { - toast("App " + app.name + " currently has no actions to perform. Please go to https://shuffler.io/apps to edit it.") - - return - } - */ - - newNodeId = uuidv4(); - const actionType = "ACTION"; - const actionLabel = getNextActionName(app.name); - //console.log("Next action name: ", actionLabel) - var parameters = null; - var example = ""; - var description = "" - - const startIndex = app.actions.findIndex((action) => action.category_label !== undefined && action.category_label !== null && action.category_label.length > 0) - const actionIndex = startIndex < 0 ? 0 : startIndex - - // Make the first action the most relevant one for them based on previous use - if ( - app.actions[actionIndex].parameters !== undefined && - app.actions[actionIndex].parameters !== null && - app.actions[actionIndex].parameters.length > 0 - ) { - parameters = app.actions[actionIndex].parameters; - for (let paramkey in parameters) { - // Check if parameter.name == "headers" and if it includes "=undefined". If it does, set the value to example if it exists, otherwise empty - if (parameters[paramkey].name === "headers" && parameters[paramkey].value.includes("=undefined")) { - if (parameters[paramkey].example !== undefined && parameters[paramkey].example !== null && parameters[paramkey].example.length > 0) { - parameters[paramkey].value = parameters[paramkey].example - } else { - parameters[paramkey].value = "" - } - } - } - - //parameters = app.actions[0].parameters; - } - - if ( - app.actions[actionIndex].returns !== undefined && - app.actions[actionIndex].returns !== null && - app.actions[actionIndex].returns.example !== undefined && - app.actions[actionIndex].returns.example !== null && - app.actions[actionIndex].returns.example.length > 0 - ) { - example = app.actions[actionIndex].returns.example; - } - - if ( - app.actions[actionIndex].description !== undefined && - app.actions[actionIndex].description !== null && - app.actions[actionIndex].description.length > 0 - ) { - description = app.actions[actionIndex].description - } - - var parsedEnvironments = - environments === null || environments === [] - ? "cloud" - : environments[defaultEnvironmentIndex] === undefined - ? "cloud" - : environments[defaultEnvironmentIndex].Name; - - // List other nodes in the workflow and see if they have an environment set. If they do, use that as the default - if (cy !== undefined && cy !== null) { - const foundnodes = cy.nodes().jsons() - if (foundnodes !== undefined && foundnodes !== null && foundnodes.length > 0) { - // As they should all be the same, this is just an override - for (let nodekey in foundnodes) { - const curnode = foundnodes[nodekey] - if (curnode.data.environment !== undefined && curnode.data.environment !== null && curnode.data.environment.length > 0) { - parsedEnvironments = curnode.data.environment - break - } - } - } - } - - const newAppData = { - 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, - category: - app.categories !== null && - app.categories !== undefined && - app.categories.length > 0 - ? app.categories[0] - : "", - authentication_id: "", - finished: false, - template: app.template === true ? true : false, - }; - - // FIXME: overwrite category if the ACTION chosen has a different category - // - - if (!isCloud && (!app.is_valid || (!app.activated && app.generated))) { - console.log("NOT VALID: Activate!") - - activateApp(app.id, false) - } - - // const image = "url("+app.large_image+")" - // FIXME - find the cytoscape offset position - // Can this be done with zoom calculations? - const nodeToBeAdded = { - group: "nodes", - data: newAppData, - renderedPosition: { - x: e.pageX - cycontainer.offsetLeft, - y: e.pageY - cycontainer.offsetTop, - }, - }; - - parsedApp = nodeToBeAdded; - cy.add(nodeToBeAdded); - return; - } - } - }; const getNextActionName = (appName) => { @@ -13601,7 +13641,7 @@ const releaseToConnectLabel = "Release to Connect" // Special SCHEDULE handler var trigger_header_auth = "" - if (Object.getOwnPropertyNames(selectedTrigger).length > 0 && workflow.triggers[selectedTriggerIndex] !== undefined ) { + if (Object.getOwnPropertyNames(selectedTrigger).length > 0 && workflow.triggers !== null && workflow.triggers !== undefined && workflow.triggers.length >= selectedTriggerIndex && workflow.triggers[selectedTriggerIndex] !== undefined ) { if (selectedTrigger.trigger_type === "SCHEDULE" && workflow.triggers[selectedTriggerIndex].parameters === undefined || workflow.triggers[selectedTriggerIndex].parameters === null) { console.log("Autofixing schedule") @@ -15495,34 +15535,49 @@ const releaseToConnectLabel = "Release to Connect" View Suborg workflow