From 2c9bac9eb04b18e1bbd3b7085c4db0f4316fec98 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 2 Jun 2021 15:09:28 +0200 Subject: [PATCH] Fixed clipboard issues - requires HTTPS or localhost --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 2 +- backend/go-app/walkoff.go | 83 +++++ docker-compose.yml | 5 +- frontend/package.json | 8 +- frontend/src/views/Admin.jsx | 24 +- frontend/src/views/AngularWorkflow.jsx | 36 ++- frontend/src/views/AppCreator.jsx | 407 ++++++++++++++++++++++++- 8 files changed, 552 insertions(+), 15 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 74585b9b..569852b4 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle go 1.13 -//replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi diff --git a/backend/go-app/main.go b/backend/go-app/main.go index dd71a38f..bcf2abac 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5805,7 +5805,6 @@ func initHandlers() { r.HandleFunc("/api/v1/apps/authentication", shuffle.GetAppAuthentication).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication", shuffle.AddAppAuthentication).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", shuffle.SetAuthenticationConfig).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/apps/authentication/{appauthId}", shuffle.DeleteAppAuthentication).Methods("DELETE", "OPTIONS") // Legacy app things @@ -5863,6 +5862,7 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/validate_app_values", shuffle.HandleKeyValueCheck).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/get_cache", shuffle.HandleGetCacheKey).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/set_cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/apps/{key}/execute", executeSingleAction).Methods("POST", "OPTIONS") // Docker orborus specific - downloads an image r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 42623468..a0614772 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -4385,3 +4385,86 @@ func handleUserInput(trigger shuffle.Trigger, organizationId string, workflowId return nil } + +func executeSingleAction(resp http.ResponseWriter, request *http.Request) { + cors := shuffle.HandleCors(resp, request) + if cors { + return + } + + user, err := shuffle.HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("[WARNING] Api authentication failed in execute SINGLE workflow - CONTINUING ANYWAY: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "You need to sign up to try it out}`)) + return + } + + location := strings.Split(request.URL.String(), "/") + var fileId string + if location[1] == "api" { + if len(location) <= 4 { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + fileId = location[4] + } + + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("[INFO] Failed workflowrequest POST read: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + ctx := context.Background() + workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, fileId, body) + if err != nil { + log.Printf("[INFO] Failed workflowrequest POST read: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + //workflowExecution.ProjectId = gceProject + //workflowExecution.Locations = []string{defaultLocation} + + environments, err := shuffle.GetEnvironments(ctx, user.ActiveOrg.Id) + environment := "Shuffle" + if len(environments) >= 1 { + environment = environments[0].Name + } else { + log.Printf("[ERROR] No environments found for org %s. Exiting", user.ActiveOrg.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + log.Printf("[INFO] Execution: %s should execute onprem with execution environment \"%s\". Workflow: %s", workflowExecution.ExecutionId, environment, workflowExecution.Workflow.ID) + + executionRequest := shuffle.ExecutionRequest{ + ExecutionId: workflowExecution.ExecutionId, + WorkflowId: workflowExecution.Workflow.ID, + Authorization: workflowExecution.Authorization, + Environments: []string{environment}, + } + + err = shuffle.SetWorkflowQueue(ctx, executionRequest, environment) + if err != nil { + log.Printf("[ERROR] Failed adding execution to db: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + time.Sleep(2 * time.Second) + log.Printf("[INFO] Starting validation of execution %s", workflowExecution.ExecutionId) + + returnBytes := shuffle.HandleRetValidation(ctx, workflowExecution) + + resp.WriteHeader(200) + resp.Write(returnBytes) +} diff --git a/docker-compose.yml b/docker-compose.yml index 7cc35878..6b863b6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,9 @@ services: - ${SHUFFLE_FILE_LOCATION}:/shuffle-files #- ${SHUFFLE_OPENSEARCH_CERTIFICATE_FILE}:/shuffle-files/es_certificate env_file: .env + environment: + - SHUFFLE_APP_HOTLOAD_FOLDER=/shuffle-apps + - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped depends_on: - opensearch @@ -46,7 +49,7 @@ services: - /var/run/docker.sock:/var/run/docker.sock environment: - SHUFFLE_APP_SDK_VERSION=0.8.97 - - SHUFFLE_WORKER_VERSION=nightly + - SHUFFLE_WORKER_VERSION=0.8.97 - ORG_ID=${ORG_ID} - ENVIRONMENT_NAME=${ENVIRONMENT_NAME} - BASE_URL=http://${OUTER_HOSTNAME}:${BACKEND_PORT} diff --git a/frontend/package.json b/frontend/package.json index 7f872271..5b290522 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,6 @@ "version": "0.8.92", "private": true, "dependencies": { - "@babel/helper-regex": "^7.10.5", "@material-ui/core": "^4.5.2", "@material-ui/data-grid": "^4.0.0-alpha.22", "@material-ui/icons": "^4.5.1", @@ -12,17 +11,15 @@ "@material-ui/styles": "^4.5.2", "@use-it/interval": "^1.0.0", "babel-eslint": "^10.1.0", - "cache-base": "^4.0.0", "class-transformer": "^0.3.1", "create-react-app": "^2.0.3", - "cytoscape": "^3.19.0", + "cytoscape": "^3.11.0", "cytoscape-clipboard": "^2.2.1", "cytoscape-cxtmenu": "^3.1.1", "cytoscape-edgehandles": "^3.6.0", "cytoscape-grid-guide": "~2.1.2", "cytoscape-node-html-label": "^1.1.5", "cytoscape-panzoom": "^2.5.2", - "cytoscape-popper": "^2.0.0", "cytoscape-undo-redo": "^1.3.2", "d3": "~4.10.0", "dotenv": "^6.1.0", @@ -47,7 +44,7 @@ "react-cytoscapejs": "^1.2.0", "react-device-detect": "^1.9.10", "react-dom": "^16.14.0", - "react-draggable": "4.4.3", + "react-draggable": "^3.3.2", "react-dropzone": "^10.1.10", "react-ga": "^2.7.0", "react-iframe": "^1.8.0", @@ -59,7 +56,6 @@ "react-router": "^4.3.1", "react-router-dom": "^4.3.1", "react-scripts": "^4.0.1", - "react-scroll": "^1.8.2", "reactstrap": "^7.1.0", "shellwords": "^0.1.1", "simplebar": "^4.2.3", diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 7905e069..7d8a37b8 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1522,14 +1522,20 @@ const Admin = (props) => { const org_id = selectedOrganization.id var copyText = document.getElementById(elementName); if (copyText !== null && copyText !== undefined) { + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(org_id) copyText.select(); copyText.setSelectionRange(0, 99999); /* For mobile devices */ - /* Copy the text inside the text field */ - document.execCommand("copy"); + /* Copy the text inside the text field */ + document.execCommand("copy"); - alert.info(org_id + " copied to clipboard") + alert.info(org_id + " copied to clipboard") } }}> @@ -1910,6 +1916,12 @@ const Admin = (props) => { const elementName = "copy_element_shuffle" var copyText = document.getElementById(elementName); if (copyText !== null && copyText !== undefined) { + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(data.apikey) copyText.select(); copyText.setSelectionRange(0, 99999); /* For mobile devices */ @@ -2161,6 +2173,12 @@ const Admin = (props) => { const elementName = "copy_element_shuffle" var copyText = document.getElementById(elementName); if (copyText !== null && copyText !== undefined) { + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(file.id) copyText.select(); copyText.setSelectionRange(0, 99999); /* For mobile devices */ diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e27e04ef..5a7a8c79 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2228,7 +2228,7 @@ const AngularWorkflow = (props) => { } var parents = getParents(dstdata) - console.log("PARENTS: ", parents) + //console.log("PARENTS: ", parents) if (parents.length > 1) { for (var key in parents) { const item = parents[key] @@ -2272,8 +2272,9 @@ const AngularWorkflow = (props) => { // Checks for errors in edges when they're added const onEdgeAdded = (event) => { - setLastSaved(false) const edge = event.target.data() + console.log("EDGE ADDED: ", edge) + //setLastSaved(false) var targetnode = workflow.triggers.findIndex(data => data.id === edge.target) if (targetnode !== -1) { console.log("TARGETNODE: ", targetnode) @@ -2892,6 +2893,9 @@ const AngularWorkflow = (props) => { setEstablished(true) // Validate if the node is just a node lol + console.log("CY: ", cy) + //console.log("CY: ", cy.edgehandles()) + //try { cy.edgehandles({ handleNodes: (el) => el.isNode() && !el.data("isButton") && !el.data("isDescriptor"), preview: false, @@ -2900,6 +2904,9 @@ const AngularWorkflow = (props) => { return false; }, }) + //} catch (e) { + // console.log("Error in edgehandles: ", e) + //} cy.fit(null, 200) @@ -6276,6 +6283,12 @@ const AngularWorkflow = (props) => { var copyText = document.getElementById("webhook_uri_field") if (copyText !== undefined && copyText !== null) { console.log("NAVIGATOR: ", navigator) + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(copyText.value) copyText.select() copyText.setSelectionRange(0, 99999) /* For mobile devices */ @@ -7510,6 +7523,12 @@ const AngularWorkflow = (props) => { console.log("NEW: ", copy) console.log("NAVIGATOR: ", navigator) + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(JSON.stringify(copy)) copyText.select() copyText.setSelectionRange(0, 99999) /* For mobile devices */ @@ -7560,6 +7579,12 @@ const AngularWorkflow = (props) => { var copyText = document.getElementById(elementName) if (copyText !== null && copyText !== undefined) { console.log("NAVIGATOR: ", navigator) + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(to_be_copied) copyText.select() copyText.setSelectionRange(0, 99999); /* For mobile devices */ @@ -8093,7 +8118,14 @@ const AngularWorkflow = (props) => { if (copyText !== null && copyText !== undefined) { console.log("COPY: ", copyText) console.log("NAVIGATOR: ", navigator) + const clipboard = navigator.clipboard + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)") + return + } + navigator.clipboard.writeText(to_be_copied) + copyText.select(); copyText.setSelectionRange(0, 99999); /* For mobile devices */ diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index e12fb05b..f2e759f3 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1,10 +1,12 @@ import React, {useState, useEffect} from 'react'; import { makeStyles } from '@material-ui/styles'; +import { useTheme } from '@material-ui/core/styles'; import {BrowserView, MobileView} from "react-device-detect"; import {Paper, Typography, FormControlLabel, Button, Divider, Select, MenuItem, FormControl, Switch, Dialog, DialogTitle, DialogContent, DialogActions, TextField, Tooltip, Breadcrumbs, CircularProgress, Chip} from '@material-ui/core'; -import {FileCopy as FileCopyIcon, Delete as DeleteIcon, Remove as RemoveIcon, Add as AddIcon, CheckCircle as CheckCircleIcon, AttachFile as AttachFileIcon, Apps as AppsIcon, ErrorOutline as ErrorOutlineIcon} from '@material-ui/icons'; +import {LockOpen as LockOpenIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, Remove as RemoveIcon, Add as AddIcon, CheckCircle as CheckCircleIcon, AttachFile as AttachFileIcon, Apps as AppsIcon, ErrorOutline as ErrorOutlineIcon} from '@material-ui/icons'; +import uuid from "uuid"; import {Link} from 'react-router-dom'; import YAML from 'yaml' import ChipInput from 'material-ui-chip-input' @@ -192,6 +194,7 @@ const AppCreator = (props) => { const { globalUrl, isLoaded } = props; const classes = useStyles(); const alert = useAlert() + const theme = useTheme(); var upload = "" const increaseAmount = 50 @@ -237,6 +240,12 @@ const AppCreator = (props) => { } const [extraAuth, setExtraAuth] = useState([]) + + const [app, setApp] = useState({}) + const [appAuthentication, setAppAuthentication] = React.useState([]); + const [selectedAction, setSelectedAction] = useState({}) + const [authLoaded, setAuthLoaded] = useState(false) + //const [actions, setActions] = useState([{ // "name": "Get workflows", // "description": "Get workflows", @@ -2416,6 +2425,401 @@ const AppCreator = (props) => { /> + const ParsedActionHandler = () => { + const passedOrg = {"id": "", "name": ""} + const owner = "" + const passedTags = ["single test"] + + const [, setUpdate] = useState() + const [authenticationModalOpen, setAuthenticationModalOpen] = useState(false); + const [selectedApp, setSelectedApp] = useState({ + versions: [{ + "id": selectedAction.app_id, + "version": selectedAction.app_version, + }], + loop_versions: [selectedAction.app_version], + id: selectedAction.app_id, + name: selectedAction.app_name, + version: selectedAction.app_version, + }) + + const [requiresAuthentication, setRequiresAuthentication] = useState(app.authentication.required && app.authentication.parameters !== undefined && app.authentication.parameters !== null) + const [workflow, setWorkflow] = useState({ + name: "", + description: "", + actions: [selectedAction], + start: selectedAction.id, + tags: passedTags, + execution_org: passedOrg, + org_id: passedOrg.id, + id: uuid.v4(), + isValid: true, + owner: owner, + created: Date.now(), + }) + + const EndpointData = () => { + const [tmpVar, setTmpVar] = React.useState("") + + return ( +
+ The API endpoint to use (URL) - predefined in the app + { + setTmpVar(event.target.value) + }} + onBlur={() => { + selectedApp.link = tmpVar + console.log("LINK: ", selectedApp.link) + setSelectedApp(selectedApp) + }} + /> +
+ ) + } + + const setAppActionAuthentication = (newauth) => { + if (app.authentication.required) { + var findAuthId = "" + if (selectedAction.authentication_id !== null && selectedAction.authentication_id !== undefined && selectedAction.authentication_id.length > 0) { + findAuthId = selectedAction.authentication_id + } + + var baseAuthOptions = [] + for (var key in newauth) { + var item = newauth[key] + + const newfields = {} + for (var filterkey in item.fields) { + newfields[item.fields[filterkey].key] = item.fields[filterkey].value + } + + item.fields = newfields + if (item.app.name === app.name) { + baseAuthOptions.push(item) + + if (item.id === findAuthId) { + selectedAction.selectedAuthentication = item + } + } + } + + selectedAction.authentication = baseAuthOptions + //console.log("Authentication: ", authenticationOptions) + if (selectedAction.selectedAuthentication === null || selectedAction.selectedAuthentication === undefined || selectedAction.selectedAuthentication.length === "") { + selectedAction.selectedAuthentication = {} + } + } else { + selectedAction.authentication = [] + selectedAction.authentication_id = "" + selectedAction.selectedAuthentication = {} + } + + setSelectedAction(selectedAction) + console.log(selectedAction) + } + + //{selectedAction.authentication !== undefined && selectedAction.authentication.length > 0 ? + const getAppAuthentication = () => { + fetch(globalUrl+"/api/v1/apps/authentication", { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for apps :O!") + } + + return response.json() + }) + .then((responseJson) => { + if (responseJson.success && responseJson.data !== undefined && responseJson.data !== null && responseJson.data.length !== 0) { + var newauth = [] + for (var key in responseJson.data) { + if (responseJson.data[key].defined === false) { + continue + } + + newauth.push(responseJson.data[key]) + } + + setAppAuthentication(newauth) + setAppActionAuthentication(newauth) + } else { + if (app.authentication.required) { + const tmpParams = selectedAction.parameters + selectedAction.parameters = [] + + for (var paramkey in app.authentication.parameters) { + var item = app.authentication.parameters[paramkey] + item.configuration = true + + const found = selectedAction.parameters.find(param => param.name === item.name) + if (found === null || found === undefined) { + selectedAction.parameters.push(item) + } + } + + for (var paramkey in tmpParams) { + var item = tmpParams[paramkey] + //item.configuration = true + + const found = selectedAction.parameters.find(param => param.name === item.name) + if (found === null || found === undefined) { + selectedAction.parameters.push(item) + } + } + + setSelectedAction(selectedAction) + } + + //alert.error("Failed getting authentications") + } + }) + .catch(error => { + alert.error("Auth loading error: "+error.toString()) + }) + } + + if (!authLoaded && appAuthentication.length === 0 && selectedAction.id !== undefined) { + setAuthLoaded(true) + getAppAuthentication() + } else if (selectedAction.id === undefined && currentAction.name !== undefined && currentAction.name !== null && currentAction.name.length > 0) { + var methodName = `${currentAction.method}_${currentAction.name}` + if (currentAction.method.toLowerCase() === "custom" || currentAction.name.toLowerCase().startsWith(currentAction.method.toLowerCase())) { + methodName = currentAction.name + } + + methodName = methodName.toLowerCase().replaceAll(" ", "_") + if (app.actions !== null && app.actions !== undefined) { + var newselectedaction = app.actions.find(item => item.name.toLowerCase().replaceAll(" ", "_") === methodName) + if (newselectedaction !== undefined && newselectedaction !== null) { + newselectedaction.app_id = app.id + newselectedaction.app_name = app.name + newselectedaction.app_version = app.app_version + newselectedaction.authentication = [] + newselectedaction.authentication_id = "" + newselectedaction.selectedAuthentication = {} + setSelectedAction(newselectedaction) + } + } + } + + const setNewAppAuth = (appAuthData) => { + //console.log("DAta: ", appAuthData) + fetch(globalUrl+"/api/v1/apps/authentication", { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify(appAuthData), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for setting app auth :O!") + } + + return response.json() + }) + .then((responseJson) => { + if (!responseJson.success) { + alert.error("Failed to set app auth: "+responseJson.reason) + } else { + getAppAuthentication(true) + setAuthenticationModalOpen(false) + + // Needs a refresh with the new authentication.. + //alert.success("Successfully saved new app auth") + } + }) + .catch(error => { + alert.error(error.toString()) + }) + } + + const AuthenticationData = (props) => { + const selectedApp = props.app + console.log("APP: ", selectedApp) + + const [authenticationOption, setAuthenticationOptions] = React.useState({ + app: JSON.parse(JSON.stringify(selectedApp)), + fields: {}, + label: "", + usage: [{ + workflow_id: workflow.id, + }], + id: uuid.v4(), + active: true, + }) + + if (selectedApp.authentication === undefined) { + return null + } + + if (selectedApp.authentication.parameters === null || selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0) { + return null + } + + authenticationOption.app.actions = [] + + for (var key in selectedApp.authentication.parameters) { + if (authenticationOption.fields[selectedApp.authentication.parameters[key].name] === undefined) { + authenticationOption.fields[selectedApp.authentication.parameters[key].name] = "" + } + } + + const handleSubmitCheck = () => { + console.log("NEW AUTH: ", authenticationOption) + if (authenticationOption.label.length === 0) { + authenticationOption.label = `Auth for ${selectedApp.name}` + //alert.info("Label can't be empty") + //return + } + + for (var key in selectedApp.authentication.parameters) { + if (authenticationOption.fields[selectedApp.authentication.parameters[key].name].length === 0) { + alert.info("Field "+selectedApp.authentication.parameters[key].name+" can't be empty") + return + } + } + + console.log("Action: ", selectedAction) + selectedAction.authentication_id = authenticationOption.id + selectedAction.selectedAuthentication = authenticationOption + if (selectedAction.authentication === undefined || selectedAction.authentication === null) { + selectedAction.authentication = [authenticationOption] + } else { + selectedAction.authentication.push(authenticationOption) + } + + setSelectedAction(selectedAction) + + var newAuthOption = JSON.parse(JSON.stringify(authenticationOption)) + var newFields = [] + for (const key in newAuthOption.fields) { + const value = newAuthOption.fields[key] + newFields.push({ + key: key, + value: value, + }) + } + + console.log("FIELDS: ", newFields) + newAuthOption.fields = newFields + setNewAppAuth(newAuthOption) + //appAuthentication.push(newAuthOption) + //setAppAuthentication(appAuthentication) + // + + setUpdate(authenticationOption.id) + + /* + {selectedAction.authentication.map(data => ( + + */ + + } + + if (authenticationOption.label === null || authenticationOption.label === undefined) { + authenticationOption.label = selectedApp.name+" authentication" + } + + return ( +
+ + What is this?
+ These are required fields for authenticating with {selectedApp.name} +
+ Name - what is this used for? + { + authenticationOption.label = event.target.value + }} + /> + {selectedApp.link.length > 0 ?
: null} + +
+ {selectedApp.authentication.parameters.map((data, index) => { + return ( +
+ + {data.name} + { + authenticationOption.fields[data.name] = event.target.value + }} + /> +
+ ) + })} + + + + + +
+ ) + } + } + const actionView =
@@ -2628,6 +3032,7 @@ const AppCreator = (props) => { Continue + : null;