diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index 1f758a7e..8a5d9a18 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -116,7 +116,7 @@ const AppGrid = (props) => { }) .then((response) => response.json()) .then((response) => { - if (response.success === true) { + if (response?.success === true) { setFormMessage(response.reason); //toast("Thanks for submitting!") } else { @@ -307,7 +307,7 @@ const AppGrid = (props) => { }) .then(response => response.json()) .then(responseJson => { - if (responseJson.success) { + if (responseJson?.success) { setUserdata(responseJson); setAllActivatedAppIds(responseJson.active_apps) setIsLoggedIn(true); @@ -350,7 +350,7 @@ const AppGrid = (props) => { }) .then((response) => response.json()) .then((responseJson) => { - if (responseJson.success === false) { + if (responseJson?.success === false) { toast.error(responseJson.reason); } else { //toast.success(`App ${type}d Successfully!`); @@ -414,7 +414,7 @@ const AppGrid = (props) => { scrollbarColor: "#494949 #2f2f2f", }} > - {hits.map((data, index) => { + {hits?.map((data, index) => { const appUrl = isCloud === true ? `/apps/${data.objectID}` @@ -556,7 +556,7 @@ const AppGrid = (props) => { }} > - {data.tags.slice(0, 1).map((tag, tagIndex) => ( + {data?.tags?.slice(0, 1)?.map((tag, tagIndex) => ( {normalizedString(tag)} {tagIndex < 1 ? ", " : ""} @@ -569,7 +569,7 @@ const AppGrid = (props) => { ) : (
{data.tags && - data.tags.map((tag, tagIndex) => ( + data?.tags?.map((tag, tagIndex) => ( {normalizedString(tag)} {tagIndex < data.tags.length - 1 ? ", " : ""} @@ -760,7 +760,7 @@ const AppGrid = (props) => { }; const transformRefinementListItems = items => - items.map(item => ({ + items?.map(item => ({ ...item, label: item.label === 'true' ? 'App Editor' : 'Python', })); @@ -1103,7 +1103,7 @@ const AppGrid = (props) => { } }); - const categoryArray = Object.keys(categoryCountMap).map((category) => ({ + const categoryArray = Object.keys(categoryCountMap)?.map((category) => ({ category, count: categoryCountMap[category], })); @@ -1169,7 +1169,7 @@ const AppGrid = (props) => { {!isLoading && (
- {topCategories.map((data, index) => ( + {topCategories?.map((data, index) => (
- {topTags && topTags.length > 0 && topTags.map((data, index) => ( + {topTags && topTags.length > 0 && topTags?.map((data, index) => (
- + Preferences @@ -647,9 +650,9 @@ const [orgName, setOrgName] = useState(selectedOrganization?.name); - Org Documentation reference + Org Documentation reference - + Add a URL that is added as a link, pointing to any external documentation page you want. @@ -672,7 +675,7 @@ const [orgName, setOrgName] = useState(selectedOrganization?.name); placeholder="Paste a URL to an external reference for this implementation" value={documentationReference} onBlur={() => { - if(documentationReference !== selectedOrganization?.defaults?.documentation_reference) { + if (documentationReference !== selectedOrganization?.defaults?.documentation_reference) { handleEditOrg( orgName, orgDescription, @@ -714,7 +717,7 @@ const [orgName, setOrgName] = useState(selectedOrganization?.name); }, style: { color: "white", - + fontWeight: 400, fontSize: 16, borderRadius: 4, @@ -728,16 +731,16 @@ const [orgName, setOrgName] = useState(selectedOrganization?.name); globalUrl={globalUrl} userdata={userdata} serverside={false} - /> + /> - Workflow Backup Repository - - Decide where workflows are backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the repo root in the /orgId/workflow-status/workflowId.json format. MSSP: If suborg exists, this will automatically be applied for them as well (not retroactive). Credentials are encrypted. + Workflow Backup Repository + + Decide where workflows are backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the repo root in the /orgId/workflow-status/workflowId.json format. MSSP: If suborg exists, this will automatically be applied for them as well (not retroactive). Credentials are encrypted. - Repository for workflow backup + Repository for workflow backup - Branch + Branch - Username for backup of workflows + Username for backup of workflows - Git token/password + Git token/password { +const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userdata, handleSendChangeRegionMail }) => { // Show from options: "us-west2", "europe-west2", "europe-west3", "northamerica-northeast1" // var regions = ["us-west2", "europe-west2", "europe-west3", "northamerica-northeast1"] const regionMapping = { "US": "us", "EU-2": "eu", "CA": "ca", - "UK": "gb" + "UK": "gb", }; //let regiontag = "UK"; @@ -1095,6 +1098,7 @@ const RegionChangeModal = memo(({selectedOrganization, setSelectedRegion, userda let regionCode = "gb"; const regionsplit = selectedOrganization?.region_url?.split("."); + if (regionsplit?.length > 2 && !regionsplit[0]?.includes("shuffler")) { const namesplit = regionsplit[0]?.split("/"); regiontag = namesplit[namesplit.length - 1]; @@ -1103,58 +1107,59 @@ const RegionChangeModal = memo(({selectedOrganization, setSelectedRegion, userda regiontag = "US"; regionCode = "us"; } else if (regiontag === "frankfurt") { - regiontag = "EU"; + regiontag = "EU-2"; regionCode = "eu"; } else if (regiontag === "ca") { regiontag = "CA"; regionCode = "ca"; } } -return ( - - {/* Region */} - { + if (userdata?.support) { + setSelectedRegion(e.target.value) + } else { + handleSendChangeRegionMail(e.target.value) + } + }} + > + {Object.keys(regionMapping).map((region, index) => { + const regionImageCode = regionMapping[region]; + // Set the default region if selectedOrganization.region is not set + if (selectedOrganization.region === undefined) { + selectedOrganization.region = "europe-west2"; + } + + // Check if the current region matches the selected region + if (region === selectedOrganization.region) { + // If the region matches, set the MenuItem as selected + return ( + + {/* show region image through cdn */} + {region} + {region} + + ); + } else { + return + {region} {region} - - ); - } else { - return - {region} - {region} ; - } - })} - - -); + } + })} + + + ); }) diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 85840e8d..e035724d 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -214,6 +214,20 @@ const ParsedAction = (props) => { } }, [expansionModalOpen]) + /* + useEffect(() => { + // This will have the OLD selectedAction, not the new one huh? + // How do we map the fields correctly? + if (selectedAction === undefined || selectedAction === null) { + console.log("Selected action is undefined") + return + } + + console.log("Selected action: ", selectedAction?.name, selectedAction) + + }, [selectedAction]) + */ + useEffect(() => { // Changes the order of params to show in order: // auth, required, optional @@ -347,7 +361,7 @@ const ParsedAction = (props) => { } if (keyorder.join(",") !== newkeyorder.join(",")) { - //toast("KEYORDER CHANGED!") + console.log("KEYORDER CHANGED! DID ACTION AS WELL?", keyorder, newkeyorder) setSelectedActionParameters(newparams) selectedAction.parameters = newparams @@ -872,7 +886,7 @@ const ParsedAction = (props) => { } } return { ...param, value: paramvalue, error: message } - }); + }) setSelectedActionParameters(newParameters) setActionlist(newActionList) @@ -3645,6 +3659,14 @@ const ParsedAction = (props) => { ); + if ((multiline === undefined || multiline === false) && ((data?.autocompleted === true || data?.field_active === true) || data.name.startsWith("${") && data.name.endsWith("}"))) { + multiline = true + } + + if (data?.autocompleted === true || data?.field_active === true) { + rows = "1" + } + var datafield = ( { setScrollConfig(scrollConfig) } }} - rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows} + minRows={rows} + maxRows={6} color="primary" // defaultValue={data.value} value={ @@ -4034,7 +4057,8 @@ const ParsedAction = (props) => { helperText={returnHelperText(data.name, data.value)} fullWidth multiline={multiline} - rows={"3"} + minRows={3} + maxRows={6} color="primary" defaultValue={data.value} type={"text"} @@ -4580,7 +4604,7 @@ const ParsedAction = (props) => { data.field_active === true ? { var imageSource = ""; if (params?.row?.org?.id?.length > 0) { if (params?.row?.org?.image?.length > 0){ - imageSource = params.row.org.image + imageSource = params?.row.org?.image }else { imageSource = "/images/no_image.png" } }else { if (userdata.active_org.image?.length > 0){ - imageSource = userdata.active_org.image + imageSource = userdata?.active_org?.image }else { imageSource = "/images/no_image.png" } @@ -370,7 +370,7 @@ const RuntimeDebugger = (props) => { { //setStatus(params.row.status) }}> - {userdata?.active_org?.creator_org?.length === 0 ? ( + {userdata?.active_org?.creator_org?.length === 0 && suborgWorkflowRuns ? ( {source} ) : null} @@ -924,28 +924,13 @@ const RuntimeDebugger = (props) => { onClick={() => setSearchQuery('')} /> )} - ), }} onChange={(e)=>{handleQueryChange(e)}} color="primary" - placeholder="Filter by Workflow Name, Status, Execution Argument, Results.." + placeholder="Filter by Workflow Name, Status, Execution Argument, Results" id="shuffle_search_field" />
diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx index 08139472..0a53e7e9 100644 --- a/frontend/src/components/SearchData.jsx +++ b/frontend/src/components/SearchData.jsx @@ -109,6 +109,12 @@ const SearchData = props => { } }, [searchOpen]); + useEffect(() => { + if (currentRefinement !== inputValue) { + refine(inputValue); + } + }, [currentRefinement]); + return (
diff --git a/frontend/src/views/RunWorkflow.jsx b/frontend/src/views/RunWorkflow.jsx index 34b8c38c..883051a3 100644 --- a/frontend/src/views/RunWorkflow.jsx +++ b/frontend/src/views/RunWorkflow.jsx @@ -77,6 +77,24 @@ const RunWorkflow = (defaultprops) => { const [boxWidth, setBoxWidth] = React.useState(500) const [inputQuestions, setInputQuestions] = React.useState([]) + + useEffect(() => { + if (workflow === undefined || workflow === null || Object.keys(workflow).length === 0) { + return + } + + if (workflow.input_questions === undefined || workflow.input_questions === null) { + return + } + + // Checks if it's a user input-node based or not + if ((answer !== undefined && answer !== null) || (foundSourcenode !== undefined && foundSourcenode !== null)) { + } else { + setInputQuestions(workflow.input_questions) + setUpdate(Math.random()) + } + }, [workflow]) + const IframeWrapper = (props) => { var propsCopy = JSON.parse(JSON.stringify(props)) propsCopy.width = 400 diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index 7dd21dea..cb5c24c5 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -447,6 +447,7 @@ const Welcome = (props) => { } navigate("/welcome?tab=2") + setActiveStep(1) setShowWelcome(true) }}> diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index b9baf567..30376769 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -553,6 +553,10 @@ export const validateJson = (showResult) => { // Check fields if they can be parsed too try { for (const [key, value] of Object.entries(result)) { + if (typeof value === "string") { + value = value.replaceAll(" ", "_") + } + if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) { //console.log("CHECKING STRING: ", value) @@ -573,6 +577,10 @@ export const validateJson = (showResult) => { // Usually only reaches here if raw array > dict > value if (typeof showResult !== "array") { for (const [subkey, subvalue] of Object.entries(value)) { + if (typeof subvalue === "string") { + subvalue = subvalue.replaceAll(" ", "_") + } + if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) { const inside_result = validateJson(subvalue) if (inside_result.valid) { @@ -1841,9 +1849,13 @@ const Workflows = (props) => { var parsedworkflows = []; for (var key in newSubflows) { + if (key === data.id) { + continue + } + const foundWorkflow = workflows.find( (workflow) => workflow.id === newSubflows[key] - ); + ) if (foundWorkflow !== undefined && foundWorkflow !== null) { parsedworkflows.push(foundWorkflow); } @@ -1854,7 +1866,7 @@ const Workflows = (props) => { "Appending subflows during export: ", parsedworkflows.length ); - data.subflows = parsedworkflows; + data.subflows = parsedworkflows } } diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 40aaf42d..33f8618f 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -1935,6 +1935,10 @@ const Workflows2 = (props) => { var parsedworkflows = []; for (var key in newSubflows) { + if (key === data.id) { + continue + } + const foundWorkflow = workflows.find( (workflow) => workflow.id === newSubflows[key] ); @@ -2362,7 +2366,6 @@ const Workflows2 = (props) => { { setDeleteModalOpen(true); setSelectedWorkflowId(data.id); diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 085cf45e..22deeb07 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -4,13 +4,13 @@ go 1.22.7 toolchain go1.22.11 -//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared require ( github.com/docker/docker v27.5.0+incompatible github.com/docker/go-connections v0.5.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.7.96 + github.com/shuffle/shuffle-shared v0.7.99 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 ) diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 4caf4280..e077c2a4 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -319,6 +319,8 @@ github.com/shuffle/shuffle-shared v0.7.82 h1:La11F5jp9bNtM3VuR9PawyWo90/vZ+1Txo4 github.com/shuffle/shuffle-shared v0.7.82/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/shuffle/shuffle-shared v0.7.83 h1:OyyDo0ii8rOYHN5wGbcM94JuDKLmbZ9jhMQ0+/KMb0A= github.com/shuffle/shuffle-shared v0.7.83/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= +github.com/shuffle/shuffle-shared v0.7.96 h1:mH6Bkzn8QIFntkcUxPfyMZJY2r7PNKfc0zWYjZXYQm8= +github.com/shuffle/shuffle-shared v0.7.96/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 82891dbe..72afaa6c 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1240,18 +1240,20 @@ func deployK8sWorker(image string, identifier string, env []string) error { } func deployWorker(image string, identifier string, env []string, executionRequest shuffle.ExecutionRequest) error { + + if len(os.Getenv("REGISTRY_URL")) > 0 && os.Getenv("REGISTRY_URL") != "" { env = append(env, fmt.Sprintf("REGISTRY_URL=%s", os.Getenv("REGISTRY_URL"))) } - // if isKubernetes == "true" { - // err := deployK8sWorker(image, identifier, env, executionRequest) - // if err != nil { - // log.Printf("[ERROR] Failed deploying Kubernetes worker: %s", err) - // } + if swarmConfig == "run" || swarmConfig == "swarm" || isKubernetes == "true" { + // FIXME: Should we handle replies properly? + // In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns + // a 401 from the worker, which returns an error here + go sendWorkerRequest(executionRequest, image, env) - // return err - // } + return nil + } // Binds is the actual "-v" volume. // Max 20% CPU every second @@ -1299,6 +1301,10 @@ func deployWorker(image string, identifier string, env []string, executionReques } } + + //var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG") + parsedUuid := uuid.NewV4() + config := &container.Config{ Image: image, Env: env, @@ -1312,17 +1318,6 @@ func deployWorker(image string, identifier string, env []string, executionReques } } - //var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG") - parsedUuid := uuid.NewV4() - if swarmConfig == "run" || swarmConfig == "swarm" || isKubernetes == "true" { - // FIXME: Should we handle replies properly? - // In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns - // a 401 from the worker, which returns an error here - go sendWorkerRequest(executionRequest, image, env) - - return nil - } - //log.Printf("[INFO] Identifier: %s", identifier) cont, err := dockercli.ContainerCreate( context.Background(), @@ -1356,6 +1351,8 @@ func deployWorker(image string, identifier string, env []string, executionReques } } + log.Printf("WORKER STARTING WITH ENV: %#v", env) + containerStartOptions := container.StartOptions{} err = dockercli.ContainerStart(context.Background(), cont.ID, containerStartOptions) if err != nil { @@ -1390,27 +1387,30 @@ func deployWorker(image string, identifier string, env []string, executionReques log.Printf("[INFO][%s] Worker Container created (2). Environment %s: docker logs %s", executionRequest.ExecutionId, environment, cont.ID) } - //stats, err := cli.ContainerInspect(context.Background(), containerName) - //if err != nil { - // log.Printf("Failed checking worker %s", containerName) - // return - //} + stats, err := dockercli.ContainerInspect(context.Background(), containerName) + if err != nil { + log.Printf("[WARNING] Failed checking worker %s", containerName) + return nil + } - //containerStatus := stats.ContainerJSONBase.State.Status - //if containerStatus != "running" { - // log.Printf("Status of %s is %s. Should be running. Will reset", containerName, containerStatus) - // err = stopWorker(containerName) - // if err != nil { - // log.Printf("Failed stopping worker %s", execution.ExecutionId) - // return - // } + containerStatus := stats.ContainerJSONBase.State.Status + if containerStatus != "running" { + log.Printf("[ERROR] Status of %s is %s. Should be running. Will reset", containerName, containerStatus) + } + /* + err = stopWorker(containerName) + if err != nil { + log.Printf("Failed stopping worker %s", execution.ExecutionId) + return nil + } - // err = deployWorke(cli, workerImage, containerName, env) - // if err != nil { - // log.Printf("Failed executing worker %s in state %s", execution.ExecutionId, containerStatus) - // return - // } - //} + err = deployWorker(dockercli, workerImage, containerName, env) + if err != nil { + log.Printf("Failed executing worker %s in state %s", execution.ExecutionId, containerStatus) + return nil + } + } + */ } else { log.Printf("[INFO][%s] New Worker created. Environment %s: docker logs %s", executionRequest.ExecutionId, environment, cont.ID) }