From 3628812ff659a48190b4bc694f0d1ec441b39396 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 1 Feb 2024 14:32:31 +0100 Subject: [PATCH 001/142] Fixed api calls not following PROXY guidelines: https://github.com/Shuffle/Shuffle/issues/1318 --- backend/go-app/docker.go | 4 ++-- backend/go-app/main.go | 18 ++++++++---------- backend/go-app/walkoff.go | 37 +------------------------------------ 3 files changed, 11 insertions(+), 48 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index a4565523..b7db534a 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -798,7 +798,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { // Downloads and activates an app from shuffler.io if possible func handleRemoteDownloadApp(resp http.ResponseWriter, ctx context.Context, user shuffle.User, appId string) { url := fmt.Sprintf("https://shuffler.io/api/v1/apps/%s/config", appId) - log.Printf("Downloading API from %s", url) + log.Printf("[DEBUG] Downloading API from URL %s", url) req, err := http.NewRequest( "GET", url, @@ -812,7 +812,7 @@ func handleRemoteDownloadApp(resp http.ResponseWriter, ctx context.Context, user return } - httpClient := &http.Client{} + httpClient := shuffle.GetExternalClient(url) newresp, err := httpClient.Do(req) if err != nil { log.Printf("[ERROR] Failed running auto-download request for %s: %s", appId, err) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b702f16d..e291c9ae 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1972,13 +1972,8 @@ func executeCloudAction(action shuffle.CloudSyncJob, apikey string) error { return err } - //transport := http.DefaultTransport.(*http.Transport).Clone() - //client := &http.Client{ - // Transport: transport, - //} - client := &http.Client{} - syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync/handle_action", syncUrl) + client := shuffle.GetExternalClient(syncUrl) req, err := http.NewRequest( "POST", syncUrl, @@ -3483,8 +3478,8 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { } - client := &http.Client{} syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl) + client := shuffle.GetExternalClient(syncUrl) req, err := http.NewRequest( "POST", syncUrl, @@ -3867,6 +3862,9 @@ func runInitEs(ctx context.Context) { log.Printf("[INFO] Running schedule for cleaning up or re-running unfinished workflows in %d environments.", len(environments)) for _, environment := range environments { + // Allowed without PROXY management as it's localhost + // client := shuffle.GetExternalClient(syncUrl) + httpClient := &http.Client{} url := fmt.Sprintf("http://localhost:5001/api/v1/environments/%s/stop", environment) req, err := http.NewRequest( @@ -4077,7 +4075,7 @@ func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) { //r.HandleFunc("/api/v1/getorgs", handleGetOrgs).Methods("GET", "OPTIONS") syncURL := fmt.Sprintf("%s/api/v1/cloud/sync/get_access", syncUrl) - client := &http.Client{} + client := shuffle.GetExternalClient(syncURL) req, err := http.NewRequest( "GET", syncURL, @@ -4121,7 +4119,7 @@ func handleStopCloudSync(syncUrl string, org shuffle.Org) (*shuffle.Org, error) log.Printf("[INFO] Should run cloud sync disable for org %s with URL %s and sync key %s", org.Id, syncUrl, org.SyncConfig.Apikey) - client := &http.Client{} + client := shuffle.GetExternalClient(syncUrl) req, err := http.NewRequest( "DELETE", syncUrl, @@ -4292,7 +4290,6 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { //log.Printf("Apidata: %s", tmpData.Apikey) // FIXME: Path - client := &http.Client{} apiPath := "/api/v1/cloud/sync/setup" if tmpData.Disable { if !org.CloudSync { @@ -4362,6 +4359,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { bytes.NewBuffer(b), ) + client := shuffle.GetExternalClient(syncPath) newresp, err := client.Do(req) if err != nil { resp.WriteHeader(400) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 659c52b3..c1e71f2f 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -952,42 +952,7 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { resp.Write([]byte(`{"success": true}`)) } -// Identifies what a category defined really is -func getWorkflowLocal(fileId string, request *http.Request) ([]byte, error) { - fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s", localBase, fileId) - client := &http.Client{} - req, err := http.NewRequest( - "GET", - fullUrl, - nil, - ) - - if err != nil { - return []byte{}, err - } - - for key, value := range request.Header { - req.Header.Add(key, strings.Join(value, ";")) - } - - newresp, err := client.Do(req) - if err != nil { - return []byte{}, err - } - - body, err := ioutil.ReadAll(newresp.Body) - if err != nil { - return []byte{}, err - } - - // Temporary solution - if strings.Contains(string(body), "reason") && strings.Contains(string(body), "false") { - return []byte{}, errors.New(fmt.Sprintf("Failed getting workflow %s with message %s", fileId, string(body))) - } - - return body, nil -} func handleExecution(id string, workflow shuffle.Workflow, request *http.Request, orgId string) (shuffle.WorkflowExecution, string, error) { //go func() { @@ -1777,7 +1742,7 @@ func cloudExecuteAction(execution shuffle.WorkflowExecution) error { } syncURL := fmt.Sprintf("%s/api/v1/cloud/sync/execute_node", syncUrl) - client := &http.Client{} + client := shuffle.GetExternalClient(syncURL) req, err := http.NewRequest( "POST", syncURL, From 0e8fdd9027bfdd57a525c57eefe695b0fc259ee2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 1 Feb 2024 14:34:03 +0100 Subject: [PATCH 002/142] Rerolled to nightly --- .github/workflows/dockerbuild.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index e6e0600c..8f139bba 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -3,7 +3,7 @@ name: dockerbuild on: push: branches: - - main + - 1.4.0 paths: - "**" - "!.github/**" @@ -19,23 +19,23 @@ jobs: include: - app: frontend path: frontend - version: 1.3.2 + version: nightly experimental: true - app: backend path: backend - version: 1.3.2 + version: nightly experimental: true - app: app_sdk path: backend/app_sdk - version: 1.3.2 + version: nightly experimental: true - app: orborus path: functions/onprem/orborus - version: 1.3.2 + version: nightly experimental: true - app: worker path: functions/onprem/worker - version: 1.3.2 + version: nightly experimental: true steps: - name: Checkout @@ -77,11 +77,11 @@ jobs: cache-to: type=local,dest=/tmp/.buildx-cache tags: | ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }} - ghcr.io/shuffle/shuffle-${{ matrix.app }}:latest + ghcr.io/shuffle/shuffle-${{ matrix.app }}:nightly ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }} - ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:latest + ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:nightly frikky/shuffle-${{ matrix.app }}:${{ matrix.version }} - frikky/shuffle-${{ matrix.app }}:latest + frikky/shuffle-${{ matrix.app }}:nightly frikky/shuffle:${{ matrix.app }} - name: Image digest From 7dbb1a8b149e9348bb927e8e283f3f2108377e5b Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 1 Feb 2024 14:34:29 +0100 Subject: [PATCH 003/142] Force rebuidl --- backend/go-app/docker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index b7db534a..5b1bc1ee 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -822,6 +822,7 @@ func handleRemoteDownloadApp(resp http.ResponseWriter, ctx context.Context, user } defer newresp.Body.Close() + respBody, err := ioutil.ReadAll(newresp.Body) if err != nil { log.Printf("[ERROR] Failed setting respbody for workflow download: %s", err) From cb9be52245a327b50a95af5e9c51e07a9b5c6033 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 2 Feb 2024 01:54:18 +0100 Subject: [PATCH 004/142] Fixed recursion bug where items that used to become empty strings became full values --- backend/app_sdk/app_base.py | 106 +++++++++++++++------- backend/app_sdk/recurse_test.py | 153 ++++++++++++++++++++++++-------- 2 files changed, 186 insertions(+), 73 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 3937b052..9e90ec62 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -2021,6 +2021,7 @@ class AppBase: return " ".join(newlist) # Parses JSON loops and such down to the item you're looking for + # Check recurse_test.py for examples and tests of this function # $nodename.#.id # $nodename.data.#min-max.info.id # $nodename.data.#1-max.info.id @@ -2048,7 +2049,7 @@ class AppBase: for innervalue in basejson: # 1. Check the next item (message) # 2. Call this function again - + try: ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:]) except IndexError: @@ -2077,7 +2078,7 @@ class AppBase: # Means it's a single item -> continue if seconditem == "": - print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) + #print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end": firstitem = len(basejson)-1 elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first": @@ -2085,14 +2086,14 @@ class AppBase: else: firstitem = int(firstitem) - print(f"[DEBUG] Post lower checks with item {firstitem}") + #print(f"[DEBUG] Post lower checks with item {firstitem}") tmpitem = basejson[int(firstitem)] try: newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) except IndexError: newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) else: - print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) + #print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) if isinstance(firstitem, str): if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end": firstitem = len(basejson)-1 @@ -2113,7 +2114,7 @@ class AppBase: else: seconditem = int(seconditem) - print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") + #print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") newvalue = [] if int(seconditem) > len(basejson): seconditem = len(basejson) @@ -2121,12 +2122,11 @@ class AppBase: for i in range(int(firstitem), int(seconditem)+1): # 1. Check the next item (message) # 2. Call this function again - #self.logger.info("Base: %s" % basejson[i]) try: ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) except IndexError: - print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) + #print("[DEBUG] INDEXERROR (1): ", parsersplit[outercnt]) #ret = innervalue ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:]) @@ -2137,16 +2137,16 @@ class AppBase: else: if len(value) == 0: return basejson, False - + try: if isinstance(basejson, list): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson, bool): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) return basejson, False elif isinstance(basejson, int): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) return basejson, False elif isinstance(basejson[value], str): try: @@ -2154,8 +2154,16 @@ class AppBase: basejson = json.loads(basejson[value]) else: # Should we sanitize here? - self.logger.info("[DEBUG] VALUE TO SANITIZE?: %s" % basejson[value]) - return str(basejson[value]), False + #print("[DEBUG] VALUE TO SANITIZE FOR KEY '%s'?: %s" % (value, basejson[value])) + + # Check if we are on the last item? + if outercnt == len(parsersplit)-1: + #print("[DEBUG] LAST KEY") + return str(basejson[value]), False + else: + #print("[DEBUG] NOT LAST KEY") + pass + except json.decoder.JSONDecodeError as e: return str(basejson[value]), False else: @@ -2169,54 +2177,88 @@ class AppBase: try: if isinstance(basejson, list): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson, bool): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) return basejson, False elif isinstance(basejson, int): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) return basejson, False elif isinstance(basejson[value], str): - print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) + #print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: - print("[DEBUG] BASEJSON: %s" % basejson) + #print("[DEBUG] BASEJSON: %s" % basejson) if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")): basejson = json.loads(basejson[value]) else: - return str(basejson[value]), False + + if outercnt == len(parsersplit)-1: + #print("LAST KEY (2)") + return str(basejson[value]), False + else: + #print("NOT LAST KEY (2)") + pass + except json.decoder.JSONDecodeError as e: - print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) + #print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return str(basejson[value]), False else: basejson = basejson[value] - except KeyError as e: - print("\n\n[WARNING] Running third dot notation fix that always find the correct value %s: %s" % (value, e)) + # Check if previous key was handled or not + previouskey = parsersplit[outercnt-1] + #print("[DEBUG] PREVIOUS KEY: ", previouskey) + + tmpval = previouskey + "." + value + #print("\n\n[WARNING] Running third dot notation fix '%s' on data %s: %s" % (value, basejson, e)) + if tmpval in basejson: + return basejson[tmpval], False try: currentsplitcnt = splitcnt + recursed_value = value handled = False + + #tmpbase = basejson + previouskey = value while True: + #print("\n\n[DEBUG] CURRENTSPLITCNT: ", currentsplitcnt) newvalue = parsersplit[currentsplitcnt+1] if newvalue == "#" or newvalue == "": break recursed_value += "." + newvalue + #print("\n\nRECURSED: ", recursed_value) + found = False for key, value in basejson.items(): if recursed_value.lower() in key.lower(): found = True if found == False: - print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value) - break + #print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value) + + # Check if we are on the last key or not + return "", False + #if outercnt == len(parsersplit)-1: + # print("[DEBUG] LAST KEY (3)") + # break + #else: + # print("[DEBUG] NOT LAST KEY (3)") + # return "", False if recursed_value in basejson: - print("[INFO] FOUND RECURSED VALUE: ", recursed_value) + #print("[INFO] FOUND RECURSED VALUE: ", recursed_value) basejson = basejson[recursed_value] - handled = True + + # Whether to dig deeper or not + if isinstance(basejson, bool) or isinstance(basejson, int) or isinstance(basejson, str): + handled = False + else: + handled = True + break currentsplitcnt += 1 @@ -2226,21 +2268,17 @@ class AppBase: break except IndexError as e: - print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) - break - + print("[DEBUG] INDEXERROR (2):", parsersplit[outercnt]) + return "", False outercnt += 1 - + except KeyError as e: print("[INFO] Lower keyerror: %s" % e) return "", False except Exception as e: print("[WARNING] Exception: %s" % e) - return basejson, False - - #return basejson - #return "KeyError: Couldn't find key: %s" % e + return "", False return basejson, False diff --git a/backend/app_sdk/recurse_test.py b/backend/app_sdk/recurse_test.py index 9dfbd2c5..1cf2bfea 100644 --- a/backend/app_sdk/recurse_test.py +++ b/backend/app_sdk/recurse_test.py @@ -58,7 +58,7 @@ def recurse_json(basejson, parsersplit): # Means it's a single item -> continue if seconditem == "": - print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) + #print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end": firstitem = len(basejson)-1 elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first": @@ -66,14 +66,14 @@ def recurse_json(basejson, parsersplit): else: firstitem = int(firstitem) - print(f"[DEBUG] Post lower checks with item {firstitem}") + #print(f"[DEBUG] Post lower checks with item {firstitem}") tmpitem = basejson[int(firstitem)] try: newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) except IndexError: newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) else: - print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) + #print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) if isinstance(firstitem, str): if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end": firstitem = len(basejson)-1 @@ -94,7 +94,7 @@ def recurse_json(basejson, parsersplit): else: seconditem = int(seconditem) - print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") + #print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") newvalue = [] if int(seconditem) > len(basejson): seconditem = len(basejson) @@ -106,7 +106,7 @@ def recurse_json(basejson, parsersplit): try: ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) except IndexError: - print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) + #print("[DEBUG] INDEXERROR (1): ", parsersplit[outercnt]) #ret = innervalue ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:]) @@ -115,20 +115,18 @@ def recurse_json(basejson, parsersplit): return newvalue, is_loop else: - print("IN ELSE WITH VALUE: %s" % value) if len(value) == 0: return basejson, False try: - print("PRINT:", basejson) if isinstance(basejson, list): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson, bool): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) return basejson, False elif isinstance(basejson, int): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) return basejson, False elif isinstance(basejson[value], str): try: @@ -136,8 +134,16 @@ def recurse_json(basejson, parsersplit): basejson = json.loads(basejson[value]) else: # Should we sanitize here? - print("[DEBUG] VALUE TO SANITIZE?: %s" % basejson[value]) - return str(basejson[value]), False + #print("[DEBUG] VALUE TO SANITIZE FOR KEY '%s'?: %s" % (value, basejson[value])) + + # Check if we are on the last item? + if outercnt == len(parsersplit)-1: + #print("[DEBUG] LAST KEY") + return str(basejson[value]), False + else: + #print("[DEBUG] NOT LAST KEY") + pass + except json.decoder.JSONDecodeError as e: return str(basejson[value]), False else: @@ -151,42 +157,60 @@ def recurse_json(basejson, parsersplit): try: if isinstance(basejson, list): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson, bool): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) return basejson, False elif isinstance(basejson, int): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) + #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) return basejson, False elif isinstance(basejson[value], str): - print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) + #print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: - print("[DEBUG] BASEJSON: %s" % basejson) + #print("[DEBUG] BASEJSON: %s" % basejson) if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")): basejson = json.loads(basejson[value]) else: - return str(basejson[value]), False + + if outercnt == len(parsersplit)-1: + #print("LAST KEY (2)") + return str(basejson[value]), False + else: + #print("NOT LAST KEY (2)") + pass + except json.decoder.JSONDecodeError as e: - print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) + #print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return str(basejson[value]), False else: basejson = basejson[value] except KeyError as e: - print("\n\n[WARNING] Running third dot notation fix %s: %s" % (value, e)) + # Check if previous key was handled or not + previouskey = parsersplit[outercnt-1] + #print("[DEBUG] PREVIOUS KEY: ", previouskey) + + tmpval = previouskey + "." + value + #print("\n\n[WARNING] Running third dot notation fix '%s' on data %s: %s" % (value, basejson, e)) + if tmpval in basejson: + return basejson[tmpval], False try: - currentsplitcnt = splitcnt + recursed_value = value handled = False + + #tmpbase = basejson + previouskey = value while True: + #print("\n\n[DEBUG] CURRENTSPLITCNT: ", currentsplitcnt) newvalue = parsersplit[currentsplitcnt+1] if newvalue == "#" or newvalue == "": break recursed_value += "." + newvalue - print("\n\nRECURSED: ", recursed_value) + #print("\n\nRECURSED: ", recursed_value) found = False for key, value in basejson.items(): @@ -194,13 +218,27 @@ def recurse_json(basejson, parsersplit): found = True if found == False: - print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value) - break + #print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value) + + # Check if we are on the last key or not + return "", False + #if outercnt == len(parsersplit)-1: + # print("[DEBUG] LAST KEY (3)") + # break + #else: + # print("[DEBUG] NOT LAST KEY (3)") + # return "", False if recursed_value in basejson: - print("[INFO] FOUND RECURSED VALUE: ", recursed_value) + #print("[INFO] FOUND RECURSED VALUE: ", recursed_value) basejson = basejson[recursed_value] - handled = True + + # Whether to dig deeper or not + if isinstance(basejson, bool) or isinstance(basejson, int) or isinstance(basejson, str): + handled = False + else: + handled = True + break currentsplitcnt += 1 @@ -210,8 +248,8 @@ def recurse_json(basejson, parsersplit): break except IndexError as e: - print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) - break + print("[DEBUG] INDEXERROR (2):", parsersplit[outercnt]) + return "", False outercnt += 1 @@ -220,10 +258,7 @@ def recurse_json(basejson, parsersplit): return "", False except Exception as e: print("[WARNING] Exception: %s" % e) - return basejson, False - - #return basejson - #return "KeyError: Couldn't find key: %s" % e + return "", False return basejson, False @@ -231,21 +266,61 @@ print("[INFO] Starting") #input_data = "test" #input_data = "test2.data" -#input_data = "test2.test3.data" -input_data = "test2.test5.data.hello" -parsersplit = input_data.split(".") + + +# Matchwith basejson = { "test": "hello", "test2": { - "data": "hello2", + "test3": "hello2", "test3.data": "hello3", "test4.data.testing": { "value": "hello4" }, - "test5.data.hello": "wut" + "test5.data.hello": "wut", }, + "test3": ["hello", "hello2", "hello3"], + "test4": [{ + "id": "1", + }] } -ret, is_loop = recurse_json(basejson, parsersplit) -print("\n\nOUTPUT RET (%s): %s" % (input_data, ret)) +# Inputexamples (ALL should be True) +inputs = { + #"": "", + "badkey": "", + "test": "hello", + "test2.badkey": "", + "test2.test3": "hello2", + "test2.test3.data": "hello3", + "test2.test4.data.testing": "{'value': 'hello4'}", # FIXME: Doesn't work due to break vs return "", False in last exception + "test2.test4.data.testing.value": "hello4", # FIXME: Doesn't work due to break vs return "", False in last exception. Not fixed as we didn't find one of these yet. + "test2.test5.data.hello": "wut", + "test2.test5.data.badkey": "", + "test3.#1": "hello2", + "test4.#0.id": "1", + "test4.#1.id": "", +} + +outputs = [] +for key, value in inputs.items(): + parsersplit = key.split(".") + ret, is_loop = recurse_json(basejson, parsersplit) + print("\n\nOUTPUT RET (%s): %s" % (key, ret)) + + outputs.append("[%s]: %s = '%s' vs '%s'" % (str(ret) == str(value), key, ret, value)) + +print("\n\n%s" % "\n".join(outputs)) + +#input_data = "" +#input_data = "badkey" +#input_data = "test" +#input_data = "test2.data" +#input_data = "test2.test3.data" +#input_data = "test2.test4.data.testing.value.as" +#input_data = "test2.test5.data.hello" + + + + From 8c640cbfd1e082bee8c26dfc712a6a2816e0959c Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 5 Feb 2024 23:47:05 +0100 Subject: [PATCH 005/142] Loads of frontend fixes --- .env | 6 +- backend/app_sdk/app_base.py | 2 +- frontend/src/components/Header.jsx | 2 +- frontend/src/components/NewHeader.jsx | 4 +- frontend/src/components/Oauth2Auth.jsx | 2 +- frontend/src/components/ParsedAction.jsx | 7 +- frontend/src/components/SearchData.jsx | 78 ++- frontend/src/components/Searchfield.jsx | 2 +- frontend/src/components/ShuffleCodeEditor.jsx | 229 +++---- frontend/src/views/Admin.jsx | 570 +++++++++++------- frontend/src/views/AngularWorkflow.jsx | 141 ++--- frontend/src/views/AppCreator.jsx | 10 +- frontend/src/views/Docs.jsx | 423 +++++++------ frontend/src/views/Workflows.jsx | 17 +- 14 files changed, 885 insertions(+), 608 deletions(-) diff --git a/.env b/.env index 4bd36483..f574d03d 100755 --- a/.env +++ b/.env @@ -59,9 +59,9 @@ SHUFFLE_ORBORUS_STARTUP_DELAY= # Used for setting up a startup delay for Orbor SHUFFLE_SKIPSSL_VERIFY=true IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not -SHUFFLE_BASE_IMAGE_NAME=shuffle -SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io -SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.3.1" +#SHUFFLE_BASE_IMAGE_NAME=shuffle +#SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io +#SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.3.1" # The eth0 interface inside a container corresponds # to the virtual Ethernet interface that connects diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 9e90ec62..538d3afe 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -466,8 +466,8 @@ class AppBase: "success": True, "status": request.status_code, "url": request.url, - "headers": parsedheaders, "body": jsondata, + "headers": parsedheaders, "cookies":cookies, }) except Exception as e: diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index dbf68fe2..4f1d47ef 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -615,7 +615,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
- +
diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 558d0c72..c0cf173d 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -741,7 +741,7 @@ const Header = (props) => { >
- +
- +
{ }, "fields": parsedFields, "type": "oauth2-app", - "reference_workflow": workflowId, + //"reference_workflow": workflowId, } if (setNewAppAuth !== undefined) { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 68366fe4..7d9f2ac0 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1644,13 +1644,11 @@ const ParsedAction = (props) => { multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline} helperText={returnHelperText(data.name, data.value)} onClick={() => { - console.log("Clicked field: ", clickedFieldId, data.name) /* setExpansionModalOpen(false); */ if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { - console.log("IN SCROLL CONFIG!") scrollConfig.selected = clickedFieldId setScrollConfig(scrollConfig) @@ -3737,6 +3735,11 @@ const ParsedAction = (props) => { break } } + + // Check if it starts with "Get List" and method is "Get" + if (params.inputProps.value.startsWith("Get List")) { + console.log("Get List") + } } return ( diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx index 10d58c7a..148b5bab 100644 --- a/frontend/src/components/SearchData.jsx +++ b/frontend/src/components/SearchData.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'; import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; +import { toast } from "react-toastify" import { Chip, @@ -41,7 +42,7 @@ const chipStyle = { const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const SearchData = props => { - const { serverside, userdata, setModalOpen, modalOpen } = props + const { serverside, globalUrl, userdata, setModalOpen, modalOpen } = props let navigate = useNavigate(); const borderRadius = 3 const node = useRef() @@ -326,6 +327,57 @@ const SearchData = props => { ) } + const activateApp = (name, appid, type) => { + if (globalUrl === undefined || globalUrl === null) { + console.log(`Global URL not set`) + return + } + + if (name === undefined || name === null) { + name = "" + } + + name = name.replaceAll("_", " ") + + if (userdata === undefined || userdata === null || userdata.id === undefined) { + toast(`You need to be logged in to activate the ${name} app. Redirecting`) + + setTimeout(() => { + navigate(`/register?message=You need to be logged in to use the ${name} app.`) + }, 500) + return + } + + const url = `${globalUrl}/api/v1/apps/${appid}/${type}` + + fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io`) + } + + return response.json() + }) + .then((responseJson) => { + if (responseJson.success === false) { + toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io for more info`) + } else { + toast(`App successfully ${type}d. Please refresh the page to use it.`) + } + }) + .catch(error => { + //toast(error.toString()) + console.log("Activate app error: ", error.toString()) + }); + } + const AppHits = ({ hits }) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(0) @@ -431,7 +483,6 @@ const SearchData = props => { return ( { setSearchOpen(true) - setModalOpen(false) aa('init', { appId: searchClient.appId, @@ -474,6 +525,29 @@ const SearchData = props => { */} + ) diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 4e292661..1652c55d 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -98,7 +98,7 @@ const SearchField = props => {
: null} - + diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 0c86d0ee..15aec446 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -236,8 +236,6 @@ const CodeEditor = (props) => { //var newitem = JSON.parse(base); var newitem = validateJson(base).result - - to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); for (let copykey in copy.namespace) { if (copy.namespace[copykey].includes("Results for")) { @@ -825,7 +823,7 @@ const CodeEditor = (props) => { console.log("ERR IN INPUT: ", e) } - console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) + //console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) if (typeof new_input === "object") { new_input = JSON.stringify(new_input) @@ -1078,114 +1076,122 @@ const CodeEditor = (props) => { */} { isFileEditor ? null :
- - { - setAnchorEl(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {liquidFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - - - { - setAnchorEl2(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {mathFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - - - { - setAnchorEl3(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {pythonFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - + {selectedAction.name === "execute_python" ? + + Run Python Code + + : +
+ + { + setAnchorEl(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {liquidFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl2(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {mathFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl3(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {pythonFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + +
+ } */} - - - - - ); - })} + } + + setMatchingOrganizations(active); + } + }} + > + + + {/**/} + + + + + ); + })}
) : null; @@ -4131,6 +4251,46 @@ If you're interested, please let me know a time that works for you, or set up a
) : null; + const getLogs = async (ip, userId) => { + setLogsLoading(true); + console.log("logs loading: ", logsLoading); + fetch(`${globalUrl}/api/v1/users/${userId}/audit?user_ip=${ip}`, { + mode: "cors", + method: "GET", + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + return response.json(); + }) + .then((responseJson) => { + console.log("ResponseJSON: ", responseJson); + if (responseJson.success === true) { + setLogs(responseJson.logs); + } else { + if (responseJson.success === false || responseJson.reason !== undefined) { + console.log("Reason given: ", responseJson.reason) + toast("Failed getting logs: " + responseJson.reason) + setLogs([]) + } else { + toast("Failed getting logs"); + } + } + console.log("logs loading now: ", logsLoading); + setLogsLoading(false); + }) + .catch((error) => { + console.log("Error: ", error); + toast("Failed getting logs. Please contact: ", error); + console.log("logs loading now: ", logsLoading); + setLogsLoading(false); + }); + }; + const changeRecommendation = (recommendation, action) => { const data = { action: action, diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index f22ee36b..2f461690 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -17,7 +17,7 @@ import { ToastContainer, toast } from "react-toastify" import { isMobile } from "react-device-detect" import aa from 'search-insights' import Drift from "react-driftjs"; -import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; +import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx"; import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; import algoliasearch from 'algoliasearch/lite'; @@ -116,17 +116,12 @@ import { AutoAwesome as AutoAwesomeIcon, } from "@mui/icons-material"; - import * as cytoscape from "cytoscape"; import * as edgehandles from "cytoscape-edgehandles"; -//import * as clipboard from "cytoscape-clipboard"; -//import undoRedo from "cytoscape-undo-redo"; -//import cxtmenu from "cytoscape-cxtmenu"; import CytoscapeComponent from "react-cytoscapejs"; - import Draggable from "react-draggable"; - import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; +import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; @@ -842,48 +837,6 @@ const AngularWorkflow = (defaultprops) => { }); }; - function OuterLink(props) { - if (props.href.includes("http") || props.href.includes("mailto")) { - return ( - - {props.children} - - ); - } - return ( - - {props.children} - - ); - } - - function Img(props) { - return {props.alt}; - } - - function CodeHandler(props) { - return ( -
-        {props.value}
-      
- ); - } - function Heading(props) { const element = React.createElement( `h${props.level}`, @@ -1021,8 +974,6 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - console.log("GOT A RESPONSE??") - // getWorkflowExecutionCount(id); if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null) { // - means it's opposite @@ -1035,8 +986,6 @@ const AngularWorkflow = (defaultprops) => { tmpView = execution_id; } - console.log("EXECUTION ID: ", tmpView) - // Compare with currently selected item if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { // Don't clean up if it's already open @@ -1820,7 +1769,6 @@ const AngularWorkflow = (defaultprops) => { } } - console.log("FOUNDMISSING: ", foundmissing) if (foundmissing) { //toast("This workflow contains a node that requires an execution argument. Please provide one.") setExecutionRequestStarted(false) @@ -2788,11 +2736,17 @@ const AngularWorkflow = (defaultprops) => { if (response.status >= 500) { toast("Something went wrong while loading the workflow. Please reload.") } else { - toast("You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..") - setTimeout(() => { - window.location.pathname = "/workflows"; - }, 2000); + // Check for execution_id in URL + // don't redirect if it exists + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + var execFound = new URLSearchParams(cursearch).get("execution_id"); + if (execFound === null) { + toast(`You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..`) + setTimeout(() => { + window.location.pathname = "/workflows"; + }, 2000); + } } } @@ -8518,8 +8472,6 @@ const AngularWorkflow = (defaultprops) => { } if (param.name === "headers") { - console.log("Swap header? For now, yes. File found: ", fileid_found) - if (fileid_found) { newSelectedAction.parameters[paramkey].value = "" newSelectedAction.parameters[paramkey].autocompleted = true @@ -8614,7 +8566,6 @@ const AngularWorkflow = (defaultprops) => { if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { const foundActionIndex = workflow.actions.findIndex(actiondata => actiondata.id === newSelectedAction.id) - console.log("Found action on index ", foundActionIndex) if (foundActionIndex >= 0) { workflow.actions[foundActionIndex] = newSelectedAction setWorkflow(workflow) @@ -9444,14 +9395,18 @@ const AngularWorkflow = (defaultprops) => { bottom: 10, left: 10, color: "rgba(255,255,255,0.6)", + zIndex: 10000, }} > Conditions can't be used for loops [ .# ]{" "} Learn more @@ -15935,7 +15890,10 @@ const AngularWorkflow = (defaultprops) => { setSelectedResult(data); setCodeModalOpen(true); } else { - toast("Please wait until the workflow is loaded and try again") + toast("Please wait until the workflow is loaded and try again") + setCodeModalOpen(true) + setSelectedResult(data) + } }} > @@ -16197,6 +16155,25 @@ const AngularWorkflow = (defaultprops) => { }} > + + { + e.preventDefault() + }} + > + + + { right: 170, }} onClick={(e) => { - e.preventDefault(); + e.preventDefault() - if (workflowExecutions !== null) { + if (workflowExecutions !== null) { for (let execkey in workflowExecutions) { const execution = workflowExecutions[execkey]; - if (execution.execution_argument.includes("too large")) { - continue - } + if (execution.execution_argument.includes("too large")) { + continue + } const result = execution.results.find((data) => data.status === "SUCCESS" && data.action.id === selectedResult.action.id) - if (result !== undefined) { - const oldstartnode = cy.getElementById(selectedResult.action.id); - if (oldstartnode !== undefined && oldstartnode !== null) { - const foundname = oldstartnode.data("label") - if (foundname !== undefined && foundname !== null) { - result.action.label = foundname - } - } + if (result !== undefined) { + const oldstartnode = cy.getElementById(selectedResult.action.id); + if (oldstartnode !== undefined && oldstartnode !== null) { + const foundname = oldstartnode.data("label") + if (foundname !== undefined && foundname !== null) { + result.action.label = foundname + } + } - setSelectedResult(result); - setUpdate(Math.random()); - break; - } + setSelectedResult(result); + setUpdate(Math.random()); + break; + } } - } + } }} > diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 71625903..12e81862 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -264,7 +264,7 @@ export const appCategories = [ "name": "IAM", "color": "#FFC107", "icon": "iam", - "action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", ], + "action_labels": ["Reset Password", "Enable user", "Disable user", "Get Identity", "Get Asset", "Search Identity", "Get KMS Key",], }, { "name": "Network", "color": "#FFC107", @@ -1240,7 +1240,7 @@ const AppCreator = (defaultprops) => { if (methodvalue.responses.default.content["text/plain"]["schema"]["format"] === "binary" && methodvalue.responses.default.content["text/plain"]["schema"]["type"] === "string") { newaction.example_response = "shuffle_file_download" - } + } } } } @@ -2174,6 +2174,10 @@ const AppCreator = (defaultprops) => { queryitem.name.toLowerCase() == "ssl_verify" || queryitem.name.toLowerCase() == "queries" || queryitem.name.toLowerCase() == "headers" || + queryitem.name.toLowerCase() == "list" || + queryitem.name.toLowerCase() == "dict" || + queryitem.name.toLowerCase() == "str" || + queryitem.name.toLowerCase() == "int" || queryitem.name.toLowerCase() == "access_token") { /* @@ -3748,7 +3752,7 @@ const AppCreator = (defaultprops) => { }} > - +
New action
diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 37bcec0a..027aad59 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -3,10 +3,12 @@ import React, { useEffect, useState } from "react"; import { toast } from 'react-toastify'; import Markdown from 'react-markdown' +import theme from '../theme.jsx'; +import ReactJson from "react-json-view"; +import { isMobile } from "react-device-detect"; import { BrowserView, MobileView } from "react-device-detect"; import { useParams, useNavigate, Link } from "react-router-dom"; -import { isMobile } from "react-device-detect"; -import theme from '../theme.jsx'; +import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { Grid, @@ -30,6 +32,7 @@ import { Edit as EditIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ExpandMore as ExpandMoreIcon, + FileCopy as FileCopyIcon } from "@mui/icons-material"; const Body = { @@ -60,6 +63,120 @@ const innerHrefStyle = { textDecoration: "none", }; + +export const CopyToClipboard = (props) => { + const {text, style, onCopy} = props; + const parsedstyle = style !== undefined ? style : { + position: "absolute", + right: 0, + top: -10, + } + + return ( +
+ { + navigator.clipboard.writeText(text); + toast("Copied to clipboard") + }} + > + + +
+ ) +} + +export const OuterLink = (props) => { + if (props.href.includes("http") || props.href.includes("mailto")) { + return ( + + {props.children} + + ); + } + + return ( + + {props.children} + + ); + } + + +export const Img = (props) => { + return {props.alt}; +} + +export const CodeHandler = (props) => { + const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : "" + + + + const validate = validateJson(propvalue) + + var newprop = propvalue + if (validate.valid === false) { + // Check if https://shuffler.io in the url + // if so, then we change it for the current url + if (propvalue.includes("https://shuffler.io")) { + newprop = propvalue.replace("https://shuffler.io", window.location.origin) + } + + // Check if it contains Bearer APIKEY + // If so, replace apikey + //if (newprop.includes("Bearer APIKEY")) { + // newprop = newprop.replace("Bearer APIKEY", "Bearer API + //} + } + + return ( +
+ {validate.valid === true ? + + : +
+ + {newprop} + + +
+ } +
+ ) +} + const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; @@ -121,6 +238,123 @@ const Docs = (defaultprops) => { //height: "50vh", }; + const Heading = (props) => { + const element = React.createElement( + `h${props.level}`, + { style: { marginTop: props.level === 1 ? 20 : 50 } }, + props.children + ); + const [hover, setHover] = useState(false); + + var extraInfo = ""; + if (props.level === 1) { + extraInfo = ( +
+
+ {isMobile ? null : ( + + + + + + )} + {isMobile ? null : ( +
+ )} + + {selectedMeta.read_time} minute + {selectedMeta.read_time === 1 ? "" : "s"} to read + +
+
+ {isMobile || + selectedMeta.contributors === undefined || + selectedMeta.contributors === null ? ( + "" + ) : ( +
+ {selectedMeta.contributors.slice(0, 7).map((data, index) => { + return ( + + + {data.url} + + + ); + })} +
+ )} +
+
+ ); + } + + if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { + if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { + extraInfo = "" + } + } + + return ( + { + setHover(true); + }} + > + {props.level !== 1 ? ( + + ) : null} + {element} + {extraInfo} + + ) + } + const SideBar = { minWidth: 300, width: "20%", @@ -382,190 +616,7 @@ const Docs = (defaultprops) => { fontSize: isMobile ? "1.3rem" : "1.1rem", }; - function OuterLink(props) { - if (props.href.includes("http") || props.href.includes("mailto")) { - return ( - - {props.children} - - ); - } - return ( - - {props.children} - - ); - } - - function Img(props) { - return {props.alt}; - } - - function CodeHandler(props) { - //console.log("Codehandler PROPS: ", props) - - const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : "" - - return ( -
- {propvalue} -
- ); - } - - const Heading = (props) => { - const element = React.createElement( - `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 } }, - props.children - ); - const [hover, setHover] = useState(false); - - var extraInfo = ""; - if (props.level === 1) { - extraInfo = ( -
-
- {mobile ? null : ( - - - - - - )} - {mobile ? null : ( -
- )} - - {selectedMeta.read_time} minute - {selectedMeta.read_time === 1 ? "" : "s"} to read - -
-
- {mobile || - selectedMeta.contributors === undefined || - selectedMeta.contributors === null ? ( - "" - ) : ( -
- {selectedMeta.contributors.slice(0, 7).map((data, index) => { - return ( - - - {data.url} - - - ); - })} -
- )} -
-
- ); - } - - if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { - if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { - extraInfo = "" - } - } - - return ( - { - setHover(true); - }} - > - {props.level !== 1 ? ( - - ) : null} - {element} - {/*hover ? {setHover(true)}} style={{cursor: "pointer", display: "inline", }} onClick={() => { - window.location.href += "#hello" - console.log(window.location) - //window.history.pushState('page2', 'Title', '/page2.php'); - //window.history.replaceState('page2', 'Title', '/page2.php'); - }} /> - : "" - */} - {extraInfo} - - ); - }; - //React.createElement("p", {style: {color: "red", backgroundColor: "blue"}}, this.props.paragraph) - - //function unicodeToChar(text) { - // return text.replace(/\\u[\dA-F]{4}/gi, - // function (match) { - // return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16)); - // } - // ); - //} + const CustomButton = (props) => { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 7f78244a..0cff5f0f 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -484,7 +484,6 @@ export const validateJson = (showResult) => { // This is where we start recursing if (jsonvalid) { // Check fields if they can be parsed too - //console.log("In this window for the data. Should look for list in result! Does recursion.") try { for (const [key, value] of Object.entries(result)) { if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) { @@ -1649,6 +1648,7 @@ const Workflows = (props) => { addFilter(e.target.innerHTML); }; + const hasWorkflows = workflows === undefined || workflows === null || workflows.length === 0 const NewWorkflowPaper = () => { const [hover, setHover] = React.useState(false); @@ -1659,17 +1659,17 @@ const Workflows = (props) => { minWidth: paperAppStyle.width, color: innerColor, padding: paperAppStyle.padding, - borderRadius: paperAppStyle.borderRadius, display: "flex", boxSizing: "border-box", position: "relative", - border: `2px solid ${innerColor}`, + border: hasWorkflows ? `2px solid #f85a3e` : `2px solid ${innerColor}`, cursor: "pointer", backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)", + borderRadius: paperAppStyle.borderRadius, }; return ( - + { }} > - + + + New Workflow + @@ -3328,8 +3331,8 @@ const Workflows = (props) => {
-
- {!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ? +
+ {!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
{usecases.map((usecase, index) => { //console.log(usecase) From 0a349019abb2f53059cb2d5ad0e19bcb2e8833ee Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 7 Feb 2024 18:46:19 +0100 Subject: [PATCH 006/142] Changed app sdk order for apps --- backend/app_sdk/app_base.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 538d3afe..51690770 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1107,8 +1107,8 @@ class AppBase: else: raise Exception(json.dumps({ "success": False, - "reason": "You may be running an old version of this action. Please delete and remake the node.", "exception": f"TypeError: {e}", + "reason": "You may be running an old version of this action. Please delete and remake the node.", })) break @@ -3381,8 +3381,8 @@ class AppBase: if check: raise Exception(json.dumps({ "success": False, - "reason": "Parameter {parameter} has an issue", "exception": f"Value Error: {check}", + "reason": "Parameter {parameter} has an issue", })) #if parameter["name"] == "body": @@ -3791,8 +3791,8 @@ class AppBase: future.cancel() newres = json.dumps({ "success": False, - "reason": "Timeout error within %d seconds (1). This happens if we can't reach or use the API you're trying to use within the time limit. Configure SHUFFLE_APP_SDK_TIMEOUT=100 in Orborus to increase it to 100 seconds. Not changeable for cloud." % timeout, "exception": str(e), + "reason": "Timeout error within %d seconds (1). This happens if we can't reach or use the API you're trying to use within the time limit. Configure SHUFFLE_APP_SDK_TIMEOUT=100 in Orborus to increase it to 100 seconds. Not changeable for cloud." % timeout, }) else: @@ -3824,8 +3824,8 @@ class AppBase: newres = json.dumps({ "success": False, - "reason": "An exception occurred while running this function (1). See exception for more details and contact support if this persists (support@shuffler.io)", "exception": f"{type(e).__name__} - {e}", + "reason": "An exception occurred while running this function (1). See exception for more details and contact support if this persists (support@shuffler.io)", }) break elif "got an unexpected keyword argument" in errorstring: @@ -3841,8 +3841,8 @@ class AppBase: else: newres = json.dumps({ "success": False, - "reason": "You may be running an old version of this action. Try remaking the node, then contact us at support@shuffler.io if it doesn't work with all these details.", "exception": f"TypeError: {e}", + "reason": "You may be running an old version of this action. Try remaking the node, then contact us at support@shuffler.io if it doesn't work with all these details.", }) break except Exception as e: @@ -3855,8 +3855,8 @@ class AppBase: newres = json.dumps({ "success": False, - "reason": "An exception occurred while running this function (2). See exception for more details and contact support if this persists (support@shuffler.io)", "exception": f"{type(e).__name__} - {e}", + "reason": "An exception occurred while running this function (2). See exception for more details and contact support if this persists (support@shuffler.io)", }) break From bf776409bdf869e4130c0e7d7f9e57324922dc71 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 8 Feb 2024 00:23:30 +0100 Subject: [PATCH 007/142] Added basic image distribution on app rebuild to be sent to orborus and handled --- backend/go-app/go.mod | 10 +- backend/go-app/go.sum | 91 +++---------- backend/go-app/main.go | 20 +++ functions/onprem/orborus/orborus.go | 194 +++++++++++++++++++--------- 4 files changed, 175 insertions(+), 140 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index a3fcf9d6..98f8f418 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared go 1.19 @@ -39,11 +39,10 @@ require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/acomagu/bufpipe v1.0.4 // indirect github.com/adrg/strutil v0.2.3 // indirect github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect github.com/bitly/go-simplejson v0.5.0 // indirect - github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 // indirect + github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/containerd v1.6.18 // indirect @@ -53,6 +52,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/frikky/schemaless v0.0.5 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -69,7 +69,6 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.10.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect - github.com/imdario/mergo v0.3.15 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -89,6 +88,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/sashabaranov/go-openai v1.19.2 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/skeema/knownhosts v1.2.1 // indirect @@ -106,7 +106,7 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index f119a704..3bdc7d06 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -65,20 +65,14 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= @@ -110,11 +104,10 @@ github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUK github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw= github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw= -github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 h1:9bAydALqAjBfPHd/eAiJBHnMZUYov8m2PkXVr+YGQeI= github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA14J0sA3Hph4dt+AfCjPrYR13+vVodshQSM7km9qw= @@ -126,7 +119,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -162,7 +154,7 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -184,6 +176,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= +github.com/frikky/schemaless v0.0.5 h1:ptQ0FpQpm/+e7HCYt7Wmc+vcMSelTYCRpKqtLVN2SOY= +github.com/frikky/schemaless v0.0.5/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= @@ -195,14 +189,11 @@ github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= -github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -211,8 +202,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -274,7 +263,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -305,7 +293,6 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -332,8 +319,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -354,20 +339,17 @@ github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6 github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/libgit2/git2go/v34 v34.0.0/go.mod h1:blVco2jDAw6YTXkErMMqzHLcAjKkwF0aWIRHBqiJkZ0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= @@ -430,6 +412,7 @@ github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3 github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -460,55 +443,23 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= +github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.4.66 h1:Aw4qOp0VsVJrRzW1sJhEy4OY4fRGlFErUD5+93RXL6g= -github.com/shuffle/shuffle-shared v0.4.66/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.4.80 h1:03OL+O8prwL9zq6Gnb9SRORPWi5+ThO0jPoxk+xctOo= -github.com/shuffle/shuffle-shared v0.4.80/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.4.95 h1:xr92/03/uQeJiDme9S8/vgF1KWyQgJ1KQXVE7nQMKis= -github.com/shuffle/shuffle-shared v0.4.95/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.4.96 h1:iaIB/HP9eKpw9DMMJZhSLDbKdHJt075kFYLHg9AaiiM= -github.com/shuffle/shuffle-shared v0.4.96/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.4.97 h1:1c8LdNteMykKNEV97vwP63oSP2tV/Uso3O4TC+oxdFQ= -github.com/shuffle/shuffle-shared v0.4.97/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.4.98 h1:pgsLdWUpxZ/q+eHpAjOCH9icOsmuO5u2olmirOldy5A= -github.com/shuffle/shuffle-shared v0.4.98/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.11 h1:Eqbs9o8E49QAL5/6aV6BfFtWSjLIvgET7AL3fa4OQTg= -github.com/shuffle/shuffle-shared v0.5.11/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.14 h1:d14u1e4k+qKgnf4Insq4x2S+0MMKlDqdyTTyVP3puRA= -github.com/shuffle/shuffle-shared v0.5.14/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.29 h1:n4vThl7v3mFVXbrIW71XREFdmZZo7mOBAWxnsdiNjDk= -github.com/shuffle/shuffle-shared v0.5.29/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.30 h1:ORWjQU3UJhdZY5mRsAoR2hvftNcJmiEekNKjbaMo7K8= -github.com/shuffle/shuffle-shared v0.5.30/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.31 h1:OV4IIfKWWFW66WjGvyXOmmsSz3p8pW9L1ge1mDo8ftM= -github.com/shuffle/shuffle-shared v0.5.31/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.44 h1:6WiFPIsij+IWvXY7vzVX7cUicb+PYOzhTbWF/gDmYeU= -github.com/shuffle/shuffle-shared v0.5.44/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= -github.com/shuffle/shuffle-shared v0.5.60 h1:R0BWYp/DlgyNPU0xVNSPhsWBnJ+LeVD3iLoDRZmtKMo= -github.com/shuffle/shuffle-shared v0.5.60/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0= -github.com/shuffle/shuffle-shared v0.5.61 h1:Fkd7pvk8ypwaio1VvMOmJ6PQF9xCfNYx8RWjPnXjn5E= -github.com/shuffle/shuffle-shared v0.5.61/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0= -github.com/shuffle/shuffle-shared v0.5.62 h1:L1la7++aqPLPsh1z3cuGIzH+NTuvBlmEU/T2mQvfEk8= -github.com/shuffle/shuffle-shared v0.5.62/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0= -github.com/shuffle/shuffle-shared v0.5.65 h1:x4ZM+e0LK21rRtG0xbtnUb+6qU08+zNBQ6azUyhn8ck= -github.com/shuffle/shuffle-shared v0.5.65/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= -github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -534,8 +485,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -570,7 +521,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -581,8 +531,6 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -622,7 +570,6 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -682,8 +629,6 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -709,8 +654,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -751,14 +696,12 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -780,7 +723,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -798,8 +740,6 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -818,7 +758,6 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= @@ -885,7 +824,6 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= @@ -926,8 +864,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index e291c9ae..b2e901d5 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3039,6 +3039,26 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s return } + if len(user.ActiveOrg.Id) > 0 { + org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) + if err != nil { + log.Printf("[ERROR] Failed getting org during image build (%s): %s", user.ActiveOrg.Id, err) + } else { + log.Printf("[INFO] Successfully uploaded app %s to org %s (2). Validating and distributing image to available environments in org.", api.ID, org.Id) + + imagenames := []string{ + fmt.Sprintf("%s_%s", api.Name, api.AppVersion), + fmt.Sprintf("%s_%s", api.Name, test.Id), + } + + err = shuffle.DistributeAppToEnvironments(ctx, *org, imagenames) + if err != nil { + log.Printf("[ERROR] Failed distributing app to environments: %s", err) + } + } + } + + log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID) if len(user.Id) > 0 { resp.WriteHeader(200) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 0b8c08f3..14cba361 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -627,6 +627,43 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar { return envVars } + +func handleBackendImageDownload(ctx context.Context, images string) error { + // Should use docker to: + // 1. Pull the image & tag it + // 2. Distribute the image by updating service if "run" + if swarmConfig == "run" || swarmConfig == "swarm" { + log.Printf("[DEBUG] Should update service with new image after updating(s): %s. \n\nNOT IMPLEMENTED: Contact support@shuffler.io for support.\n\n", images) + + // 1. Download the image + // 2. Find the existing service using the image + // 3. Update the service with the new image in a rolling restart + } else { + log.Printf("[DEBUG] Should remove existing image (s): %s", images) + + // Remove the image + removeOptions := types.ImageRemoveOptions{ + } + + for _, image := range strings.Split(images, ",") { + image = strings.TrimSpace(image) + if !strings.Contains(image, "/") { + image = fmt.Sprintf("frikky/shuffle:%s", image) + } + + resp, err := dockercli.ImageRemove(ctx, image, removeOptions) + if err != nil { + log.Printf("[ERROR] Failed removing image: %s", err) + } else { + log.Printf("[DEBUG] Removed image: %s", resp) + } + } + } + + return nil +} + + func deployWorker(image string, identifier string, env []string, executionRequest shuffle.ExecutionRequest) error { if isKubernetes == "true" { @@ -1222,6 +1259,65 @@ func getKubernetesClient() (*kubernetes.Clientset, error) { } } + +func sendRemoveRequest(client *http.Client, toBeRemoved shuffle.ExecutionRequestWrapper, baseUrl, environment, auth, org string, sleepTime int) error { + confirmUrl := fmt.Sprintf("%s/api/v1/workflows/queue/confirm", baseUrl) + + data, err := json.Marshal(toBeRemoved) + if err != nil { + log.Printf("[WARNING] Failed removal marshalling: %s", err) + time.Sleep(time.Duration(sleepTime) * time.Second) + return err + } + + result, err := http.NewRequest( + "POST", + confirmUrl, + bytes.NewBuffer([]byte(data)), + ) + + if err != nil { + log.Printf("[ERROR] Failed building confirm request: %s", err) + time.Sleep(time.Duration(sleepTime) * time.Second) + return err + } + + result.Header.Add("Content-Type", "application/json") + result.Header.Add("Org-Id", environment) + + if len(auth) > 0 { + result.Header.Add("Authorization", auth) + } + + if len(org) > 0 { + result.Header.Add("Org", org) + } + + if len(orborusLabel) > 0 { + result.Header.Add("X-Orborus-Label", orborusLabel) + } + + resultResp, err := client.Do(result) + if err != nil { + log.Printf("[ERROR] Failed making confirm request: %s", err) + time.Sleep(time.Duration(sleepTime) * time.Second) + return err + } + + defer resultResp.Body.Close() + body, err := ioutil.ReadAll(resultResp.Body) + if err != nil { + log.Printf("[ERROR] Failed reading confirm body: %s", err) + time.Sleep(time.Duration(sleepTime) * time.Second) + return err + } + + _ = body + //log.Printf("[DEBUG] Confirm response: %s", string(body)) + + return nil +} + func cleanup() { log.Printf("[INFO] Cleaning up during shutdown") ctx := context.Background() @@ -1495,11 +1591,48 @@ func main() { continue } + if hasStarted && len(executionRequests.Data) > 0 { //log.Printf("[INFO] Body: %s", string(body)) // Type string `json:"type"` } + // FIXME: Add features here for orborus & worker to + // do things on behalf of backend + var toBeRemoved shuffle.ExecutionRequestWrapper + if len(executionRequests.Data) > 0 { + newrequests := []shuffle.ExecutionRequest{} + for _, incRequest := range executionRequests.Data { + // Looking for specific jobs + if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" { + log.Printf("[INFO] Should delete -> download new image %#v", incRequest.ExecutionArgument) + + if len(incRequest.ExecutionArgument) > 0 { + err = handleBackendImageDownload(ctx, incRequest.ExecutionArgument) + if err != nil { + log.Printf("[ERROR] Failed handling image delete -> download: %s", err) + } + + } + toBeRemoved.Data = append(toBeRemoved.Data, incRequest) + } else { + newrequests = append(newrequests, incRequest) + } + } + + if len(toBeRemoved.Data) > 0 { + err = sendRemoveRequest(client, toBeRemoved, baseUrl, environment, auth, org, sleepTime) + if err != nil { + log.Printf("[ERROR] Failed sending remove request: %s", err) + } else { + toBeRemoved.Data = []shuffle.ExecutionRequest{} + } + } + + // Remove the download image request + executionRequests.Data = newrequests + } + // Skipping throttling with swarm if swarmConfig != "run" && swarmConfig != "swarm" { if len(executionRequests.Data) == 0 { @@ -1531,7 +1664,6 @@ func main() { } // New, abortable version. Should check executionid and remove everything else - var toBeRemoved shuffle.ExecutionRequestWrapper for _, execution := range executionRequests.Data { if len(execution.ExecutionArgument) > 0 { log.Printf("[INFO] Argument: %s", execution.ExecutionArgument) @@ -1646,68 +1778,12 @@ func main() { // Removes handled workflows (worker is made) //log.Printf("\n\n[INFO] Removing %d executions from queue\n\n", len(toBeRemoved.Data)) if len(toBeRemoved.Data) > 0 { - confirmUrl := fmt.Sprintf("%s/api/v1/workflows/queue/confirm", baseUrl) - data, err := json.Marshal(toBeRemoved) + err = sendRemoveRequest(client, toBeRemoved, baseUrl, environment, auth, org, sleepTime) if err != nil { - log.Printf("[WARNING] Failed removal marshalling: %s", err) - time.Sleep(time.Duration(sleepTime) * time.Second) - continue + log.Printf("[ERROR] Failed to remove executions from queue: %s", err) } - result, err := http.NewRequest( - "POST", - confirmUrl, - bytes.NewBuffer([]byte(data)), - ) - - if err != nil { - log.Printf("[ERROR] Failed building confirm request: %s", err) - time.Sleep(time.Duration(sleepTime) * time.Second) - continue - } - - result.Header.Add("Content-Type", "application/json") - result.Header.Add("Org-Id", environment) - - if len(auth) > 0 { - result.Header.Add("Authorization", auth) - } - - if len(org) > 0 { - result.Header.Add("Org", org) - } - - if len(orborusLabel) > 0 { - result.Header.Add("X-Orborus-Label", orborusLabel) - } - - resultResp, err := client.Do(result) - if err != nil { - log.Printf("[ERROR] Failed making confirm request: %s", err) - time.Sleep(time.Duration(sleepTime) * time.Second) - continue - } - - defer resultResp.Body.Close() - body, err := ioutil.ReadAll(resultResp.Body) - if err != nil { - log.Printf("[ERROR] Failed reading confirm body: %s", err) - time.Sleep(time.Duration(sleepTime) * time.Second) - continue - } - - _ = body - //log.Println(string(body)) - - // FIXME - remove these - //log.Println(string(body)) - //log.Println(resultResp) - if len(toBeRemoved.Data) == len(executionRequests.Data) { - //log.Println("Should remove ALL!") - } else { - //log.Printf("[INFO] NOT IMPLEMENTED: Should remove %d workflows from backend because they're executed!", len(toBeRemoved.Data)) - } } time.Sleep(time.Duration(sleepTime) * time.Second) From 858d18c7dceaa8594ea88a13aeaf81f76ec2b5a4 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 15 Feb 2024 18:53:39 +0100 Subject: [PATCH 008/142] A lot of things --- backend/app_sdk/app_base.py | 2 - backend/go-app/walkoff.go | 4 +- frontend/src/components/AppFramework.jsx | 263 ++++++++------- frontend/src/components/AppSearchButtons.jsx | 12 +- frontend/src/components/ConfigureWorkflow.jsx | 5 +- frontend/src/components/EditWorkflow.jsx | 4 +- frontend/src/components/ParsedAction.jsx | 56 ++-- frontend/src/components/RuntimeDebugger.jsx | 18 +- .../src/components/WorkflowTemplatePopup.jsx | 62 +++- frontend/src/views/Admin.jsx | 8 +- frontend/src/views/AngularWorkflow.jsx | 314 ++++++++++++++---- frontend/src/views/Dashboard.jsx | 21 +- 12 files changed, 512 insertions(+), 257 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 51690770..1ae7049c 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1914,7 +1914,6 @@ class AppBase: c_parentheses = parse_nested_param(parse_string, 0)[0] match_string = re.escape(c_parentheses) custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string) - print("[DEBUG] In ELSE: %s" % custom_casting) # check if a wrapper was found if len(custom_casting) != 0: inner_result = parse_type(c_parentheses, custom_casting[0]) @@ -2093,7 +2092,6 @@ class AppBase: except IndexError: newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) else: - #print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) if isinstance(firstitem, str): if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end": firstitem = len(basejson)-1 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index c1e71f2f..a9d72774 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -552,7 +552,9 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { ctx := context.Background() workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) if err != nil { - log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err) + if len(actionResult.ExecutionId) > 0 { + log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err) + } resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) return diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 383553fd..fbdf7fa0 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -40,12 +40,12 @@ cytoscape.use(edgehandles); export const findSpecificApp = (framework, inputcategory) => { // Get the frameworkinfo for the org and fill in if (framework === undefined || framework === null) { - console.log("findSpecificApp: framework is null") + //console.log("findSpecificApp: framework is null") return null } if (inputcategory === undefined || inputcategory === null) { - console.log("findSpecificApp: category is null") + //console.log("findSpecificApp: category is null") return null } @@ -118,8 +118,8 @@ export const findSpecificApp = (framework, inputcategory) => { } return { - name: "EDR :default", - large_image: parsedDatatypeImages["EDR & AV"], + name: "IAM :default", + large_image: parsedDatatypeImages["IAM"], count: 0, description: "", id: "", @@ -654,7 +654,7 @@ const AppFramework = (props) => { const handleLoadNextSuggestion = (frameworkData) => { - console.log("Should check for next apps to load from App suggestion model") + //console.log("Should check for next apps to load from App suggestion model") //fetch(globalUrl + "/api/v1/workflows/usecases", { //credentials: "include", //cors: "no-cors", @@ -720,7 +720,7 @@ const AppFramework = (props) => { } const showRecommendations = (changed, frameworkData) => { - console.log("Inside recommendation loader") + //console.log("Inside recommendation loader") setChangedApp(changed) handleLoadNextSuggestion(frameworkData) @@ -761,7 +761,7 @@ const AppFramework = (props) => { //console.log("APptype, changed, framework: ", apptype.toLowerCase(), alternativeChanged.toLowerCase(), changed.toLowerCase(), frameworkData) if (changed.toLowerCase() === apptype.toLowerCase() || changed.toLowerCase().includes(apptype.toLowerCase()) || alternativeChanged.toLowerCase() === apptype.toLowerCase() || alternativeChanged.toLowerCase().includes(apptype.toLowerCase())) { potential = true - console.log("Potential: !", apptype) + //console.log("Potential: !", apptype) if (frameworkData[apptype] !== undefined && frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) { usecase.items[itemtype].app = frameworkData[apptype] @@ -819,10 +819,10 @@ const AppFramework = (props) => { continue } } else { - console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)") + //console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)") } - console.log("Usecase: ", usecase) + //console.log("Usecase: ", usecase) if (matches.length === usecase.items.length) { usecase.color = "#c51152" usecase.type = usecaseTypes[key].name @@ -833,8 +833,8 @@ const AppFramework = (props) => { } // FIXME: Check if a usecase has already been handled - console.log("") - console.log("GOT USECASES: ", showusecases) + //console.log("") + //console.log("GOT USECASES: ", showusecases) // FIXME: Just showing one usecase at a time for now if (showusecases.length > 0) { @@ -894,38 +894,37 @@ const AppFramework = (props) => { parsedUsecase.process.push(edge.data) } - fetch(globalUrl + "/api/v1/workflows/usecases", { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(parsedUsecase), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for framework!"); - } + fetch(globalUrl + "/api/v1/workflows/usecases", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(parsedUsecase), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - if (responseJson.reason !== undefined) { - toast("Failed updating: " + responseJson.reason) - } else { - toast("Failed to update framework for your org.") - } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Failed updating: " + responseJson.reason) } else { - toast("Updated usecase.") + toast("Failed to update framework for your org.") } - }) - .catch((error) => { - toast(error.toString()); - //setFrameworkLoaded(true) - }) - } + } else { + toast("Updated usecase.") + } + }) + .catch((error) => { + toast(error.toString()); + }) + } const activateApp = (appid) => { fetch(globalUrl+"/api/v1/apps/"+appid+"/activate", { @@ -957,45 +956,54 @@ const AppFramework = (props) => { } const setFrameworkItem = (data) => { - console.log("Setting framework item: ", data, isCloud) if (!isCloud) { activateApp(data.id) } - fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(data), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for framework!"); - } + fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - if (responseJson.reason !== undefined) { - toast("Failed updating: " + responseJson.reason) - } else { - toast("Failed to update framework for your org.") + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Failed getting appframework: " + responseJson.reason) + } else { + toast("Failed to update framework for your org.") - } - } + } + } else { + if (data.id === "remove") { + frameworkData[data.type] = {} + frameworkData[data.type.toLowerCase()] = {} + } else { + frameworkData[data.type] = data + frameworkData[data.type.toLowerCase()] = data + } - //setFrameworkLoaded(true) - //setFrameworkData(responseJson) - }) - .catch((error) => { - toast(error.toString()); - //setFrameworkLoaded(true) - }) - } + setDiscoveryData({}) + if (setFrameworkData !== undefined) { + setFrameworkData(frameworkData) + } + } + }) + .catch((error) => { + toast(error.toString()); + //setFrameworkLoaded(true) + }) + } useEffect(() => { if (!window.location.pathname.includes("usecases")) { @@ -1058,7 +1066,6 @@ const AppFramework = (props) => { frameworkData[keys[key]] = submitValue } - console.log("Frameworkdata: ", frameworkData) setFrameworkData(frameworkData) if (discoveryData.large_image !== undefined && discoveryData.large_image !== null && discoveryData.large_image.includes("storage.googleapis.com")) { @@ -1076,8 +1083,6 @@ const AppFramework = (props) => { const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - - const imgSize = 50; var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData @@ -1263,7 +1268,7 @@ const AppFramework = (props) => { var parsedStyle2 = { "ghost": "yes", - "border-width": "7px", + "border-width": "7px", } /* @@ -1399,7 +1404,6 @@ const AppFramework = (props) => { const onNodeSelect = (event) => { var data = event.target.data(); - console.log("Node: ", data) if (data.id === "SHUFFLE") { event.target.unselect() return @@ -1465,6 +1469,15 @@ const AppFramework = (props) => { const foundMiddleImage = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : '/images/Shuffle_logo.png' + const siemcheck = parsedFrameworkData.SIEM.large_image === undefined || (parsedFrameworkData.SIEM.name !== undefined && parsedFrameworkData.SIEM.name !== null && parsedFrameworkData.SIEM.name.includes(":default")) + const iamcheck = parsedFrameworkData.IAM.large_image === undefined || (parsedFrameworkData.IAM.name !== undefined && parsedFrameworkData.IAM.name !== null && parsedFrameworkData.IAM.name.includes(":default")) + const casescheck = parsedFrameworkData.Cases.large_image === undefined || (parsedFrameworkData.Cases.name !== undefined && parsedFrameworkData.Cases.name !== null && parsedFrameworkData.Cases.name.includes(":default")) + const assetscheck = parsedFrameworkData.Assets.large_image === undefined || (parsedFrameworkData.Assets.name !== undefined && parsedFrameworkData.Assets.name !== null && parsedFrameworkData.Assets.name.includes(":default")) + const intelcheck = parsedFrameworkData.Intel.large_image === undefined || (parsedFrameworkData.Intel.name !== undefined && parsedFrameworkData.Intel.name !== null && parsedFrameworkData.Intel.name.includes(":default")) + const commscheck = parsedFrameworkData.Comms.large_image === undefined || (parsedFrameworkData.Comms.name !== undefined && parsedFrameworkData.Comms.name !== null && parsedFrameworkData.Comms.name.includes(":default")) + const edrcheck = parsedFrameworkData["EDR & AV"].large_image === undefined || (parsedFrameworkData["EDR & AV"].name !== undefined && parsedFrameworkData["EDR & AV"].name !== null && parsedFrameworkData["EDR & AV"].name.includes(":default")) + const networkcheck = parsedFrameworkData.Network.large_image === undefined || (parsedFrameworkData.Network.name !== undefined && parsedFrameworkData.Network.name !== null && parsedFrameworkData.Network.name.includes(":default")) + const fontSize = `${12*scale}px` const defaultSize = `${85*scale}px` @@ -1484,11 +1497,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Cases.name === undefined ? "" : parsedFrameworkData.Cases.name, description: parsedFrameworkData.Cases.description === undefined ? "" : parsedFrameworkData.Cases.description, app_id: parsedFrameworkData.Cases.id === undefined ? "" : parsedFrameworkData.Cases.id, - text_margin_y: parsedFrameworkData.Cases.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Cases.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Cases.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize, + text_margin_y: casescheck ? textMarginDefault : textMarginImage, + margin_x: casescheck ? `${32*scale}px` : "0px", + margin_y: casescheck ? `${19*scale}px` : `0px`, + width: casescheck ? iconSize : defaultSize, + height: casescheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image, label: securityFramework[0].text.toUpperCase(), @@ -1501,6 +1514,7 @@ const AppFramework = (props) => { } }, { + group: "nodes", data: { font_size: fontSize, @@ -1512,11 +1526,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.IAM.name === undefined ? "" : parsedFrameworkData.IAM.name, description: parsedFrameworkData.IAM.description === undefined ? "" : parsedFrameworkData.IAM.description, app_id: parsedFrameworkData.IAM.id === undefined ? "" : parsedFrameworkData.IAM.id, - text_margin_y: parsedFrameworkData.IAM.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.IAM.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.IAM.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize, + text_margin_y: iamcheck ? textMarginDefault : textMarginImage, + margin_x: iamcheck ? `${32*scale}px` : "0px", + margin_y: iamcheck ? `${19*scale}px` : `0px`, + width: iamcheck ? iconSize : defaultSize, + height: iamcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image, label: securityFramework[3].text.toUpperCase(), @@ -1540,11 +1554,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Assets.name === undefined ? "" : parsedFrameworkData.Assets.name, description: parsedFrameworkData.Assets.description === undefined ? "" : parsedFrameworkData.Assets.description, app_id: parsedFrameworkData.Assets.id === undefined ? "" : parsedFrameworkData.Assets.id, - text_margin_y: parsedFrameworkData.Assets.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Assets.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Assets.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize, + text_margin_y: assetscheck ? textMarginDefault : textMarginImage, + margin_x: assetscheck ? `${32*scale}px` : "0px", + margin_y: assetscheck ? `${19*scale}px` : `0px`, + width: assetscheck ? iconSize : defaultSize, + height: assetscheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image, label: securityFramework[2].text.toUpperCase(), @@ -1568,11 +1582,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Intel.name === undefined ? "" : parsedFrameworkData.Intel.name, description: parsedFrameworkData.Intel.description === undefined ? "" : parsedFrameworkData.Intel.description, app_id: parsedFrameworkData.Intel.id === undefined ? "" : parsedFrameworkData.Intel.id, - text_margin_y: parsedFrameworkData.Intel.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Intel.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Intel.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize, + text_margin_y: intelcheck ? textMarginDefault : textMarginImage, + margin_x: intelcheck ? `${32*scale}px` : "0px", + margin_y: intelcheck ? `${19*scale}px` : `0px`, + width: intelcheck ? iconSize : defaultSize, + height: intelcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image, label: securityFramework[4].text.toUpperCase(), @@ -1596,11 +1610,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Comms.name === undefined ? "" : parsedFrameworkData.Comms.name, description: parsedFrameworkData.Comms.description === undefined ? "" : parsedFrameworkData.Comms.description, app_id: parsedFrameworkData.Comms.id === undefined ? "" : parsedFrameworkData.Comms.id, - text_margin_y: parsedFrameworkData.Comms.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Comms.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Comms.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize, + text_margin_y: commscheck ? textMarginDefault : textMarginImage, + margin_x: commscheck ? `${32*scale}px` : "0px", + margin_y: commscheck ? `${19*scale}px` : `0px`, + width: commscheck ? iconSize : defaultSize, + height: commscheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image, label: securityFramework[5].text.toUpperCase(), @@ -1624,11 +1638,11 @@ const AppFramework = (props) => { name: parsedFrameworkData["EDR & AV"].name === undefined ? "" : parsedFrameworkData["EDR & AV"].name, description: parsedFrameworkData["EDR & AV"].description === undefined ? "" : parsedFrameworkData["EDR & AV"].description, app_id: parsedFrameworkData["EDR & AV"].id === undefined ? "" : parsedFrameworkData["EDR & AV"].id, - text_margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize, + text_margin_y: edrcheck ? textMarginDefault : textMarginImage, + margin_x: edrcheck ? `${32*scale}px` : "0px", + margin_y: edrcheck ? `${19*scale}px` : `0px`, + width: edrcheck ? iconSize : defaultSize, + height: edrcheck ? iconSize : defaultSize, large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image, label: securityFramework[7].text.toUpperCase(), @@ -1652,11 +1666,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Network.name === undefined ? "" : parsedFrameworkData.Network.name, description: parsedFrameworkData.Network.description === undefined ? "" : parsedFrameworkData.Network.description, app_id: parsedFrameworkData.Network.id === undefined ? "" : parsedFrameworkData.Network.id, - text_margin_y: parsedFrameworkData.Network.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Network.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Network.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize, + text_margin_y: networkcheck ? textMarginDefault : textMarginImage, + margin_x: networkcheck ? `${32*scale}px` : "0px", + margin_y: networkcheck ? `${19*scale}px` : `0px`, + width: networkcheck ? iconSize : defaultSize, + height: networkcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image, label: securityFramework[6].text.toUpperCase(), id: securityFramework[6].text.toUpperCase(), @@ -1679,11 +1693,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.SIEM.name === undefined ? "" : parsedFrameworkData.SIEM.name, description: parsedFrameworkData.SIEM.description === undefined ? "" : parsedFrameworkData.SIEM.description, app_id: parsedFrameworkData.SIEM.id === undefined ? "" : parsedFrameworkData.SIEM.id, - text_margin_y: parsedFrameworkData.SIEM.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.SIEM.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.SIEM.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize, + text_margin_y: siemcheck ? textMarginDefault : textMarginImage, + margin_x: siemcheck ? `${32*scale}px` : "0px", + margin_y: siemcheck ? `${19*scale}px` : `0px`, + width: siemcheck ? iconSize : defaultSize, + height: siemcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image, label: securityFramework[1].text.toUpperCase(), id: securityFramework[1].text.toUpperCase(), @@ -1988,6 +2002,7 @@ const AppFramework = (props) => { const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color return (
+ {/*
{ inputSearch={changedApp} apps={apps} /> -
- {injectedApps.map((apps, appindex) => { +
+ */} + + {/*injectedApps.map((apps, appindex) => { var categoryTop = 100 var categoryLeft = 100 @@ -2028,8 +2045,11 @@ const AppFramework = (props) => { return null } + const scale = 0.9 + const offsetTop = -70 + const offsetLeft = 0 return ( -
+
{apps.map((app, appIndex) => { return ( { } if (setFrameworkData !== undefined) { - console.log("Setting frameworkdata") // Find discoveryData.id var keys = [] for (const [key, value] of Object.entries(frameworkData)) { @@ -2088,7 +2107,7 @@ const AppFramework = (props) => { })}
) - })} + })*/} {showOptions === false ? null :
diff --git a/frontend/src/components/AppSearchButtons.jsx b/frontend/src/components/AppSearchButtons.jsx index 553b7e41..262843ad 100644 --- a/frontend/src/components/AppSearchButtons.jsx +++ b/frontend/src/components/AppSearchButtons.jsx @@ -37,7 +37,7 @@ import { } from '@mui/material'; const AppSearchButtons = (props) => { - const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props + const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, getAppFramework, } = props const ref = useRef() let navigate = useNavigate(); @@ -48,8 +48,6 @@ const AppSearchButtons = (props) => { useEffect(() => { if (newSelectedApp !== undefined && setMissing != undefined) { - console.log("AppSearchButtons: setMissing is defined!") - const submitAppFramework = { "description": newSelectedApp.description, "id": newSelectedApp.objectID, @@ -60,11 +58,17 @@ const AppSearchButtons = (props) => { setFrameworkItem(submitAppFramework) setMissing(newSelectedApp) + + if (getAppFramework !== undefined) { + setTimeout(() => { + getAppFramework() + }, 1000) + } } }, [newSelectedApp]) if (appType === undefined) { - console.log("Apptype is required in AppSearchButtons") + //console.log("Apptype is required in AppSearchButtons") return null; } diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 81e9fd3d..ef727fde 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -77,10 +77,8 @@ const ConfigureWorkflow = (props) => { const [checkStarted, setCheckStarted] = React.useState(false); useEffect(() => { - console.log("Required actions: ", requiredActions) + //console.log("Configure Workflow: Required actions: ", requiredActions) if (requiredActions.length === 0) { - console.log("No more actions? Set parent to done?") - if (setConfigurationFinished !== undefined) { setConfigurationFinished(true) } @@ -731,6 +729,7 @@ const ConfigureWorkflow = (props) => { parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " "); + console.log("AUTH Action: ", action) return ( { const newWorkflow = isEditing === true ? false : true const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) - console.log("PRIO: ", priority) - var upload = ""; var total_count = 0 @@ -485,7 +483,7 @@ const EditWorkflow = (props) => { - +
} - {(missingSource !== undefined || missingDestination !== undefined) ? + {(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ? - {"Find relevevant Apps for this Usecase"} + {"Find relevant Apps for this Usecase"} : null} @@ -491,6 +525,8 @@ const WorkflowTemplatePopup = (props) => { AppImage={missingSource.image} setMissing={setMissingSource} + + getAppFramework={getAppFramework} />
: null} @@ -505,6 +541,8 @@ const WorkflowTemplatePopup = (props) => { AppImage={missingDestination.image} setMissing={setMissingDestination} + + getAppFramework={getAppFramework} />
: null} diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index f653bd2c..39e7fa25 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -4406,7 +4406,7 @@ If you're interested, please let me know a time that works for you, or set up a /> setDefaultEnvironment(environment)} color="primary" > diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 2f461690..fc7aae3c 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -13362,6 +13362,67 @@ const AngularWorkflow = (defaultprops) => {

{workflow.name}

+ + {workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id ? null : + + Warning: Change { + toast("Changing to correct organization. Please wait a few seconds.") + + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + fetch(`${globalUrl}/api/v1/orgs/${workflow.org_id}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify({"org_id": workflow.org_id}), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + console.log("In here?") + if (responseJson.success === true) { + if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { + console.log("Region Change: ", responseJson.region_url); + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + setTimeout(() => { + window.location.reload(); + }, 2000); + + toast("Successfully changed active organization - refreshing!"); + } else { + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + toast(responseJson.reason); + } else { + toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); + } + } + }) + .catch((error) => { + console.log("error changing: ", error); + //removeCookie("session_token", {path: "/"}) + }) + + + + }} + >Active Organization to edit this Workflow. + + }
{parentWorkflows.slice(0,5).map((wf, index) => { @@ -13740,9 +13801,64 @@ const AngularWorkflow = (defaultprops) => {
: null + + const RightsideBar = () => { const [hovered, setHovered] = useState(false) + useEffect(() => { + const handleKeyDown = (event) => { + if ((event.metaKey || event.ctrlKey) && event.key === '/') { + event.preventDefault(); // Prevent default browser behavior (like opening search bar) + if (!workflow.public && !executionRequestStarted) { + executeWorkflow(executionText, workflow.start, lastSaved); + } + } + if ((event.ctrlKey || event.metaKey) && event.key === "'") { + // Check if Ctrl (Windows/Linux) or Command (Mac) key is pressed along with '/' + if (!workflow.public && !executionModalOpen) { + setExecutionModalOpen(true); + getWorkflowExecution(props.match.params.key, ""); + } else if (!workflow.public && executionModalOpen) { + setExecutionModalOpen(false); + } + } + + if ((event.ctrlKey || event.metaKey) && event.key === "]") { + console.log("Show workflow revisions key pressed") + if (!workflow.public) { + setShowWorkflowRevisions(true) + setSelectedRevision(workflow) + //setOriginalWorkflow(workflow) + } + } + + if (( event.ctrlKey || event.metaKey ) && event.key === ";") { + if (!workflow.public && executionModalOpen) { + getWorkflowExecution(props.match.params.key, ""); + } + } + + if (( event.ctrlKey || event.metaKey ) && event.shiftKey) { + console.log("Shift key pressed") + if (!workflow.public && executionModalOpen) { + setExecutionRunning(false); + stop() + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const newitem = removeParam("execution_id", cursearch); + navigate(curpath + newitem) + setExecutionModalView(0); + } + } + }; + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]); + return (
{ ) : ( - +
+ + {
) : (
- - { - setExecutionRunning(false); - stop(); - getWorkflowExecution(props.match.params.key, ""); - setExecutionModalView(0); - setLastExecution(executionData.execution_id); - }} + + - { - setExecutionRunning(false); - stop() - }} - > - - -

{ - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; - const newitem = removeParam("execution_id", cursearch); - navigate(curpath + newitem) - setExecutionRunning(false); - stop() - }} - > - See more runs -

-
-
+ { + setExecutionRunning(false); + stop(); + getWorkflowExecution(props.match.params.key, ""); + setExecutionModalView(0); + setLastExecution(executionData.execution_id); + }} + > + { + setExecutionRunning(false); + stop() + }} + > + + + +

{ + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const newitem = removeParam("execution_id", cursearch); + navigate(curpath + newitem) + setExecutionRunning(false); + stop() + }} + > + See more runs +

+
+
+ { color="primary" title="Explore logs for the workflow" placement="top" - style={{ zIndex: 50000 }} + style={{ zIndex: 50000, }} >
@@ -1713,12 +1719,15 @@ const Dashboard = (props) => { {treeKeys.length > 0 ? From d2b9552dad286ff270edeb43317acfd18f67bd52 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 15 Feb 2024 18:53:54 +0100 Subject: [PATCH 009/142] More settings/admin fixes --- frontend/src/views/Admin.jsx | 29 +++++++------ frontend/src/views/SettingsPage.jsx | 65 +++++++++++++++++++++++++++-- frontend/src/views/Workflows.jsx | 2 +- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 39e7fa25..7bdc2fb5 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -3415,24 +3415,26 @@ If you're interested, please let me know a time that works for you, or set up a // using request id or trace id - diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index 2b0c1ebd..be99104c 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -13,6 +13,11 @@ import { //import { useAlert import { ToastContainer, toast } from "react-toastify" +import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material"; +import IconButton from "@mui/material/IconButton"; +import { Tooltip } from "@mui/material"; + + const Settings = (props) => { const { globalUrl, isLoaded, userdata, setUserData } = props; //const alert = useAlert(); @@ -44,6 +49,17 @@ const Settings = (props) => { const [userSettings, setUserSettings] = useState({}); + const [showApiKey, setShowApiKey] = useState(false); + const [apiKeyCopied, setApiKeyCopied] = useState(false); + + const handleCopyApiKey = () => { + navigator.clipboard.writeText(userSettings.apikey); + setApiKeyCopied(true); + setTimeout(() => { + setApiKeyCopied(false); + }, 2000); + } + /* const [userdata.eth_info, setEthInfo] = useState(userdata.eth_info !== undefined && userdata.eth_info.account !== undefined && userdata.eth_info.account.length > 0 ? userdata.eth_info : { "account": "", @@ -467,7 +483,7 @@ const Settings = (props) => { > What is the API key used for? - { id="standard-required" margin="normal" variant="outlined" - /> + /> */} + + + setShowApiKey(!showApiKey)} + > + {showApiKey ? : } + + + + + + + + + ), + }} + color="primary" + value={showApiKey ? userSettings.apikey : '*'.repeat(36)} // Show API key if showApiKey is true, else show asterisks + required + disabled + fullWidth + placeholder="APIKEY" + id="standard-required" + margin="normal" + variant="outlined" + />

{passwordFormMessage}

- -

Creator Incentive Program

+ {isCloud && ( + <> + +

Creator Incentive Program

+ + )} +
{isCloud ? diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 0cff5f0f..fdf23604 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -924,7 +924,7 @@ const Workflows = (props) => { >
- Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
+ Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId)?.name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
From 51935b96a8ad60153aa88a8a376e907dfed5c7eb Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 16 Feb 2024 16:32:55 +0100 Subject: [PATCH 010/142] Made caching work well for hooks --- backend/go-app/go.mod | 4 +- frontend/src/components/Priorities.jsx | 84 +++++++++++++++++++++++--- frontend/src/components/Priority.jsx | 1 - frontend/src/views/Admin.jsx | 14 ++++- 4 files changed, 89 insertions(+), 14 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 98f8f418..278ea169 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared go 1.19 @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.65 + github.com/shuffle/shuffle-shared v0.5.77 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 63c06515..bd6e5fa8 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -67,9 +67,15 @@ const Priorities = (props) => { }) } - const dismissNotification = (alert_id) => { - // Don't really care about the logout - fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { + const dismissNotification = (alert_id, disabled) => { + var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread` + if (disabled === true) { + notificationurl += "?disabled=true" + } else if (disabled === false) { + notificationurl += "?disabled=false" + } + + fetch(notificationurl , { credentials: "include", method: "GET", headers: { @@ -85,12 +91,50 @@ const Priorities = (props) => { }) .then(function (responseJson) { if (responseJson.success === true) { - const newNotifications = notifications.filter( - (data) => data.id !== alert_id - ); - console.log("NEW NOTIFICATIONS: ", newNotifications); + // Mark current one as read + var newNotifications = notifications.map((notification) => { + if (notification.id === alert_id) { + notification.read = true + } - if (setNotifications !== undefined) { + return notification + }) + + + if (disabled === true) { + toast("Notification disabled, and will not be shown again.") + + newNotifications = newNotifications.map((notification) => { + if (notification.id === alert_id) { + notification.ignored = true + } + + return notification + }) + + console.log("NEW NOTIFICATIONS: ", newNotifications); + } else if (disabled === false) { + toast("Notification re-enabled successfully") + + newNotifications = newNotifications.map((notification) => { + if (notification.id === alert_id) { + notification.ignored = false + } + + return notification + }) + + } else { + toast("Notification dismissed successfully") + } + + //const newNotifications = notifications.filter( + // (data) => data.id !== alert_id + //) + + //console.log("NEW NOTIFICATIONS: ", newNotifications); + + if (setNotifications !== undefined && newNotifications !== undefined) { setNotifications(newNotifications) } } else { @@ -174,6 +218,14 @@ const Priorities = (props) => { style={{marginRight: 15, height: 25, }} /> : null} + {data.ignored === true ? + + : null} {data.read === false ? { Dismiss ) : null} + + + diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 13c278d3..60e4bce2 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -32,7 +32,6 @@ const Priority = (props) => { let newdescription = priority.description const descsplit = priority.description.split("&") if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) { - console.log("descsplit: ", descsplit) if (descsplit[1] === "") { const item = findSpecificApp(appFramework, descsplit[0]) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 7bdc2fb5..ce774122 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1867,16 +1867,24 @@ If you're interested, please let me know a time that works for you, or set up a {selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
- Only the name of the auth can be modified for Oauth2. Please remake the authentication to change the fields like Client ID, Secret, Scopes etc. + Only the name and url can be modified for Oauth2/OpenID connect. Please remake the authentication if you want to change the other fields like Client ID, Secret, Scopes etc.
- : - selectedAuthentication.fields.map((data, index) => { + : null } + + {selectedAuthentication.fields.map((data, index) => { var fieldname = data.key.replaceAll("_", " ") if (fieldname.endsWith(" basic")) { fieldname = fieldname.substring(0, fieldname.length - 6) } + if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") { + if (selectedAuthentication.fields[index].key !== "url") { + return null + } + } + + //console.log("DATA: ", data, selectedAuthentication) return (
From ce5e89e292133647c9d1a7930e7f7154c7e4a357 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Sat, 17 Feb 2024 10:15:54 +0000 Subject: [PATCH 011/142] added notification workflow stuff --- frontend/src/components/OrgHeaderexpanded.jsx | 465 +++++++++++++++++- 1 file changed, 464 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 268350a2..7729057d 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -2,7 +2,9 @@ import React, { useEffect } from "react"; import { makeStyles } from "@mui/styles"; import theme from '../theme.jsx'; -import { toast } from "react-toastify" +import { toast } from "react-toastify" +import Chip from '@mui/material/Chip'; +import Stack from '@mui/material/Stack'; import { FormControl, @@ -25,6 +27,11 @@ import { Grid, IconButton, Autocomplete, + Dialog, + DialogTitle, + DialogActions, + DialogContent, + Box } from "@mui/material"; import { @@ -158,6 +165,19 @@ const OrgHeaderexpanded = (props) => { const [workflows, setWorkflows] = React.useState([]) const [workflow, setWorkflow] = React.useState({}) + // notification workflow + const [notificationApp, setNotificationApp] = React.useState("") + const [notificationWorkflowModal, setNotificationWorkflowModal] = React.useState(false); + const [selectedAppDetails, setSelectedAppDetails] = React.useState({}); + const [notificationWorkflowTestModal, setNotificationWorkflowTestModal] = React.useState(false); + const [webhookInputValue, setWebhookInputValue] = React.useState(""); + const [authOptions, setAuthOptions] = React.useState([]); + const [selectedAuth, setSelectedAuth] = React.useState(''); + + // for jira modal + const [jiraIssueType, setJiraIssueType] = React.useState(""); + const [jiraProjectKey, setJiraProjectKey] = React.useState(""); + const getAvailableWorkflows = (trigger_index) => { fetch(globalUrl + "/api/v1/workflows", { method: "GET", @@ -295,12 +315,455 @@ const OrgHeaderexpanded = (props) => { ); + const executeTestWorkflow = async (workflowid) => { + const data = { "execution_argument": '{"title":"THIS IS TEST ALERT","description":"TEST ALERT FROM SHUFFLE","reference_url": "shuffler.io"}' } + fetch(globalUrl + `/api/v1/workflows/${workflowid}/execute`, { + 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; + } + toast("Notification workflow ran successfully"); + return response.json(); + }).catch((error) => { + console.log("Error getting workflows: " + error); + }) +} + +const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId,issuetype) => { + //currently only supports JIRA figure out a way to support more apps + var workflowName = `[GENARATED] ${appname} notification workflow` + var workflowDescription = "Generated by Shuffle for sending 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" + } + ] + } + ] + } + } + fetch(globalUrl + `/api/v1/workflows/${workflow_id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(workflowBody), + 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) { + handleEditOrg( + orgName, + orgDescription, + selectedOrganization.id, + selectedOrganization.image, + { + app_download_repo: appDownloadUrl, + app_download_branch: appDownloadBranch, + workflow_download_repo: workflowDownloadUrl, + workflow_download_branch: workflowDownloadBranch, + notification_workflow: workflow_id, + documentation_reference: documentationReference, + }, + { + sso_entrypoint: ssoEntrypoint, + sso_certificate: ssoCertificate, + client_id: openidClientId, + client_secret: openidClientSecret, + openid_authorization: openidAuthorization, + openid_token: openidToken, + } + ) + console.log("Notification workflow updated successfully") + toast("Notification workflow updated successfully") + } + }) + } + }).catch((error) => { + console.log("Error setting workflows: " + error); + }) +} + +const getAppAuth = async (appName) => { + fetch(globalUrl + "/api/v1/apps/authentication", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + toast(`Failed getting auth for ${appName}: `, response.reason); + console.log("Status not 200 for app auth :O!"); + return; + } + return response.json(); + }).then((responseJson) => { + if (!responseJson.success) { + console.log("Could not get app auth") + return; + } + var authList = responseJson.data.filter(entry => entry.app.name === appName) + console.log("authList: ", authList) + setAuthOptions(authList); + + }).catch((error) => { + console.log("Error getting app auth: " + error); + }) +} + +const testWorkflowModal = notificationWorkflowTestModal ? + ( { + setNotificationWorkflowTestModal(false); + }} + > + + {/* +
+ Notification workflow +
+
*/} + + We have updated the Notification workflow. Do you want to test it? + + + + + +
+
) : null + +const notificationWorkflowModalValid = () => { +return selectedAuth && webhookInputValue; +}; + +const modalView = notificationWorkflowModal ? ( + { + setNotificationWorkflowModal(false); + }} + > + + +
+ {`Configure ${selectedAppDetails.name} workflow`} +
+
+ + + + {authOptions.length > 0 ? + <> + + + Pick an authentication method from the list + + + Available authentications + + + + + + Provide additional required details: + + { + setJiraProjectKey(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} /> + { + setJiraIssueType(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} /> + + : <> + + {`No ${selectedAppDetails.name} auth found. Click below to set one up.`} + + + + } + + + + + +
+
+) : null + +// getting comms & cases app from app framework +var notificationAppList = []; +if (selectedOrganization.security_framework.cases && selectedOrganization.security_framework.cases.name.length > 0) { +notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.cases); +} +if (selectedOrganization.security_framework.communication && selectedOrganization.security_framework.communication.name.length > 0) { +notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.communication); +} + +const renderChips = (apps) => { +if (!apps || apps.length === 0) { + return ( + { + console.log(`Clicked EMAIL`) + setNotificationWorkflowModal(true) + }} + avatar={{"email} + /> + ) +} + +return ( + + {apps.map((app) => ( + { + console.log(`Clicked ${app.name}`) + setSelectedAppDetails(app) + setNotificationWorkflowModal(true) + getAppAuth(app.name) + console.log(selectedAppDetails) + }} + avatar={{app.name}} + /> + ))} + +); +}; + + return (
Notification Workflow + {modalView} + {/*{testWorkflowModal} */} +
+ {renderChips(notificationAppList)}
{/* Add a Workflow that receives notifications from Shuffle when an error occurs in one of your workflows From f8640d854fc271dd3ff90a5618ade3de8f4ce6fe Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 21 Feb 2024 09:32:33 +0100 Subject: [PATCH 012/142] Fixed file category loading and small workflow changes --- backend/app_sdk/app_base.py | 2 +- backend/go-app/go.mod | 4 +- backend/go-app/go.sum | 6 +- backend/go-app/main.go | 1 + frontend/src/components/ConfigureWorkflow.jsx | 26 +- frontend/src/components/Files.jsx | 312 ++++++++++++++++-- frontend/src/components/ParsedAction.jsx | 6 - frontend/src/components/RenderCytoscape.jsx | 34 +- frontend/src/components/ShuffleCodeEditor.jsx | 32 -- .../src/components/WorkflowTemplatePopup.jsx | 39 ++- frontend/src/views/Admin.jsx | 2 - frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/Dashboard.jsx | 21 +- frontend/src/views/Workflows.jsx | 10 +- 14 files changed, 375 insertions(+), 122 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 1ae7049c..a7e1e7bf 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1212,7 +1212,7 @@ class AppBase: return results # Downloads all files from a namespace - # Currently only working on local version of Shuffle + # Currently only working on local version of Shuffle (2023) def get_file_category_ids(self, category): org_id = self.full_execution["workflow"]["execution_org"]["id"] diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 278ea169..ccfc643b 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.77 + github.com/shuffle/shuffle-shared v0.5.78 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 @@ -52,7 +52,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/frikky/schemaless v0.0.5 // indirect + github.com/frikky/schemaless v0.0.6 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 3bdc7d06..bdd5b7b7 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -176,8 +176,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= -github.com/frikky/schemaless v0.0.5 h1:ptQ0FpQpm/+e7HCYt7Wmc+vcMSelTYCRpKqtLVN2SOY= -github.com/frikky/schemaless v0.0.5/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= +github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= @@ -455,6 +455,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE= +github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b2e901d5..0eca60ba 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4953,6 +4953,7 @@ func initHandlers() { // PS: For cloud, this has to use cloud storage. // https://developer.box.com/reference/get-files-id-content/ // 1. Creating the "get file" option. Make it possible to run this in the frontend. + r.HandleFunc("/api/v1/files/download_remote", shuffle.HandleDownloadRemoteFiles).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS") diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index ef727fde..e3938c65 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -77,7 +77,6 @@ const ConfigureWorkflow = (props) => { const [checkStarted, setCheckStarted] = React.useState(false); useEffect(() => { - //console.log("Configure Workflow: Required actions: ", requiredActions) if (requiredActions.length === 0) { if (setConfigurationFinished !== undefined) { setConfigurationFinished(true) @@ -703,9 +702,9 @@ const ConfigureWorkflow = (props) => { }) .then((responseJson) => { if (!responseJson.success) { - toast("Failed to set app auth: " + responseJson.reason); + toast("Failed to set app authentication: " + responseJson.reason); } else { - toast("App auth set for app " + app.name.replace("_", " ")); + toast("App authentication set for app " + app.name.replace("_", " ")); setFinalized(true) setOpened(false) } @@ -743,6 +742,25 @@ const ConfigureWorkflow = (props) => {
{ setOpened(!opened); + + // Scroll to it + const element = document.getElementById("app-config"); + if (element) { + // Scroll down 100px + setTimeout(() => { + element.scrollIntoView({ + behavior: "smooth", + top: 100, + }) + + //element.scrollIntoView({ + // behavior: "smooth", + // block: "center", + // inline: "center" + //}); + }, 250) + } + }} >
@@ -1317,7 +1335,7 @@ const ConfigureWorkflow = (props) => { : null} - + {requiredActions.map((data, index) => { // AppWrapper = Default in a workflow, only shows with steps diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index 670cba97..73dbcfb7 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -16,6 +16,11 @@ import { Divider, Select, MenuItem, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Typography, } from "@mui/material"; import { @@ -30,9 +35,8 @@ import { Add as AddIcon, } from "@mui/icons-material"; -//import { useAlert import Dropzone from "../components/Dropzone.jsx"; -import CodeEditor from "../components/ShuffleCodeEditor.jsx"; +import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import theme from "../theme.jsx"; const Files = (props) => { @@ -45,6 +49,13 @@ const Files = (props) => { const [fileContent, setFileContent] = React.useState(""); const [openEditor, setOpenEditor] = React.useState(false); const [renderTextBox, setRenderTextBox] = React.useState(false); + const [loadFileModalOpen, setLoadFileModalOpen] = React.useState(false); + + const [field1, setField1] = React.useState(""); + const [field2, setField2] = React.useState(""); + const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards") + const [downloadBranch, setDownloadBranch] = React.useState("main"); + const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); //const alert = useAlert(); const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"] @@ -67,7 +78,7 @@ const Files = (props) => { } - const runUpdateText = (text) =>{ + const runUpdateText = (text) =>{ fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, { method: "PUT", headers: { @@ -76,17 +87,34 @@ const Files = (props) => { }, body:text, credentials: "include", - }).then((response) => { + }) + .then((response) => { if (response.status !== 200) { console.log("Can't update file"); } return response.json(); - }) - //console.log(text); + }) + .then((responseJson) => { + if (responseJson.success === true) { + toast("Successfully updated file"); + } + }) + .catch((error) => { + toast("Error updating file: " + error.toString()); + }) } - const getFiles = () => { - fetch(globalUrl + "/api/v1/files", { + const getFiles = (namespace) => { + var parsedurl = `${globalUrl}/api/v1/files` + if (namespace === undefined || namespace === "default") { + + } else if (namespace !== undefined && namespace !== null && namespace !== "") { + parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true` + } else if (selectedNamespace !== undefined && selectedNamespace !== null && selectedNamespace !== "default" && selectedNamespace !== "") { + parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedNamespace}?ids=true` + } + + fetch(parsedurl, { method: "GET", headers: { "Content-Type": "application/json", @@ -104,12 +132,23 @@ const Files = (props) => { }) .then((responseJson) => { if (responseJson.files !== undefined && responseJson.files !== null) { - setFiles(responseJson.files); - } else { + setFiles(responseJson.files); + } else if (responseJson.list !== undefined && responseJson.list !== null) { + // Set the "namespace" field in all items + if (namespace !== undefined && namespace !== null) { + responseJson.list.forEach((item) => { + item.namespace = namespace + item.filename = item.name + item.workflow_id = "global" + }) + } + + setFiles(responseJson.list); + } else { setFiles([]); } - if (responseJson.namespaces !== undefined && responseJson.namespaces !== null) { + if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileNamespaces.length === 0 || responseJson.namespaces.length > fileNamespaces.length)) { setFileNamespaces(responseJson.namespaces); } }) @@ -119,9 +158,214 @@ const Files = (props) => { }; useEffect(() => { - getFiles(); + getFiles(selectedNamespace) }, []); + const importStandardsFromUrl = (url, folder) => { + if (url === undefined || url === null || url.length < 5) { + toast("Please enter a valid URL"); + return; + } + + if (folder === undefined || folder === null || folder.length < 2) { + toast("Please enter a valid folder name") + return + } + + const parsedData = { + url: url, + path: folder, + field_3: downloadBranch || "master", + }; + + if (field1.length > 0) { + parsedData["field_1"] = field1; + } + + if (field2.length > 0) { + parsedData["field_2"] = field2; + } + + toast(`Getting files from url ${url}. This may take a while if the repository is large. Please wait...`); + fetch(globalUrl + "/api/v1/files/download_remote", { + method: "POST", + mode: "cors", + headers: { + Accept: "application/json", + }, + body: JSON.stringify(parsedData), + credentials: "include", + }) + .then((response) => { + if (response.status === 200) { + toast("Successfully loaded files from " + downloadUrl); + setLoadFileModalOpen(false); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + if (responseJson.reason !== undefined) { + toast("Failed loading: " + responseJson.reason); + } else { + toast("Failed loading"); + } + } + }) + .catch((error) => { + toast(error.toString()); + }); + } + + const handleGithubValidation = () => { + importStandardsFromUrl(downloadUrl, downloadFolder); + } + + const fileDownloadModal = loadFileModalOpen ? + {}} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: "800px", + minHeight: "320px", + }, + }} + > + +
+ Load Files from Github +
+ + Files will be loaded from the repository and branch you specify, with the focus on files in one folder at a time. This is NOT recursive. + +
+ + Repository URL (supported: github, gitlab, bitbucket) + setDownloadUrl(e.target.value)} + placeholder="https://github.com/shuffle/standards" + fullWidth + /> +
+ + + Branch (default value is "main"): + + setDownloadBranch(e.target.value)} + placeholder="master" + fullWidth + /> + + + + Folder (can use / for subfolders): + + setDownloadFolder(e.target.value)} + placeholder="translation_standards" + fullWidth + /> + +
+ + Authentication (optional - private repos etc): + +
+ setField1(e.target.value)} + type="username" + placeholder="Username / APIkey (optional)" + fullWidth + /> + setField2(e.target.value)} + type="password" + placeholder="Password (optional)" + fullWidth + /> +
+
+ + + + +
+ : null + const deleteFile = (file) => { fetch(globalUrl + "/api/v1/files/" + file.id, { method: "DELETE", @@ -140,7 +384,7 @@ const Files = (props) => { }) .then((responseJson) => { if (responseJson.success) { - toast("Successfully deleted file " + file.name); + toast("Successfully deleted file") } else if ( responseJson.reason !== undefined && responseJson.reason !== null @@ -254,7 +498,7 @@ const Files = (props) => { }); }; - const handleCreateFile = (filename, file) => { + const handleCreateFile = (filename, file) => { var data = { filename: filename, org_id: selectedOrganization.id, @@ -355,7 +599,7 @@ const Files = (props) => { } setTimeout(() => { - getFiles(); + getFiles() }, 2500); }; @@ -378,7 +622,21 @@ const Files = (props) => { }} onDrop={uploadFile} > -
+
+ + + setLoadFileModalOpen(true)} + > + + + + + {fileDownloadModal} +

Files

@@ -393,6 +651,9 @@ const Files = (props) => {
+ + + + + {fileNamespaces !== undefined && fileNamespaces !== null && fileNamespaces.length > 1 ? ( @@ -442,8 +705,13 @@ const Files = (props) => { }} value={selectedNamespace} onChange={(event) => { - console.log("CHANGE NAMESPACE: ", event.target); setSelectedNamespace(event.target.value); + + if (event.target.value === "all" || event.target.value === "default") { + getFiles() + } else { + getFiles(event.target.value) + } }} > {fileNamespaces.map((data, index) => { @@ -453,7 +721,7 @@ const Files = (props) => { value={data} style={{ color: "white" }} > - {data} + {data.replaceAll("_", " ")} ); })} @@ -487,6 +755,8 @@ const Files = (props) => { } + + {renderTextBox && { handleKeyDown(event); @@ -504,7 +774,7 @@ const Files = (props) => { autoFocus />}
- { /> @@ -615,7 +885,7 @@ const Files = (props) => { }} /> - ) : ( + : ( { - const { globalUrl, inworkflow } = props; + const { globalUrl, inworkflow, height, width } = props; const [elements, setElements] = useState([]); const [workflow, setWorkflow] = useState(inworkflow); const [cy, setCy] = React.useState(); - const bodyWidth = 200; - const bodyHeight = 150; + const bodyWidth = height === undefined ? 1000 : width + const bodyHeight = width === undefined ? 1000 : height const setupGraph = () => { const actions = workflow.actions.map((action) => { @@ -76,29 +76,6 @@ const CytoscapeWrapper = (props) => { }; // This is an attempt at prettier edges. The numbers are weird to work with. - /* - //http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html - const sourcenode = actions.find(node => node.data._id === branch.source_id) - const destinationnode = actions.find(node => node.data._id === branch.destination_id) - if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) { - //node.data._id = action["id"] - console.log("SOURCE: ", sourcenode.position) - console.log("DESTINATIONNODE: ", destinationnode.position) - - var opposite = true - if (sourcenode.position.x > destinationnode.position.x) { - opposite = false - } else { - opposite = true - } - - edge.style = { - 'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"], - 'control-point-weight': ['0.3', '0.7'], - } - } - */ - return edge; }); @@ -135,9 +112,10 @@ const CytoscapeWrapper = (props) => { elements={elements} minZoom={0.35} maxZoom={2.0} + zoom={1.0} style={{ - width: bodyWidth - 15, - height: bodyHeight - 5, + width: bodyWidth, + height: bodyHeight, backgroundColor: surfaceColor, }} stylesheet={cystyle} diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 15aec446..3b6e02a2 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -57,7 +57,6 @@ import { padding, textAlign } from '@mui/system'; import data from '../frameworkStyle.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; @@ -84,37 +83,6 @@ const pythonFilters = [ {"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``}, ] -/* -const shuffleTheme = createTheme({ - theme: 'dark', - settings: { - background: "rgba(40,40,40, 1)", - foreground: '#75baff', - caret: '#5d00ff', - selection: '#036dd626', - selectionMatch: '#036dd626', - lineHighlight: '#8a91991a', - gutterForeground: '#8a919966', - }, - styles: [ - { tag: t.comment, color: '#787b8099' }, - { tag: t.variableName, color: '#0080ff' }, - { tag: [t.string, t.special(t.brace)], color: '#5c6166' }, - { tag: t.number, color: '#5c6166' }, - { tag: t.bool, color: '#5c6166' }, - { tag: t.null, color: '#5c6166' }, - { tag: t.keyword, color: '#5c6166' }, - { tag: t.operator, color: '#5c6166' }, - { tag: t.className, color: '#5c6166' }, - { tag: t.definition(t.typeName), color: '#5c6166' }, - { tag: t.typeName, color: '#5c6166' }, - { tag: t.angleBracket, color: '#5c6166' }, - { tag: t.tagName, color: '#5c6166' }, - { tag: t.attributeName, color: '#5c6166' }, - ], -}); -*/ - const CodeEditor = (props) => { const { globalUrl, diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 08817eda..6df11b9b 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -5,6 +5,7 @@ import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import AppSearchButtons from "../components/AppSearchButtons.jsx"; import { isMobile } from "react-device-detect"; +import RenderCytoscape from "../components/RenderCytoscape.jsx"; import { Button, Typography, @@ -43,6 +44,8 @@ const WorkflowTemplatePopup = (props) => { const [missingDestination, setMissingDestination] = React.useState(undefined); const [configurationFinished, setConfigurationFinished] = React.useState(false); const [appSetupDone, setAppSetupDone] = React.useState(false) + + const [requestSent, setRequestSent] = React.useState(false) const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); @@ -294,6 +297,9 @@ const WorkflowTemplatePopup = (props) => { // middle:[] // name: "Email analysis" // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name + if (requestSent === true) { + return + } if (srcapp.includes(":default") || dstapp.includes(":default")) { @@ -331,6 +337,7 @@ const WorkflowTemplatePopup = (props) => { }, } + setRequestSent(true) const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge` fetch(url, { method: "POST", @@ -344,6 +351,7 @@ const WorkflowTemplatePopup = (props) => { .then((response) => { if (response.status !== 200) { //console.log("Status not 200 for framework!"); + setRequestSent(false) } setWorkflowLoading(false) @@ -361,6 +369,7 @@ const WorkflowTemplatePopup = (props) => { if (responseJson.success === false) { //console.log("Error in workflow template: ", responseJson.error); + setRequestSent(false) const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance." if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") { @@ -386,6 +395,7 @@ const WorkflowTemplatePopup = (props) => { }) .catch((error) => { console.log("err in framework: ", error.toString()); + setRequestSent(false) setWorkflowLoading(false) }) } @@ -422,6 +432,9 @@ const WorkflowTemplatePopup = (props) => { return null } + const divHeight = 500 + const divWidth = 500 + return ( { setConfigurationFinished={setConfigurationFinished} /> + + {/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id !== "" ? +
+ +
+ : null*/} + {errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ? - - + + { + // Open in new tab + window.open("/workflows/" + workflow.id, "_blank") + }} + > {/* */} - - Workflow generated! + + Workflow Successfully Generated! @@ -686,6 +716,7 @@ const WorkflowTemplatePopup = (props) => { : ""}
+
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index ce774122..984ebb30 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -5,8 +5,6 @@ import { makeStyles } from "@mui/styles"; import { useNavigate, Link } from "react-router-dom"; import countries from "../components/Countries.jsx"; -import CodeEditor from "../components/ShuffleCodeEditor.jsx"; -import getLocalCodeData from "../components/ShuffleCodeEditor.jsx"; import CacheView from "../components/CacheView.jsx"; import { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index fc7aae3c..30161530 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -121,7 +121,7 @@ import * as edgehandles from "cytoscape-edgehandles"; import CytoscapeComponent from "react-cytoscapejs"; import Draggable from "react-draggable"; import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; -import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; +import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index 04b53a40..e9a5c201 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -1,16 +1,11 @@ -import React, { useState, useEffect } from "react"; -import { useInterval } from "react-powerhooks"; -import AppFramework from "../components/AppFramework.jsx"; -import { makeStyles, } from "@mui/styles"; -// nodejs library that concatenates classes -import classNames from "classnames"; -import theme from '../theme.jsx'; -import { useNavigate, Link, useParams } from "react-router-dom"; -import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"; - -// react plugin used to create charts -//import { Line, Bar } from "react-chartjs-2"; -//import { useAlert +import React, { useState, useEffect } from "react" +import { useInterval } from "react-powerhooks" +import AppFramework from "../components/AppFramework.jsx" +import { makeStyles, } from "@mui/styles" +import classNames from "classnames" +import theme from '../theme.jsx' +import { useNavigate, Link, useParams } from "react-router-dom" +import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx" import { ToastContainer, toast } from "react-toastify" import { parsedDatatypeImages } from "../components/AppFramework.jsx" import { findSpecificApp } from "../components/AppFramework.jsx" diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index fdf23604..f453c98a 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -551,9 +551,7 @@ const Workflows = (props) => { const [field1, setField1] = React.useState(""); const [field2, setField2] = React.useState(""); - const [downloadUrl, setDownloadUrl] = React.useState( - "https://github.com/frikky/shuffle-workflows" - ); + const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows") const [downloadBranch, setDownloadBranch] = React.useState("master"); const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = React.useState(false); @@ -3630,7 +3628,7 @@ const Workflows = (props) => { const handleGithubValidation = () => { importWorkflowsFromUrl(downloadUrl); setLoadWorkflowsModalOpen(false); - }; + } const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( { }, }} onChange={(e) => setDownloadUrl(e.target.value)} - placeholder="https://github.com/frikky/shuffle-apps" + placeholder="https://github.com/shuffle/workflows" fullWidth /> - Branch (default value is "master"): + Branch (default value is "main"):
Date: Thu, 15 Feb 2024 07:40:46 +0000 Subject: [PATCH 013/142] fixed overflow in conditions --- frontend/src/views/AngularWorkflow.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 2f461690..3f397674 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -9767,6 +9767,8 @@ const AngularWorkflow = (defaultprops) => { marginTop: "15px", marginLeft: "10px", overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", maxWidth: 72, }} > @@ -9786,7 +9788,7 @@ const AngularWorkflow = (defaultprops) => { flex: 1, textAlign: "center", marginTop: "15px", - overflow: "hidden", + overflow: "hidden", maxWidth: 72, }} onClick={() => { }} @@ -9810,6 +9812,8 @@ const AngularWorkflow = (defaultprops) => { marginBottom: "auto", marginLeft: "10px", overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", maxWidth: 72, }} > From 77de59f9e0e0f736267e3ba3a3a3fbd7eeb656ad Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 23 Feb 2024 16:44:23 +0100 Subject: [PATCH 014/142] Fixed app sdk problem with base64 decode for Gmail/Outlook --- backend/app_sdk/app_base.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index a7e1e7bf..54e4f770 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -96,6 +96,7 @@ def md5_base64(a): @shuffle_filters.register def base64_encode(a): a = str(a) + try: return base64.b64encode(a.encode('utf-8')).decode() except: @@ -104,6 +105,18 @@ def base64_encode(a): @shuffle_filters.register def base64_decode(a): a = str(a) + + if "-" in a: + a = a.replace("-", "+", -1) + + if "_" in a: + a = a.replace("_", "/", -1) + + # Fix padding + if len(a) % 4 != 0: + a += "=" * (4 - len(a) % 4) + print("Added padding") + try: return base64.b64decode(a).decode("unicode_escape") except: @@ -543,11 +556,13 @@ class AppBase: try: ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False, proxies=self.proxy_config) - self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} (break on 200/201) & Response= {ret.text}. Action status: {action_result["status"]}""") + self.logger.info(f"""[DEBUG] Successful result request: Status= {ret.status_code} (break on 200/201) & Action status: {action_result["status"]}. Response= {ret.text}""") if ret.status_code == 200 or ret.status_code == 201: finished = True break else: + # FIXME: Add a checker for 403, and Proxy logs failing + self.logger.info(f"[ERROR] Bad resp {ret.status_code}: {ret.text}") time.sleep(sleeptime) From 7142f55216e7493ad137b8b10edb411cb2473633 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Fri, 23 Feb 2024 09:46:38 +0000 Subject: [PATCH 015/142] fixing cancel button issue --- frontend/src/views/AngularWorkflow.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 2f461690..4ae83b79 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -467,6 +467,7 @@ const AngularWorkflow = (defaultprops) => { const [sourceValue, setSourceValue] = React.useState({}); const [destinationValue, setDestinationValue] = React.useState({}); const [conditionValue, setConditionValue] = React.useState({}); + const [tmpConditionValue, setTmpConditionValue] = React.useState({}); const [dragging, setDragging] = React.useState(false); const [showWorkflowRevisions, setShowWorkflowRevisions] = React.useState(false); const [selectedRevision, setSelectedRevision] = useState({}) @@ -8897,7 +8898,7 @@ const AngularWorkflow = (defaultprops) => { const AppConditionHandler = (props) => { const { tmpdata, type } = props; - const [data] = useState(tmpdata); + const [data] = useState({...tmpdata}); const [multiline, setMultiline] = useState(false); const [showAutocomplete, setShowAutocomplete] = React.useState(false); const [actionlist, setActionlist] = React.useState([]); @@ -9499,8 +9500,8 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "equals"; - setConditionValue(conditionValue); + tmpConditionValue.value = "equals"; + setTmpConditionValue(tmpConditionValue); setVariableAnchorEl(null); }} key={"equals"} @@ -9510,8 +9511,8 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "does not equal"; - setConditionValue(conditionValue); + tmpConditionValue.value = "does not equal"; + setTmpConditionValue(tmpConditionValue); setVariableAnchorEl(null); }} key={"does not equal"} @@ -9635,6 +9636,7 @@ const AngularWorkflow = (defaultprops) => { style={{ borderRadius: "0px" }} variant="contained" onClick={() => { + setConditionValue(tmpConditionValue); setSelectedEdge({}); var data = { From ac3e413cc669d7557a6ae3e992c66d6be6e0af21 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Sat, 24 Feb 2024 11:20:10 +0000 Subject: [PATCH 016/142] fixed the issue with shared reference of the conditionValue object across components, causing changes made in one component to affect the other --- frontend/src/views/AngularWorkflow.jsx | 50 +++++++++++++++----------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 4ae83b79..2a093179 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -9500,8 +9500,9 @@ const AngularWorkflow = (defaultprops) => { { - tmpConditionValue.value = "equals"; - setTmpConditionValue(tmpConditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "equals"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"equals"} @@ -9511,8 +9512,9 @@ const AngularWorkflow = (defaultprops) => { { - tmpConditionValue.value = "does not equal"; - setTmpConditionValue(tmpConditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "does not equal"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"does not equal"} @@ -9522,8 +9524,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "startswith"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "startswith"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"starts with"} @@ -9533,8 +9536,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "endswith"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "endswith"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"ends with"} @@ -9544,8 +9548,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "contains"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "contains"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"contains"} @@ -9555,8 +9560,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "contains_any_of"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "contains_any_of"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"contains_any_of"} @@ -9566,8 +9572,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "matches regex"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "matches regex"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"matches regex"} @@ -9577,8 +9584,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "larger than"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "larger than"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"larger than"} @@ -9588,8 +9596,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "less than"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "less than"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"less than"} @@ -9599,8 +9608,9 @@ const AngularWorkflow = (defaultprops) => { { - conditionValue.value = "is empty"; - setConditionValue(conditionValue); + const newConditionValue = { ...conditionValue }; + newConditionValue.value = "is empty"; + setConditionValue(newConditionValue); setVariableAnchorEl(null); }} key={"is empty"} From 07516d3ae5a4d3c7af2a7b9b666c76a60f352cc8 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Sat, 24 Feb 2024 11:58:03 +0000 Subject: [PATCH 017/142] removed unused variable --- frontend/src/views/AngularWorkflow.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 2a093179..c49f402c 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -467,7 +467,6 @@ const AngularWorkflow = (defaultprops) => { const [sourceValue, setSourceValue] = React.useState({}); const [destinationValue, setDestinationValue] = React.useState({}); const [conditionValue, setConditionValue] = React.useState({}); - const [tmpConditionValue, setTmpConditionValue] = React.useState({}); const [dragging, setDragging] = React.useState(false); const [showWorkflowRevisions, setShowWorkflowRevisions] = React.useState(false); const [selectedRevision, setSelectedRevision] = useState({}) @@ -9646,7 +9645,6 @@ const AngularWorkflow = (defaultprops) => { style={{ borderRadius: "0px" }} variant="contained" onClick={() => { - setConditionValue(tmpConditionValue); setSelectedEdge({}); var data = { From 7c43245a88f9d37a16b099418acbe53232042ef2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Sun, 25 Feb 2024 04:07:09 +0100 Subject: [PATCH 018/142] Added Pipeline controller to Orborus for testing --- functions/onprem/orborus/orborus.go | 162 +++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 4 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 14cba361..80ac4713 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -750,7 +750,6 @@ func deployWorker(image string, identifier string, env []string, executionReques } hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - if strings.ToLower(cleanupEnv) != "false" { hostConfig.AutoRemove = true } @@ -835,7 +834,7 @@ func deployWorker(image string, identifier string, env []string, executionReques log.Printf("[ERROR] Failed to start worker container in environment %s: %s", environment, err) return err } else { - log.Printf("[INFO][%s] Worker Container created. Environment %s: docker logs %s", executionRequest.ExecutionId, environment, cont.ID) + 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) @@ -1568,7 +1567,7 @@ func main() { // FIXME - add check for StatusCode if newresp.StatusCode != 200 { - log.Printf("[ERROR] Backend connection failed, or is missing (%d): %s", newresp.StatusCode, string(body)) + log.Printf("[ERROR] Backend connection failed for url '%s', or is missing (%d): %s", fullUrl, newresp.StatusCode, string(body)) } else { if !hasStarted { log.Printf("[DEBUG] Starting iteration on environment %#v (default = Shuffle). Got statuscode %d from backend on first request", environment, newresp.StatusCode) @@ -1604,7 +1603,15 @@ func main() { newrequests := []shuffle.ExecutionRequest{} for _, incRequest := range executionRequests.Data { // Looking for specific jobs - if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" { + if incRequest.Type == "PIPELINE_CREATE" || incRequest.Type == "PIPELINE_UPDATE" || incRequest.Type == "PIPELINE_DELETE" { + + err := handlePipeline(incRequest) + if err != nil { + log.Printf("[ERROR] Failed handling pipeline: %s", err) + } + + toBeRemoved.Data = append(toBeRemoved.Data, incRequest) + } else if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" { log.Printf("[INFO] Should delete -> download new image %#v", incRequest.ExecutionArgument) if len(incRequest.ExecutionArgument) > 0 { @@ -1788,7 +1795,154 @@ func main() { time.Sleep(time.Duration(sleepTime) * time.Second) } +} + +func deployPipeline(image, identifier, command string) error { + if isKubernetes == "true" { + return errors.New("Kubernetes not implemented") + } + + ctx := context.Background() + hostConfig := &container.HostConfig{ + LogConfig: container.LogConfig{ + Type: "json-file", + Config: map[string]string{ + "max-size": "10m", + }, + }, + Resources: container.Resources{}, + } + + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) + if strings.ToLower(cleanupEnv) != "false" { + hostConfig.AutoRemove = true + } + + envVariables := []string{ + } + + + config := &container.Config{ + Image: image, + Env: envVariables, + Cmd: []string{command}, + } + + // Add label to container in case of zombies + config.Labels = map[string]string{ + "shuffle": "shuffle", + } + + cont, err := dockercli.ContainerCreate( + ctx, + config, + hostConfig, + nil, + nil, + identifier, + ) + + if err != nil { + if strings.Contains(fmt.Sprintf("%s", err), "Conflict. The container name ") { + log.Printf("[DEBUG] Pipeline Container %s already exists, removing it", identifier) + } else { + log.Printf("[ERROR] Failed to create pipeline container %s: %s", identifier, err) + return err + } + } + + containerStartOptions := types.ContainerStartOptions{} + err = dockercli.ContainerStart( + ctx, + cont.ID, + containerStartOptions, + ) + if err != nil { + if strings.Contains(fmt.Sprintf("%s", err), "cannot join network") || strings.Contains(fmt.Sprintf("%s", err), "No such container") { + hostConfig.NetworkMode = "" + cont, err = dockercli.ContainerCreate( + ctx, + config, + hostConfig, + nil, + nil, + identifier+"-2", + ) + if err != nil { + log.Printf("[ERROR] Failed to CREATE pipeline container (2): %s", err) + } + + err = dockercli.ContainerStart( + ctx, + cont.ID, + containerStartOptions, + ) + if err != nil { + log.Printf("[ERROR] Failed to start pipeline container (2): %s", err) + return err + } + } else { + log.Printf("[ERROR] Failed initial pipeline container start. Quitting as this is NOT a simple network issue. Err: %s", err) + } + + if err != nil { + log.Printf("[ERROR] Failed to start pipeline container in environment %s: %s", environment, err) + return err + } else { + log.Printf("[INFO] Pipeline Container created (1). Environment %s: docker logs %s", environment, cont.ID) + } + + stats, err := dockercli.ContainerInspect(ctx, cont.ID) + if err != nil { + log.Printf("[ERROR] Failed checking pipeline with containername '%s'", cont.ID) + return nil + } + + containerStatus := stats.ContainerJSONBase.State.Status + log.Printf("[DEBUG] Status of pipeline '%s' is %s. Should be running. Will reset", containerName, containerStatus) + } + + return nil +} + +// Tenzir command samples +// docker pull ghcr.io/dominiklohmann/tenzir-arm64:latest +// docker tag ghcr.io/dominiklohmann/tenzir-arm64:latest tenzir/tenzir:latest + +// Read from Cache and send it to a webhook +// docker run tenzir/tenzir:latest 'from http://192.168.86.44:5002/api/v1/orgs/7e9b9007-5df2-4b47-bca5-c4d267ef2943/cache/CIDR%20ranges?type=text&authorization=cec9d01f-09b2-4419-8a0a-76c6046e3fef read lines | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines' +func handlePipeline(incRequest shuffle.ExecutionRequest) error { + if len(incRequest.ExecutionArgument) == 0 { + log.Printf("[ERROR] No execution argument found for pipeline create. Skipping") + + return errors.New("No execution argument found for pipeline create. Skipping") + } + + image := "tenzir/tenzir:latest" + identifier := strings.ToLower(strings.ReplaceAll(incRequest.ExecutionSource, " ", "-")) + command := incRequest.ExecutionArgument + + if incRequest.Type == "PIPELINE_CREATE" { + log.Printf("[INFO] Should delete -> recreate new pipeline %#v. Name: %#v", incRequest.ExecutionArgument, identifier) + err := deployPipeline(image, identifier, command) + if err != nil { + log.Printf("[ERROR] Failed to deploy pipeline: %s", err) + return err + } else { + log.Printf("[INFO] Pipeline deployed successfully") + } + } else if incRequest.Type == "PIPELINE_DELETE" { + log.Printf("[INFO] Should delete pipeline %#v", incRequest.ExecutionArgument) + } else if incRequest.Type == "PIPELINE_UPDATE" { + log.Printf("[INFO] Should update pipeline %#v", incRequest.ExecutionArgument) + } else { + log.Printf("[ERROR] Unknown type for pipeline: %s", incRequest.Type) + return errors.New("Unknown type for pipeline") + } + + + return nil } // Is this ok to do with Docker? idk :) From e682a7d2a74c91435210aca825cd3a8cf96d3041 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 26 Feb 2024 23:15:57 +0100 Subject: [PATCH 019/142] Added Docs update, missing images and Tenzir pipelines --- frontend/public/images/workflows/tenzir2.png | Bin 0 -> 2384 bytes .../public/images/workflows/user_input.svg | 30 ++ frontend/src/components/CacheView.jsx | 95 ++++- frontend/src/views/Admin.jsx | 8 +- frontend/src/views/AngularWorkflow.jsx | 384 +++++++++++++++--- frontend/src/views/Docs.jsx | 15 +- functions/onprem/orborus/orborus.go | 4 +- 7 files changed, 461 insertions(+), 75 deletions(-) create mode 100644 frontend/public/images/workflows/tenzir2.png create mode 100644 frontend/public/images/workflows/user_input.svg diff --git a/frontend/public/images/workflows/tenzir2.png b/frontend/public/images/workflows/tenzir2.png new file mode 100644 index 0000000000000000000000000000000000000000..12c702bca1b9a42009800d96b62c372cb0872232 GIT binary patch literal 2384 zcmd6p`#;m|AICq=hYn_P%8gr@+hHc5kV7q}nNxFS=2Xf;79w>!WN4^p*kTh6A>nIb z+Q=B|4k7m#nmCZ{B%m5dh>=5e~#Ct|T--jS zf_kmsI=t7h;ETkFEGA{@+NK01wK>HkWlFp9iUYAuk$)QG=EVZh!j|{%ljIYu>JZnh zV1ePWYPkt-$Ia;L!lvZ$8JW(qTk{;sL}Qm-#cGpw1}Mu59Rbcmy1{V-gg(qvjt$YP zV)W_mA(8PsF!A3Zey-Jf5mFZ}rPbitQW zQ12~-UEMpLUf>e2Yy=$pVpr`lvD#m{9wQd7l(7n5_mP-A5@$AZy?N6jV5^H%v zctD^773c*vH};`Q5A59-YFa#l2a0h6zy?Vzx@G~HTQuXauIeHU!3 zf@)AHr?c&Y?&Z)j3UWd&<7Msl7N`6%8?f(Po+RQY<1oMqNb%h8h>Ks5biG{EO$5Oy zg2#7w0%xA~S97u|%6_o~)LT=68T71N`E$PTk%dLKc>K;DLiCoztjb|qaI?g6A&m6RfvxjX%jH|Yy7YK1}A_Y$2Z@OxY!FkzlJ4Y zb%VL9-xWU27LkKWv$E5;P5_hYo=KHjw#>*5+*Cr~(ko`-x@^FzVm1ns)L#e_f~7ts ze*yijX0gT9t8*(w^Y6AtmTRo zDtakKzA@I0X_l3Li8;gvSSJL<*M;90gB-x}D$wWHonKjEqqRFb{p0&N^ri<~*y zVkr$nH^xeZ?U;(AXN0Dnd3eUSU-kOY1qoc{R}W-lP9`EXf=iAlS?E`?Xs zXpidhZKlE9lFO;uS*uLxzS=rpj70Uyx8BKnQ6X;&QO_80G3ssR1T#! zNHQMOlOTbKq>S9&)Xb0p(EAfPFj~|sx;GbCd^UW(tK)(u;wwxU?qp_4W_u}#fdK<)zbVxz+~O_W;5X;G2m}3(1pSuiNN(EU1r+g^#0^%D| z0G2MFZ!YRo^ba?SGUYTEBGqDAA2|sS=w}>q$h~0~0azucY&!$Aoe`Y0SHvBGiTQLY zxIjt(=W*@cM1J~H>5j5nz7b2E{>ySmsOMlrR{nqK zYiRgVIp}_vA9_jzw{i1zR$m0={J5z>Q*YE@L{eFSsHCxn)hKz7zYfXkAHy2U)>Dmn zMz@fkP*f*tQsKrs=PvimYY`skY&3kk9U@*OL3OHnXtkk_!swmRORV>xDPl}~t&=ki zaLVU_kamGf&gh32PW0nq#(u{;5K-vf@Vw&geCFR}!#Nf>NWMdIphv8@1vu-Ve{jP4 z1N)ocMW0~L>q%1Hfo7WTZO#Q4GN2-Whm;M23?aZG&6MUw7=-fGUwVhp$RH8xP_&gr zTfb?)H=y+!LkLcoTkJkJ76#u&oS7a0^>%dHyGhn}-?&$ZjDJMIW`1V-HjoZ{3#6Y6 zfL=ACA}tqj&Y2bsDk7^w>Bt>g$$J85SX_339AOu*)~{BBox5pJzve)((I1nVJZdNO z%bR9BNIkJ!#;+FR9oNCabYK$Qv(^3uJo zs9oa3Q?<`LLrS@qM#ydloyfZUa(cT^Q(+*WFHYWv&Bt<^Lt^WHcvA@0fy z`U>;$>PDqBuI7b%-L8(@xz)_FU*6IQa%Pq9c&@iaUW1Uqj68-Fsl|Jli+>*m3wL@D z#9a+U*C!}pY4y+-=-%Qp1YsNBJF*mDaRzwws)J=WOml3c) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 89cde359..607e0030 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -4,6 +4,7 @@ import { toast } from 'react-toastify'; import ReactJson from "react-json-view"; import { + Typography, Tooltip, Divider, TextField, @@ -21,6 +22,8 @@ import { } from "@mui/material"; import { + Link as LinkIcon, + AutoFixHigh as AutoFixHighIcon, Edit as EditIcon, FileCopy as FileCopyIcon, SelectAll as SelectAllIcon, @@ -79,7 +82,6 @@ const CacheView = (props) => { useEffect(() => { listOrgCache(orgId); - console.log("orgid", orgId); }, []); const listOrgCache = (orgId) => { @@ -189,8 +191,6 @@ const CacheView = (props) => { const editOrgCache = (orgId) => { const cache = { key: dataValue.key , value: value }; setCacheInput([cache]); - console.log("cache:", cache) - console.log("cache input: ", cacheInput) fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { @@ -255,6 +255,20 @@ const CacheView = (props) => { }); }; + const isValidJson = validateJson(value) + const autoFixJson = (inputvalue) => { + console.log("inputvalue: ", inputvalue) + try { + var parsedjson = JSON.parse(inputvalue) + + // setValue() with the parsed json as string + setValue(JSON.stringify(parsedjson, null, 2)) + } catch (e) { + console.log("Error parsing JSON: ", e) + //return JSON.stringify(inputvalue); + } + } + const modalView = ( // console.log("key:", dataValue.key), //console.log("value:",dataValue.value), @@ -301,14 +315,27 @@ const CacheView = (props) => { onChange={(e) => setKey(e.target.value)} />
-
- Value +
+
+ + Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON) + + + { + autoFixJson(value) + }} + > + + + +
{ id="Valuefield" margin="normal" variant="outlined" - defaultValue={editCache ? dataValue.value : ""} + multiline + minRows={4} + maxRows={12} + //defaultValue={editCache ? dataValue.value : ""} + value={value} onChange={(e) => setValue(e.target.value)} />
@@ -417,10 +456,8 @@ const CacheView = (props) => { } const validate = validateJson(data.value); - console.log("Past validate: ", validate); - return ( - + { /> @@ -473,7 +510,11 @@ const CacheView = (props) => { style={{ padding: "6px" }} onClick={() => { setEditCache(true) - setDataValue({"key":data.key,"value":data.value}) + setDataValue({ + "key": data.key, + "value":data.value + }) + setValue(data.value) setModalOpen(true) }} > @@ -483,9 +524,27 @@ const CacheView = (props) => { + + + { + window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank"); + }} + > + + + + diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 984ebb30..2c5175eb 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1261,7 +1261,7 @@ If you're interested, please let me know a time that works for you, or set up a }; const rerunCloudWorkflows = (environment) => { - toast("Starting execution reruns. This can run in the background.") + toast("Starting execution reruns. This can run in the background.") fetch( `${globalUrl}/api/v1/environments/${environment.id}/rerun`, { @@ -1292,6 +1292,7 @@ If you're interested, please let me know a time that works for you, or set up a const abortEnvironmentWorkflows = (environment) => { //console.log("Aborting all workflows started >10 minutes ago, not finished"); + toast("Clearing the queue - this may take some time. A new will show up when finished.") fetch( `${globalUrl}/api/v1/environments/${environment.id}/stop?deleteall=true`, @@ -1306,7 +1307,9 @@ If you're interested, please let me know a time that works for you, or set up a toast("Failed aborting dangling workflows"); return; } else { - toast("Aborted all dangling workflows"); + toast("Successfully cleared the queue") + + getEnvironments() } return response.json(); @@ -4597,7 +4600,6 @@ If you're interested, please let me know a time that works for you, or set up a + +
+
+
+
const ScheduleSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined && selectedTrigger.trigger_type !== "SCHEDULE" ? null :
@@ -13211,7 +13494,7 @@ const AngularWorkflow = (defaultprops) => { workflow.triggers[selectedTriggerIndex].parameters === undefined ? "" : workflow.triggers[selectedTriggerIndex].parameters[0].value } color="primary" - placeholder="defaultValue" + placeholder="" onBlur={(e) => { setTriggerCronWrapper(e.target.value); }} @@ -13801,10 +14084,8 @@ const AngularWorkflow = (defaultprops) => {
: null - - const RightsideBar = () => { - const [hovered, setHovered] = useState(false) + const [hovered, setHovered] = useState(false) useEffect(() => { const handleKeyDown = (event) => { @@ -13856,9 +14137,8 @@ const AngularWorkflow = (defaultprops) => { return () => { document.removeEventListener('keydown', handleKeyDown); - }; - }, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]); - + } + }, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]) return (
{ } const boxSize = isMobile ? 50 : 100; - - const executionButton = executionRunning ? ( @@ -14333,6 +14611,8 @@ const AngularWorkflow = (defaultprops) => { defaultReturn = null } else if (selectedTrigger.trigger_type === "SUBFLOW") { defaultReturn = + // } else if (selectedTrigger.trigger_type === "PIPELINE") { + // defaultReturn = } else if (selectedTrigger.trigger_type === "EMAIL") { defaultReturn = } else if (selectedTrigger.trigger_type === "USERINPUT") { @@ -16317,7 +16597,7 @@ const AngularWorkflow = (defaultprops) => { style: { pointerEvents: "auto", color: "white", - minWidth: isMobile ? "90%" : 650, + minWidth: isMobile ? "90%" : 750, padding: 30, maxHeight: 550, overflowY: "auto", @@ -16327,17 +16607,19 @@ const AngularWorkflow = (defaultprops) => { }, }} > - + {/* Have a sticky top bar */} + { @@ -16356,7 +16638,7 @@ const AngularWorkflow = (defaultprops) => { style={{ zIndex: 5000, position: "absolute", - top: 34, + top: 4, right: 170, }} onClick={(e) => { @@ -16400,7 +16682,7 @@ const AngularWorkflow = (defaultprops) => { style={{ zIndex: 5000, position: "absolute", - top: 34, + top: 4, right: 136, }} onClick={(e) => { @@ -16440,7 +16722,7 @@ const AngularWorkflow = (defaultprops) => { style={{ zIndex: 10011 }} > { e.preventDefault(); const executionIndex = workflowExecutions.findIndex((data) => data.execution_id === selectedResult.execution_id); @@ -16470,7 +16752,7 @@ const AngularWorkflow = (defaultprops) => { style={{ zIndex: 10011 }} > { e.preventDefault(); setCodeModalOpen(false); @@ -16481,8 +16763,8 @@ const AngularWorkflow = (defaultprops) => { -
-
+
+
{curapp === null ? null : ( {selectedResult.action.app_name} { {/* Looks for triggers" */} {/* Only fixed the ones that require scrolling on a small screen */} {/* Most important: Actions. But these are a lot more complex */} - {rightSideBarOpen && (selectedTrigger.trigger_type === "SCHEDULE" || selectedTrigger.trigger_type === "WEBHOOK") ? + {rightSideBarOpen && (selectedTrigger.trigger_type === "SCHEDULE" || selectedTrigger.trigger_type === "WEBHOOK" || selectedTrigger.trigger_type === "PIPELINE") ?
{Object.getOwnPropertyNames(selectedTrigger).length > 0 ? selectedTrigger.trigger_type === "SCHEDULE" ? ScheduleSidebar + : selectedTrigger.trigger_type === "PIPELINE" ? + PipelineSidebar : selectedTrigger.trigger_type === "WEBHOOK" ? WebhookSidebar : null @@ -18404,14 +18688,14 @@ const AngularWorkflow = (defaultprops) => { {newView} - {executionArgumentModal} - {aiQueryModal} + {aiQueryModal} {conditionsModal} - {authenticationModal} {codePopoutModal} - {configureWorkflowModal} - {/*editWorkflowModal*/} {workflowRevisions} + {authenticationModal} + {/*editWorkflowModal*/} + {executionArgumentModal} + {configureWorkflowModal} {codeEditorModalOpen ? diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 027aad59..03d5ceda 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -118,8 +118,6 @@ export const Img = (props) => { export const CodeHandler = (props) => { const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : "" - - const validate = validateJson(propvalue) var newprop = propvalue @@ -137,6 +135,17 @@ export const CodeHandler = (props) => { //} } + // Need to check if it's singletick or multi + console.log("PROP: ", propvalue, props) + if (props.inline === true) { + // Show it inline + return ( + + {newprop} + + ) + } + return (
{ maxWidth: "100%", backgroundColor: theme.palette.inputColor, overflowY: "auto", + // Have it inline + borderRadius: theme.palette.borderRadius, }} > {validate.valid === true ? diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 80ac4713..f175595e 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1822,7 +1822,6 @@ func deployPipeline(image, identifier, command string) error { envVariables := []string{ } - config := &container.Config{ Image: image, Env: envVariables, @@ -1831,6 +1830,7 @@ func deployPipeline(image, identifier, command string) error { // Add label to container in case of zombies config.Labels = map[string]string{ + "name": identifier, "shuffle": "shuffle", } @@ -1920,7 +1920,7 @@ func handlePipeline(incRequest shuffle.ExecutionRequest) error { } image := "tenzir/tenzir:latest" - identifier := strings.ToLower(strings.ReplaceAll(incRequest.ExecutionSource, " ", "-")) + identifier := fmt.Sprintf("shuffle-%s", strings.ToLower(strings.ReplaceAll(incRequest.ExecutionSource, " ", "-"))) command := incRequest.ExecutionArgument if incRequest.Type == "PIPELINE_CREATE" { From 3489d26c7381b43ba0374ba4755c0c5510be2b2c Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 27 Feb 2024 00:06:24 +0100 Subject: [PATCH 020/142] Minor orborus test fixes --- functions/onprem/orborus/orborus.go | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index f175595e..14a013eb 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1822,17 +1822,35 @@ func deployPipeline(image, identifier, command string) error { envVariables := []string{ } + + // Add volume binds for storage + // Want read/write with full access for the container + // Should mount + sourceFolder := "/Users/frikky/git/shuffle/shuffle-database/tenzir" + destinationFolder := "/var/lib/tenzir" + hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ + Type: mount.TypeBind, + Source: sourceFolder, + Target: destinationFolder, + }) + config := &container.Config{ Image: image, Env: envVariables, - Cmd: []string{command}, + Cmd: []string{ + "mkdir", + "-p", + destinationFolder, + command, + }, } // Add label to container in case of zombies config.Labels = map[string]string{ "name": identifier, "shuffle": "shuffle", - } + } + cont, err := dockercli.ContainerCreate( ctx, @@ -1903,6 +1921,18 @@ func deployPipeline(image, identifier, command string) error { log.Printf("[DEBUG] Status of pipeline '%s' is %s. Should be running. Will reset", containerName, containerStatus) } + // Wait for the container to finish + statusCh, errCh := dockercli.ContainerWait(ctx, cont.ID, container.WaitConditionNotRunning) + select { + case err := <-errCh: + if err != nil { + log.Printf("[ERROR] Failed to wait for container: %s", err) + } + case <-statusCh: + log.Printf("[INFO] Container finished") + } + + return nil } From 54f6b07cb62dff8f32ccbe584d78f578d1aa6ad6 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 28 Feb 2024 20:06:45 +0100 Subject: [PATCH 021/142] Added contributor tracking apps, and started tracking revisions the same way as workflows --- backend/go-app/walkoff.go | 6 +++++ functions/onprem/orborus/orborus.go | 35 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index a9d72774..21a26f8e 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -3183,6 +3183,12 @@ func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) { workflowapp.Generated = false workflowapp.Activated = true + if !shuffle.ArrayContains(api.Contributors, user.Id) { + api.Contributors = append(api.Contributors, user.Id) + } + + shuffle.SetAppRevision(ctx, workflowapp) + err = shuffle.SetWorkflowAppDatastore(ctx, workflowapp, workflowapp.ID) if err != nil { log.Printf("[WARNING] Failed setting workflowapp: %s", err) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 14a013eb..05697e13 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1825,22 +1825,32 @@ func deployPipeline(image, identifier, command string) error { // Add volume binds for storage // Want read/write with full access for the container - // Should mount - sourceFolder := "/Users/frikky/git/shuffle/shuffle-database/tenzir" - destinationFolder := "/var/lib/tenzir" - hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ - Type: mount.TypeBind, - Source: sourceFolder, - Target: destinationFolder, - }) + //sourceFolder := "/Users/frikky/git/shuffle/shuffle-database" + //destinationFolder := "/tmp/storage" + //hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ + // Type: mount.TypeBind, + // Source: sourceFolder, + // Target: destinationFolder, + //}) + + // FIXME: Is using sigma "automatically" here good? + // Or is it better to run it as a separate workflow? + if strings.Contains(command, "sigma") { + log.Printf("[DEBUG] Should LOAD sigma from backend in realtime and dump it in a folder inside the container") + + //sourceFolder := "/tmp/tenzir/sigma" + //sigmaFolder := "/tmp/tenzir/sigma" + //hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ + // Type: mount.TypeBind, + // Source: sigmaFolder, + // Target: sigmaFolder, + //} + } config := &container.Config{ Image: image, Env: envVariables, Cmd: []string{ - "mkdir", - "-p", - destinationFolder, command, }, } @@ -1922,6 +1932,7 @@ func deployPipeline(image, identifier, command string) error { } // Wait for the container to finish + /* statusCh, errCh := dockercli.ContainerWait(ctx, cont.ID, container.WaitConditionNotRunning) select { case err := <-errCh: @@ -1931,7 +1942,7 @@ func deployPipeline(image, identifier, command string) error { case <-statusCh: log.Printf("[INFO] Container finished") } - + */ return nil } From d65e9215ffcd28f7d75c3d44cd4c029b3f475b2d Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 28 Feb 2024 21:49:47 +0100 Subject: [PATCH 022/142] Removed a lot of verbosity from app sdk --- backend/app_sdk/app_base.py | 218 ++++++------------------------------ 1 file changed, 34 insertions(+), 184 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 54e4f770..c54de19c 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -115,7 +115,6 @@ def base64_decode(a): # Fix padding if len(a) % 4 != 0: a += "=" * (4 - len(a) % 4) - print("Added padding") try: return base64.b64decode(a).decode("unicode_escape") @@ -244,7 +243,6 @@ def csv_parse(a): try: return json.dumps(allitems) except: - print("[ERROR] Failed dumping from JSON in csv parse") return allitems @shuffle_filters.register @@ -272,13 +270,6 @@ def split(base, sep): except: return base.split(sep) -#print(shuffle_filters.filters) -#print(Liquid("{{ '10' | plus: 1}}", filters=shuffle_filters.filters).render()) -#print(Liquid("{{ '10' | minus: 1}}", filters=shuffle_filters.filters).render()) -#print(Liquid("{{ asd | size }}", filters=shuffle_filters.filters).render()) -#print(Liquid("{{ 'asd' | md5 }}", filters=shuffle_filters.filters).render()) -#print(Liquid("{{ 'asd' | sha256 }}", filters=shuffle_filters.filters).render()) -#print(Liquid("{{ 'asd' | md5_base64 | base64_decode }}", filters=shuffle_filters.filters).render()) ### ### @@ -352,9 +343,8 @@ class AppBase: if self.proxy_config["https"].lower() == "noproxy": self.proxy_config["https"] = "" except Exception as e: - self.logger.info(f"[DEBUG] Failed setting proxy config: {e}. NOT important if running apps with webserver. This is NOT critical.") + self.logger.info(f"[WARNING] Failed setting proxy config: {e}. NOT important if running apps with webserver. This is NOT critical.") - self.logger.info(f"[DEBUG] Proxy config: {self.proxy_config}") if isinstance(self.action, str): try: @@ -363,33 +353,26 @@ class AppBase: except Exception as e: self.logger.info(f"[DEBUG] Failed parsing action as JSON (init): {e}. NOT important if running apps with webserver. This is NOT critical.") - #print(f"ACTION: {self.action}") - if len(self.base_url) == 0: self.base_url = self.url # Checks output for whether it should be automatically parsed or not def run_magic_parser(self, input_data): if not isinstance(input_data, str): - self.logger.info("[DEBUG] Not string. Returning from magic") return input_data # Don't touch existing JSON/lists if (input_data.startswith("[") and input_data.endswith("]")) or (input_data.startswith("{") and input_data.endswith("}")): - self.logger.info("[DEBUG] Already JSON-like. Returning from magic") return input_data if len(input_data) < 3: - self.logger.info("[DEBUG] Too short input data") return input_data # Don't touch large data. if len(input_data) > 100000: - self.logger.info("[DEBUG] Value too large. Returning from magic") return input_data if not "\n" in input_data and not "," in input_data: - self.logger.info("[DEBUG] No data to autoparse - requires newline or comma") return input_data new_input = input_data @@ -411,7 +394,6 @@ class AppBase: index += 1 continue - #print("FIX ITEM %s" % item) for subitem in item.split(splititem): new_return.insert(index, subitem) @@ -419,7 +401,7 @@ class AppBase: # Prevent large data or infinite loops if index > 10000: - self.logger.info(f"[DEBUG] Infinite loop. Returning default data.") + #self.logger.info(f"[DEBUG] Infinite loop. Returning default data.") return input_data fixed_return = [] @@ -484,7 +466,6 @@ class AppBase: "cookies":cookies, }) except Exception as e: - print(f"[WARNING] Failed in request: {e}") return request.text # FIXME: Add more info like logs in here. @@ -494,40 +475,29 @@ class AppBase: action_result["status"] = "FAILURE" try: - #self.logger.info(f"[DEBUG] ACTION: {self.action}") if self.action["run_magic_output"] == True: - self.logger.warning(f"[INFO] Action result ran with Magic parser output.") action_result["result"] = self.run_magic_parser(action_result["result"]) - else: - self.logger.warning(f"[WARNING] Magic output not defined.") except KeyError as e: - #self.logger.warning(f"[DEBUG] Failed to run magic autoparser (send result) - keyerror: {e}") pass except Exception as e: - #self.logger.warning(f"[DEBUG] Failed to run magic autoparser (send result): {e}") pass # Try it with some magic action_result["completed_at"] = int(time.time_ns()) - self.logger.info(f"""[DEBUG] Inside Send result with status {action_result["status"]}""") #if isinstance(action_result, # FIXME: Add cleanup of parameters to not send to frontend here params = {} # I wonder if this actually works - self.logger.info(f"[DEBUG] Before last stream result") url = "%s%s" % (self.base_url, stream_path) - self.logger.info(f"[INFO] URL FOR RESULT (URL): {url}") try: log_contents = "disabled: add env SHUFFLE_LOGS_DISABLED=true to Orborus to re-enable logs for apps. Can not be enabled natively in Cloud except in Hybrid mode." if not os.getenv("SHUFFLE_LOGS_DISABLED") == "true": log_contents = self.log_capture_string.getvalue() - #print("RESULTS: %s" % log_contents) - self.logger.info(f"[WARNING] Got logs of length {len(log_contents)}") if len(action_result["action"]["parameters"]) == 0: action_result["action"]["parameters"] = [] @@ -544,7 +514,7 @@ class AppBase: }) except Exception as e: - print(f"[WARNING] Failed adding parameter for logs: {e}") + pass try: finished = False @@ -556,25 +526,22 @@ class AppBase: try: ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False, proxies=self.proxy_config) - self.logger.info(f"""[DEBUG] Successful result request: Status= {ret.status_code} (break on 200/201) & Action status: {action_result["status"]}. Response= {ret.text}""") + #self.logger.info(f"""[DEBUG] Successful result request: Status= {ret.status_code} (break on 200/201) & Action status: {action_result["status"]}. Response= {ret.text}""") if ret.status_code == 200 or ret.status_code == 201: finished = True break else: # FIXME: Add a checker for 403, and Proxy logs failing - - self.logger.info(f"[ERROR] Bad resp {ret.status_code}: {ret.text}") + self.logger.info(f"[ERROR] Bad resp {ret.status_code} for url {url}") time.sleep(sleeptime) # Proxyerrror except requests.exceptions.ProxyError as e: - self.logger.info(f"[ERROR] Proxy error for url {url}: {e}") self.proxy_config = {} continue except requests.exceptions.RequestException as e: - self.logger.info(f"[DEBUG] Request problem for url {url}: {e}") time.sleep(sleeptime) # Check if we have a read timeout. If we do, exit as we most likely sent the result without getting a good result @@ -591,25 +558,21 @@ class AppBase: #time.sleep(5) continue except TimeoutError as e: - self.logger.info(f"[DEBUG] Timeout or request: {e}") time.sleep(sleeptime) #time.sleep(5) continue except requests.exceptions.ConnectionError as e: - self.logger.info(f"[DEBUG] Connectionerror: {e}") time.sleep(sleeptime) #time.sleep(5) continue except http.client.RemoteDisconnected as e: - self.logger.info(f"[DEBUG] Remote: {e}") time.sleep(sleeptime) #time.sleep(5) continue except urllib3.exceptions.ProtocolError as e: - self.logger.info(f"[DEBUG] Protocol err: {e}") time.sleep(0.1) #time.sleep(5) @@ -627,18 +590,19 @@ class AppBase: return except requests.exceptions.ConnectionError as e: - self.logger.info(f"[DEBUG] Unexpected ConnectionError happened: {e}") + #self.logger.info(f"[DEBUG] Unexpected ConnectionError happened: {e}") + pass except TypeError as e: action_result["status"] = "FAILURE" action_result["result"] = json.dumps({"success": False, "reason": "Typeerror when sending to backend URL %s" % url}) - self.logger.info(f"[DEBUG] Before typeerror stream result: {e}") + #self.logger.info(f"[DEBUG] Before typeerror stream result: {e}") ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False, proxies=self.proxy_config) #self.logger.info(f"[DEBUG] Result: {ret.status_code}") #if ret.status_code != 200: # pr - self.logger.info(f"[DEBUG] TypeError request: Status= {ret.status_code} & Response= {ret.text}") + #self.logger.info(f"[DEBUG] TypeError request: Status= {ret.status_code} & Response= {ret.text}") except http.client.RemoteDisconnected as e: self.logger.info(f"[DEBUG] Expected Remotedisconnect happened: {e}") except urllib3.exceptions.ProtocolError as e: @@ -653,7 +617,6 @@ class AppBase: #self.log_capture_string.close() #pass except Exception as e: - print(f"[WARNING] Failed to flush logs: {e}") pass #async def cartesian_product(self, L): @@ -1506,7 +1469,6 @@ class AppBase: self.logger.info("Ret UPLOAD: %s" % ret.text) self.logger.info("Ret2 UPLOAD: %d" % ret.status_code) - self.logger.info("IDS TO RETURN: %s" % file_ids) return file_ids #async def execute_action(self, action): @@ -1702,7 +1664,7 @@ class AppBase: except Exception as e: self.logger.info(f"[WARNING] Failed in replace params action parsing: {e}") - self.logger.info(f"[DEBUG] AFTER FULLEXEC stream result (init): {self.current_execution_id}") + #self.logger.info(f"[DEBUG] AFTER FULLEXEC stream result (init): {self.current_execution_id}") # Gets the value at the parenthesis level you want def parse_nested_param(string, level): @@ -1822,9 +1784,9 @@ class AppBase: return f"join({data})" except (KeyError, IndexError) as e: - print(f"ERROR in join(): {e}") + pass except json.decoder.JSONDecodeError as e: - print(f"JSON ERROR in join(): {e}") + pass if "len" in thistype or "length" in thistype or "lenght" in thistype: #self.logger.info(f"Trying to length-parse: {data}") @@ -1940,7 +1902,6 @@ class AppBase: else: parse_string = inner_result - #print("PARSE STRING: %s" % parse_string) return parse_string, True # Looks for parantheses to grab special cases within a string, e.g: @@ -2088,11 +2049,8 @@ class AppBase: if isinstance(seconditem, int): seconditem = str(seconditem) - #print("[DEBUG] ACTUAL PARSED: %s" % actualitem) - # Means it's a single item -> continue if seconditem == "": - #print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end": firstitem = len(basejson)-1 elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first": @@ -2100,7 +2058,6 @@ class AppBase: else: firstitem = int(firstitem) - #print(f"[DEBUG] Post lower checks with item {firstitem}") tmpitem = basejson[int(firstitem)] try: newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) @@ -2127,7 +2084,6 @@ class AppBase: else: seconditem = int(seconditem) - #print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") newvalue = [] if int(seconditem) > len(basejson): seconditem = len(basejson) @@ -2139,7 +2095,6 @@ class AppBase: try: ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) except IndexError: - #print("[DEBUG] INDEXERROR (1): ", parsersplit[outercnt]) #ret = innervalue ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:]) @@ -2153,13 +2108,10 @@ class AppBase: try: if isinstance(basejson, list): - #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson, bool): - #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) return basejson, False elif isinstance(basejson, int): - #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) return basejson, False elif isinstance(basejson[value], str): try: @@ -2167,14 +2119,10 @@ class AppBase: basejson = json.loads(basejson[value]) else: # Should we sanitize here? - #print("[DEBUG] VALUE TO SANITIZE FOR KEY '%s'?: %s" % (value, basejson[value])) - # Check if we are on the last item? if outercnt == len(parsersplit)-1: - #print("[DEBUG] LAST KEY") return str(basejson[value]), False else: - #print("[DEBUG] NOT LAST KEY") pass except json.decoder.JSONDecodeError as e: @@ -2182,7 +2130,6 @@ class AppBase: else: basejson = basejson[value] except KeyError as e: - print("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e)) if "_" in value: value = value.replace("_", " ", -1) elif " " in value: @@ -2190,41 +2137,31 @@ class AppBase: try: if isinstance(basejson, list): - #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson, bool): - #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value) return basejson, False elif isinstance(basejson, int): - #print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value) return basejson, False elif isinstance(basejson[value], str): - #print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: - #print("[DEBUG] BASEJSON: %s" % basejson) if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")): basejson = json.loads(basejson[value]) else: if outercnt == len(parsersplit)-1: - #print("LAST KEY (2)") return str(basejson[value]), False else: - #print("NOT LAST KEY (2)") pass except json.decoder.JSONDecodeError as e: - #print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return str(basejson[value]), False else: basejson = basejson[value] except KeyError as e: # Check if previous key was handled or not previouskey = parsersplit[outercnt-1] - #print("[DEBUG] PREVIOUS KEY: ", previouskey) tmpval = previouskey + "." + value - #print("\n\n[WARNING] Running third dot notation fix '%s' on data %s: %s" % (value, basejson, e)) if tmpval in basejson: return basejson[tmpval], False @@ -2237,13 +2174,11 @@ class AppBase: #tmpbase = basejson previouskey = value while True: - #print("\n\n[DEBUG] CURRENTSPLITCNT: ", currentsplitcnt) newvalue = parsersplit[currentsplitcnt+1] if newvalue == "#" or newvalue == "": break recursed_value += "." + newvalue - #print("\n\nRECURSED: ", recursed_value) found = False for key, value in basejson.items(): @@ -2251,19 +2186,10 @@ class AppBase: found = True if found == False: - #print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value) - # Check if we are on the last key or not return "", False - #if outercnt == len(parsersplit)-1: - # print("[DEBUG] LAST KEY (3)") - # break - #else: - # print("[DEBUG] NOT LAST KEY (3)") - # return "", False if recursed_value in basejson: - #print("[INFO] FOUND RECURSED VALUE: ", recursed_value) basejson = basejson[recursed_value] # Whether to dig deeper or not @@ -2281,16 +2207,13 @@ class AppBase: break except IndexError as e: - print("[DEBUG] INDEXERROR (2):", parsersplit[outercnt]) return "", False outercnt += 1 except KeyError as e: - print("[INFO] Lower keyerror: %s" % e) return "", False except Exception as e: - print("[WARNING] Exception: %s" % e) return "", False return basejson, False @@ -2307,7 +2230,6 @@ class AppBase: baseresult = "" appendresult = "" - #print("[INFO] Parsersplit length: %d" % len(parsersplit)) if (actionname_lower.startswith("exec ") or actionname_lower.startswith("webhook ") or actionname_lower.startswith("schedule ") or actionname_lower.startswith("userinput ") or actionname_lower.startswith("email_trigger ") or actionname_lower.startswith("trigger ")) and len(parsersplit) == 1: record = False for char in actionname_lower: @@ -2327,17 +2249,15 @@ class AppBase: if actionname_lower == "exec" or actionname_lower == "webhook" or actionname_lower == "schedule" or actionname_lower == "userinput" or actionname_lower == "email_trigger" or actionname_lower == "trigger": baseresult = execution_data["execution_argument"] elif actionname_lower == "shuffle_cache": - print("[DEBUG] SHOULD GET CACHE KEY: %s" % parsersplit) if len(parsersplit) > 1: actual_key = parsersplit[1] - print("[DEBUG] KEY: %s" % actual_key) cachedata = self.get_cache(actual_key) - print("CACHE: %s" % cachedata) parsersplit.pop(1) try: baseresult = json.dumps(cachedata) except json.decoder.JSONDecodeError as e: - print("[WARNING] Failed json dumping: %s" % e) + pass + else: if execution_data["results"] != None: @@ -2347,7 +2267,6 @@ class AppBase: baseresult = result["result"] break else: - print("[DEBUG] No results to get values from.") baseresult = "$" + parsersplit[0][1:] if len(baseresult) == 0: @@ -2360,10 +2279,8 @@ class AppBase: break except KeyError as e: - #print("[INFO] KeyError wf variables: %s" % e) pass except TypeError as e: - #print("[INFO] TypeError wf variables: %s" % e) pass if len(baseresult) == 0: @@ -2374,34 +2291,27 @@ class AppBase: baseresult = variable["value"] break except KeyError as e: - #print("[INFO] KeyError exec variables: %s" % e) pass except TypeError as e: - #print("[INFO] TypeError exec variables: %s" % e) pass except KeyError as error: - print(f"[DEBUG] KeyError in JSON: {error}") - - #print(f"[INFO] After first trycatch. Baseresult")#, baseresult) + pass # 2. Find the JSON data # Returns if there isn't any JSON in the base ($nodename) if len(baseresult) == 0: return ""+appendresult, False - #print("[INFO] After second return") # Returns if the result is JUST something like $nodename, not $nodename.value if len(parsersplit) == 1: returndata = str(baseresult)+str(appendresult) - print("[DEBUG] RETURNING!")#: %s" % returndata) return returndata, False baseresult = baseresult.replace(" True,", " true,") baseresult = baseresult.replace(" False", " false,") # Tries to actually read it as JSON with some stupid formatting - #print("[INFO] After third parser return - Formatted")#, baseresult) basejson = {} try: basejson = json.loads(baseresult) @@ -2410,10 +2320,8 @@ class AppBase: baseresult = baseresult.replace("\'", "\"") basejson = json.loads(baseresult) except json.decoder.JSONDecodeError as e: - print(f"[ERROR] Parser issue with JSON for {baseresult}: {e}") return str(baseresult)+str(appendresult), False - print("[INFO] After fourth parser return as JSON") # Finds the ACTUAL value which is in the $nodename.value.test - focusing on value.test data, is_loop = recurse_json(basejson, parsersplit[1:]) parseditem = data @@ -2422,17 +2330,13 @@ class AppBase: try: parseditem = json.dumps(parseditem) except json.decoder.JSONDecodeError as e: - print("[WARNING] Parseditem issue: %s" % e) pass if is_loop: - print("[DEBUG] DATA IS A LOOP - SHOULD WRAP") if parsersplit[-1] == "#": - print("[WARNING] SET DATA WRAPPER TO NORMAL!") parseditem = "${SHUFFLE_NO_SPLITTER%s}$" % json.dumps(data) else: # Return value: ${id[12345, 45678]}$ - print("[WARNING] SET DATA WRAPPER TO %s!" % parsersplit[-1]) parseditem = "${%s%s}$" % (parsersplit[-1], json.dumps(data)) @@ -2539,7 +2443,6 @@ class AppBase: newlines = [] thisline = [] for line in template.split("\n"): - #print("LINE: %s" % repr(line)) if "\"\"\"" in line or "\'\'\'" in line: if replace: skip_next = True @@ -2550,7 +2453,6 @@ class AppBase: thisline.append(line) if skip_next == True: if len(thisline) > 0: - #print(thisline) newlines.append(" ".join(thisline)) thisline = [] @@ -2575,7 +2477,6 @@ class AppBase: except TypeError as e: try: if "string as left operand" in f"{e}": - #print(f"HANDLE REPLACE: {template}") split_left = template.split("|") if len(split_left) < 2: return template @@ -2595,8 +2496,6 @@ class AppBase: return run.render(**globals()) except Exception as e: - print(f"SubError in Liquid: {e}") - self.action["parameters"].append({ "name": "liquid_general_error", "value": f"There was general error Liquid input (2). Details: {e}", @@ -2637,7 +2536,6 @@ class AppBase: self.action_result["result"] = json.dumps(data) except Exception as e: self.action_result["result"] = f"Failed to parse LiquidPy: {error_msg}" - print("[WARNING] Failed to set LiquidPy result") self.action_result["completed_at"] = int(time.time_ns()) self.send_result(self.action_result, headers, stream_path) @@ -2671,15 +2569,12 @@ class AppBase: try: value = json.dumps(value) except: - print("[WARNING] Json parsing issue in recursed value") pass if value == "${%s}" % key: - print("[WARNING] Deleting %s because key = value" % key) deletekeys.append(key) continue elif "${" in value and "}" in value: - print("[WARNING] Deleting %s because it contains ${ and }" % key) deletekeys.append(key) continue @@ -2689,9 +2584,9 @@ class AppBase: except json.decoder.JSONDecodeError as e: # Since here the data isn't at all JSON compatible..? # Seems to happen with newlines in variables being parsed in as strings? - print(f"[ERROR] Failed JSON replacement for OpenAPI keys (3) {e}. Value: {data}") + pass except Exception as e: - print(f"[ERROR] Failed as an exception (1): {e}") + pass try: for deletekey in deletekeys: @@ -2700,7 +2595,6 @@ class AppBase: except: pass except Exception as e: - print(f"[WARNING] Failed in deletekeys: {e}") return data try: @@ -2721,13 +2615,12 @@ class AppBase: try: data = json.dumps(newvalue) except json.decoder.JSONDecodeError as e: - print("[WARNING] JsonDecodeError: %s" % e) data = newvalue except json.decoder.JSONDecodeError as e: - print("[WARNING] Failed JSON replacement for OpenAPI keys (2) {e}") + pass except Exception as e: - print(f"[WARNING] Failed as an exception (2): {e}") + pass return data @@ -2746,7 +2639,7 @@ class AppBase: value = value.replace("\\\'", "\'") value = value.replace("\'", "\\\'") except Exception as e: - print(f"[WARNING] Failed to fix json string value: {e}") + pass return value @@ -2823,7 +2716,6 @@ class AppBase: # 2. Check if there is a quote infront of it and also if there are {} in the data to validate JSON # 3. If there are, sanitize! #if data.find(f'"{to_be_replaced}"') != -1 and data.find("{") != -1 and data.find("}") != -1: - # print(f"[DEBUG] Found quotes infront of and after {to_be_replaced}! This probably means it's JSON and should be sanitized.") # returnvalue = fix_json_string_value(value) # value = returnvalue @@ -2987,7 +2879,6 @@ class AppBase: continue if item.strip() in sourcevalue: - print("[INFO] Found %s in %s" % (item, sourcevalue)) return True elif check.lower() == "larger than" or check.lower() == "bigger than": @@ -3020,7 +2911,7 @@ class AppBase: return True except AttributeError as e: - print("[WARNING] Condition smaller than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + pass try: destinationvalue = len(json.loads(destinationvalue)) @@ -3038,10 +2929,8 @@ class AppBase: try: found = re.search(str(destinationvalue), str(sourcevalue)) except re.error as e: - print("[WARNING] Regex error in condition (re.error): %s" % e) return False except Exception as e: - print("[WARNING] Regex error in condition (catchall): %s" % e) return False if found == None: @@ -3068,30 +2957,6 @@ class AppBase: if action["id"] == fullexecution["start"]: return True, "" - # Need to validate if the source is a trigger or not - # need to remove branches that are not from trigger to the startnode to make it all work - #if "workflow" in fullexecution["workflow"] and "triggers" in fullexecution["workflow"]: - # cnt = 0 - # found_branch_indexes = [] - # for branch in fullexecution["workflow"]["branches"]: - # if branch["destination_id"] != action["id"]: - # continue - - # # Check if the source is a trigger - # # if we can't find it as trigger, remove the branch - # print("Found relevant branch: %s" % branch) - # for action in fullexecution["workflow"]["actions"]: - # if action["id"] == branch["source_id"]: - # found_branch_indexes.append(branch["source_id"]) - # break - - # if len(found_branch_indexes) > 0: - # for i in sorted(found_branch_indexes, reverse=True): - # fullexecution["workflow"]["branches"].pop(i) - - # print("Removed %d branches" % len(found_branch_indexes)) - #else: - # print("[WARNING] No branches or triggers found in fullexecution for startnode") except Exception as error: self.logger.info(f"[WARNING] Failed checking startnode: {error}") #return True, "" @@ -3157,8 +3022,6 @@ class AppBase: successful_conditions = 0 total_conditions = len(branch["conditions"]) for condition in branch["conditions"]: - self.logger.info("[DEBUG] Getting condition value of %s" % condition) - # Parse all values first here sourcevalue = condition["source"]["value"] check, sourcevalue, is_loop = parse_params(action, fullexecution, condition["source"], self) @@ -3251,8 +3114,6 @@ class AppBase: if " " in actionname: actionname.replace(" ", "_", -1) - #print("ACTION: ", action) - #print("exec: ", self.full_execution) #if action.generated: # actionname = actionname.lower() @@ -3647,7 +3508,7 @@ class AppBase: if str(value).startswith("b'") and str(value).endswith("'"): value = value[2:-1] except Exception as e: - print(f"Value rawbytes Exception: {e}") + pass params[parameter["name"]] = value multi_parameters[parameter["name"]] = value @@ -3668,7 +3529,7 @@ class AppBase: #remove_params.append(parameter["name"]) # Fix lists here # FIXME: This doesn't really do anything anymore - self.logger.info("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) + #self.logger.info("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) if len(multi_execution_lists) > 0: self.logger.info("\n [DEBUG] Multi execution list has more data: %d" % len(multi_execution_lists)) filteredlist = [] @@ -3730,7 +3591,7 @@ class AppBase: self.send_result(self.action_result, headers, stream_path) return - self.logger.info("[INFO] Running normal execution (not loop)\n\n") + #self.logger.info("[INFO] Running normal execution (not loop)\n\n") # Added literal evaluation of anything resembling a string # The goal is to parse objects that e.g. use single quotes and the like @@ -3811,7 +3672,6 @@ class AppBase: else: # The future is done, so we can just get the result from newres :) #newres = future.result() - #print("Future is done!") pass except concurrent.futures.TimeoutError as e: @@ -3892,7 +3752,7 @@ class AppBase: except Exception as e: self.logger.warning("[ERROR] Failed to parse coroutine value for old app: {e}") - self.logger.info("\n\n\n[INFO] Returned from execution with type(s) %s" % type(newres)) + #self.logger.info("\n\n\n[INFO] Returned from execution with type(s) %s" % type(newres)) #self.logger.info("\n[INFO] Returned from execution with %s of types %s" % (newres, type(newres)))#, newres) if isinstance(newres, tuple): self.logger.info(f"[INFO] Handling return as tuple: {newres}") @@ -3997,7 +3857,7 @@ class AppBase: if self.action_result["result"] == "": self.action_result["result"] = result - self.logger.debug(f"[DEBUG] Executed {action['label']}-{action['id']}")#with result: {result}") + #self.logger.debug(f"[DEBUG] Executed {action['label']}-{action['id']}")#with result: {result}") #self.logger.debug(f"Data: %s" % action_result) except TypeError as e: self.logger.info("[ERROR] TypeError issue: %s" % e) @@ -4076,16 +3936,16 @@ class AppBase: logger = logging.getLogger(f"{cls.__name__}") logger.setLevel(logging.DEBUG) - logger.info("[DEBUG] Normal execution.") + #logger.info("[DEBUG] Normal execution.") ############################################## exposed_port = os.getenv("SHUFFLE_APP_EXPOSED_PORT", "") - logger.info(f"[DEBUG] \"{runtime}\" - run indicates microservices. Port: \"{exposed_port}\"") + #logger.info(f"[DEBUG] \"{runtime}\" - run indicates microservices. Port: \"{exposed_port}\"") if runtime == "run" and exposed_port != "": # Base port is 33334. Exposed port may differ based on discovery from Worker port = int(exposed_port) - logger.info(f"[DEBUG] Starting webserver on port {port} (same as exposed port)") + #logger.info(f"[DEBUG] Starting webserver on port {port} (same as exposed port)") from flask import Flask, request from waitress import serve @@ -4100,7 +3960,6 @@ class AppBase: @flask_app.route("/api/v1/run", methods=["POST"]) def execute(): if request.method == "POST": - #print(request.get_json(force=True)) requestdata = {} try: requestdata = json.loads(request.data) @@ -4110,8 +3969,6 @@ class AppBase: "reason": f"Invalid Action data {e}", } - #logger.info(f"[DEBUG] Datatype: {type(requestdata)}: {requestdata}") - # Remaking class for each request app = cls(redis=None, logger=logger, console_logger=logger) @@ -4122,41 +3979,33 @@ class AppBase: try: app.full_execution = json.dumps(requestdata["workflow_execution"]) except Exception as e: - logger.info(f"[ERROR] Failed parsing full execution from workflow_execution: {e}") extra_info += f"\n{e}" try: app.action = requestdata["action"] except Exception as e: - logger.info(f"[ERROR] Failed parsing action: {e}") extra_info += f"\n{e}" try: app.authorization = requestdata["authorization"] app.current_execution_id = requestdata["execution_id"] except Exception as e: - logger.info(f"[ERROR] Failed parsing auth and exec id: {e}") extra_info += f"\n{e}" # BASE URL (backend) try: app.url = requestdata["url"] - logger.info(f"BACKEND URL (url): {app.url}") except Exception as e: - logger.info(f"[ERROR] Failed parsing url (backend): {e}") extra_info += f"\n{e}" # URL (worker) try: app.base_url = requestdata["base_url"] - logger.info(f"WORKER URL (base url): {app.base_url}") except Exception as e: - logger.info(f"[ERROR] Failed parsing base url (worker): {e}") extra_info += f"\n{e}" #await app.execute_action(app.action) - logger.info("[DEBUG] Done awaiting app action running") except Exception as e: return { "success": False, @@ -4199,12 +4048,12 @@ class AppBase: # Has to start like this due to imports in other apps # Move it outside everything? app = cls(redis=None, logger=logger, console_logger=logger) - #logger.info(f"[DEBUG] Action: {action}") if isinstance(action, str): - logger.info("[DEBUG] Normal execution (env var). Action is a string.") + #logger.info("[DEBUG] Normal execution (env var). Action is a string.") + pass elif isinstance(action, object): - logger.info("[DEBUG] OBJECT execution (cloud). Action is NOT a string.") + #logger.info("[DEBUG] OBJECT execution (cloud). Action is NOT a string.") app.action = action try: @@ -4225,7 +4074,8 @@ class AppBase: except: pass else: - self.logger.info("ACTION TYPE (unhandled): %s" % type(action)) + #self.logger.info("ACTION TYPE (unhandled): %s" % type(action)) + pass app.execute_action(app.action) From fb626a7c21532ecb00d8bea5dd39add1bce21c1a Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 28 Feb 2024 23:09:50 +0100 Subject: [PATCH 023/142] Fixed minor schedule location issues and app build revisions --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 + backend/go-app/walkoff.go | 4 +- frontend/src/components/Billing.jsx | 22 ++++++--- frontend/src/components/Files.jsx | 41 ++++++++++++++--- frontend/src/views/AngularWorkflow.jsx | 62 +++++++++++++++++++------- frontend/src/views/Apps.jsx | 4 +- 7 files changed, 104 insertions(+), 33 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index ccfc643b..c7094cb8 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.78 + github.com/shuffle/shuffle-shared v0.5.81 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index bdd5b7b7..8033f69e 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -457,6 +457,8 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE= github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.5.81 h1:pt4lT42FrXN/kd/vlYtm7nXShZI0mOamXUMjpWLH7qI= +github.com/shuffle/shuffle-shared v0.5.81/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 21a26f8e..40f9ba3a 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -3183,8 +3183,8 @@ func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) { workflowapp.Generated = false workflowapp.Activated = true - if !shuffle.ArrayContains(api.Contributors, user.Id) { - api.Contributors = append(api.Contributors, user.Id) + if !shuffle.ArrayContains(workflowapp.Contributors, user.Id) { + workflowapp.Contributors = append(workflowapp.Contributors, user.Id) } shuffle.SetAppRevision(ctx, workflowapp) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 4152ce2a..26e5b857 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -874,10 +874,11 @@ const Billing = (props) => { }); }; + const isChildOrg = userdata.active_org.creator_org !== "" && userdata.active_org.creator_org !== undefined && userdata.active_org.creator_org !== null return (
{addDealModal} - + Billing @@ -912,8 +913,16 @@ const Billing = (props) => { : null } + + + {isChildOrg ? + + Billing is handled by your parent organisation. Reach out to support@shuffler.io if you have questions about this. + + : null} +
- {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? + {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : { /> : null} + {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && - selectedOrganization.subscriptions.length > 0 ? - + selectedOrganization.subscriptions.length > 0 && + !isChildOrg ? selectedOrganization.subscriptions .reverse() .map((sub, index) => { @@ -1224,9 +1234,9 @@ const Billing = (props) => {
- Shuffle Utilization + Utilization & Stats
{ }; const downloadFile = (file) => { - fetch(globalUrl + "/api/v1/files/" + file.id + "/content", { - method: "GET", - credentials: "include", - }) + fetch(globalUrl + "/api/v1/files/" + file.id + "/content", { + method: "GET", + credentials: "include", + }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for apps :O!"); @@ -972,6 +973,32 @@ const Files = (props) => { + {/* + + + { + // Open the file, without downloading it + window.open(`${globalUrl}/api/v1/files/${file.id}/content?type=text&authorization=${file.public_authorization}`, "_blank noreferrer noopener") + }} + > + + + + + */} { ReactDOM.unstable_batchedUpdates(() => { setSelectedAction({}); setSelectedApp({}); - setSelectedTrigger({}); setSelectedComment({}) setSelectedEdge({}); + setSelectedEdge({}) setSelectedActionEnvironment({}) setTriggerAuthentication({}) - setSelectedTriggerIndex(-1) setTriggerFolders([]) setLocalFirstrequest(true) + setSelectedTrigger({}); + setSelectedTriggerIndex(-1) + setUpdate(Math.random()) + // Can be used for right side view setRightSideBarOpen(false); setScrollConfig({ @@ -3551,7 +3554,9 @@ const AngularWorkflow = (defaultprops) => { const onNodeSelect = (event, newAppAuth) => { // Forces all states to update at the same time, // Otherwise everything is SUPER slow - const data = event.target.data(); + + //const data = JSON.parse(JSON.stringify(event.target.data())) + const data = event.target.data() if (data.isSuggestion === true) { console.log("Suggestion! Replace with a real action.") @@ -4171,9 +4176,11 @@ const AngularWorkflow = (defaultprops) => { console.log("TRIGGER: ", data) - setSelectedTriggerIndex(trigger_index); - setSelectedTrigger(data); - setSelectedActionEnvironment(data.env); + setTimeout(() => { + setSelectedTriggerIndex(trigger_index); + setSelectedTrigger(data) + setSelectedActionEnvironment(data.env) + }, 25) } else if (data.type === "COMMENT") { setSelectedComment(data); } else { @@ -6993,13 +7000,14 @@ const AngularWorkflow = (defaultprops) => { }); }; + const parsedHeight = isMobile ? bodyHeight - appBarSize * 4 : bodyHeight - appBarSize - 50 const appViewStyle = { marginLeft: 5, marginRight: 5, display: "flex", flexDirection: "column", - minHeight: isMobile ? bodyHeight - appBarSize * 4 : "100%", - maxHeight: isMobile ? bodyHeight - appBarSize * 4 : "100%", + minHeight: isMobile ? bodyHeight - appBarSize * 4 : parsedHeight, + maxHeight: isMobile ? bodyHeight - appBarSize * 4 : parsedHeight, }; const paperAppStyle = { @@ -7321,7 +7329,6 @@ const AngularWorkflow = (defaultprops) => { marginRight: 5, }; - const parsedHeight = isMobile ? bodyHeight - appBarSize * 4 : bodyHeight - appBarSize - 50 return (
{ workflow.triggers[selectedTriggerIndex].parameters[0] = { value: value, name: "cron", - }; + } } workflow.triggers[selectedTriggerIndex].parameters[1] = { @@ -13289,7 +13296,7 @@ const AngularWorkflow = (defaultprops) => { const pipelineConfig = { "name": selectedTrigger.label, "type": "create", - "command": "load tcp://0.0.0.0:514 | read syslog | to http://api.com X-Token:Secret", + "command": "load tcp://0.0.0.0:514 | read syslog | export", "environment": selectedTrigger.environment, } @@ -13299,6 +13306,29 @@ const AngularWorkflow = (defaultprops) => { Start Syslog listener
+
{ + const pipelineConfig = { + "name": selectedTrigger.label, + "type": "create", + "command": "export --live | sigma /path/to/rules | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines", + "environment": selectedTrigger.environment, + } + + submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) + }} + > + Run Sigma Rulesearch +
+
{ }} fullWidth disabled={ - workflow.triggers[selectedTriggerIndex].status === "running" + selectedTrigger.status === "running" } defaultValue={ - workflow.triggers[selectedTriggerIndex].parameters === undefined ? "" : workflow.triggers[selectedTriggerIndex].parameters[0].value + selectedTrigger.parameters === undefined ? "" : selectedTrigger.parameters[0].value } color="primary" placeholder="" @@ -13544,7 +13574,7 @@ const AngularWorkflow = (defaultprops) => { multiline color="primary" defaultValue={ - workflow.triggers[selectedTriggerIndex] !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== null && workflow.triggers[selectedTriggerIndex].parameters.length > 1 ? + workflow.triggers[selectedTriggerIndex] !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== null && workflow.triggers[selectedTriggerIndex].parameters.length > 1 ? workflow.triggers[selectedTriggerIndex].parameters[1].value : "" } @@ -14022,8 +14052,8 @@ const AngularWorkflow = (defaultprops) => { }} > - - {workflow.errors.length} Workflow Issue{workflow.errors.length > 1 ? "s" : ""} + {/**/} + Workflow Issues: {workflow.errors.length} { })} - {isCloud && (selectedApp.sharing === true || selectedApp.public === true || creatorProfile.github_avatar !== undefined) && !internalIds.includes(selectedApp.name.toLowerCase()) ? + {/*isCloud && (selectedApp.sharing === true || selectedApp.public === true || creatorProfile.github_avatar !== undefined) && !internalIds.includes(selectedApp.name.toLowerCase()) */} + + {isCloud && !internalIds.includes(selectedApp.name.toLowerCase()) ? : null*/} + + {userdata.has_card_available === true ? + + : null} + + {userdata.has_card_available === false ? + + : null} : null} {showSupport ? @@ -1046,7 +1129,7 @@ const Billing = (props) => {
- {isCloud && + {/*isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true ? (
@@ -1230,7 +1313,7 @@ const Billing = (props) => { />
- ) : null} + ) : null*/}
Date: Thu, 29 Feb 2024 13:55:51 +0100 Subject: [PATCH 026/142] Made app sdk support local caching for cache keys for a very short amount of time as to make it possible to check a ton of keys fast --- backend/app_sdk/app_base.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index df6ee007..d37a3934 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -875,7 +875,6 @@ class AppBase: #self.logger.info(f"MERGE: {should_merge}") if isinstance(value, list): - self.logger.info(f"[DEBUG] Item {value} is a list.") if len(value) <= 1: if len(value) == 1: baseparams[key] = value[0] @@ -986,7 +985,6 @@ class AppBase: self.logger.info("[WARNING] Exception in loop wrapper: {e}") loop_wrapper[key] = 1 - self.logger.info(f"[DEBUG] Key {key} is a list: {value}") newparams[key] = value[0] has_loop = True else: @@ -1046,8 +1044,6 @@ class AppBase: # self.send_result(action_result, headers, stream_path) # return - self.logger.info("[INFO] Multiplier length: %d" % len(param_multiplier)) - #tmp = "" for subparams in param_multiplier: #self.logger.info(f"SUBPARAMS IN MULTI: {subparams}") try: @@ -3303,7 +3299,6 @@ class AppBase: # Has a loop without a variable used inside if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": - self.logger.info("(1) Pre replacement: %s" % actualitem[0][2]) tmpitem = value index = 0 @@ -3346,12 +3341,10 @@ class AppBase: try: newvalue = json.loads(newvalue) except json.decoder.JSONDecodeError as e: - self.logger.info("DECODER ERROR: %s" % e) pass new_replacement.append(newvalue) - self.logger.info("New replacement: %s" % new_replacement) # FIXME: Should this use new_replacement? tmpitem = tmpitem.replace(actualitem[index][0], replacement, 1) @@ -3378,11 +3371,9 @@ class AppBase: self.logger.info("(1) JSON ERROR IN FILE HANDLING: %s" % e) if not isfile: - self.logger.info("Resultarray (NOT FILE): %s" % resultarray) params[parameter["name"]] = tmpitem multi_parameters[parameter["name"]] = new_replacement else: - self.logger.info("Resultarray (FILE): %s" % resultarray) params[parameter["name"]] = resultarray multi_parameters[parameter["name"]] = resultarray @@ -3396,7 +3387,6 @@ class AppBase: multi_execution_lists.append(new_replacement) #self.logger.info("MULTI finished: %s" % json_replacement) else: - self.logger.info(f"(2) Pre replacement (loop with variables). Variables: {actualitem}") #% actualitem) # This is here to handle for loops within variables.. kindof # 1. Find the length of the longest array # 2. Build an array with the base values based on parameter["value"] @@ -3531,7 +3521,6 @@ class AppBase: # FIXME: This doesn't really do anything anymore #self.logger.info("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) if len(multi_execution_lists) > 0: - self.logger.info("\n [DEBUG] Multi execution list has more data: %d" % len(multi_execution_lists)) filteredlist = [] for listitem in multi_execution_lists: if listitem in filteredlist: @@ -3580,11 +3569,10 @@ class AppBase: if not multiexecution: # Runs a single iteration here new_params = self.validate_unique_fields(params) - self.logger.info(f"[DEBUG] Returned with newparams of length {len(new_params)}") if isinstance(new_params, list) and len(new_params) == 1: params = new_params[0] else: - self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") + #self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") self.action_result["status"] = "SKIPPED" self.action_result["result"] = f"A non-unique value was found" self.action_result["completed_at"] = int(time.time_ns()) @@ -3755,7 +3743,7 @@ class AppBase: #self.logger.info("\n\n\n[INFO] Returned from execution with type(s) %s" % type(newres)) #self.logger.info("\n[INFO] Returned from execution with %s of types %s" % (newres, type(newres)))#, newres) if isinstance(newres, tuple): - self.logger.info(f"[INFO] Handling return as tuple: {newres}") + #self.logger.info(f"[INFO] Handling return as tuple: {newres}") # Handles files. filedata = "" file_ids = [] @@ -3780,7 +3768,7 @@ class AppBase: result = json.dumps(tmp_result) elif isinstance(newres, str): - self.logger.info("[INFO] Handling return as string of length %d" % len(newres)) + #self.logger.info("[INFO] Handling return as string of length %d" % len(newres)) result += newres elif isinstance(newres, dict) or isinstance(newres, list): try: @@ -3791,7 +3779,7 @@ class AppBase: result += str(newres) except ValueError: result += "Failed autocasting. Can't handle %s type from function. Must be string" % type(newres) - self.logger.info("Can't handle type %s value from function" % (type(newres))) + self.logger.info("[ERROR] Can't handle type %s value from function" % (type(newres))) except Exception as e: self.logger.info("[ERROR] Failed to json dump. Returning as string.") result += str(newres) @@ -3817,7 +3805,6 @@ class AppBase: # Dump the result as a string of a list #self.logger.info("RESULTS: %s" % results) if isinstance(results, list) or isinstance(results, dict): - self.logger.info(f"JSON OBJECT? {json_object}") # This part is weird lol if json_object: From cdf0a1eee768c10228dd6c6fca7cb237b6c2dab1 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Mon, 4 Mar 2024 12:28:21 +0000 Subject: [PATCH 027/142] added list of sub orgs --- frontend/src/views/Admin.jsx | 112 +++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index f653bd2c..e6dc8bc0 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1102,6 +1102,40 @@ If you're interested, please let me know a time that works for you, or set up a toast("Error getting current organization"); }); }; +const handleGetSubOrgs = (orgId) => { + + if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundorgid = params["org_id"]; + if (foundorgid !== undefined && foundorgid !== null) { + orgId = foundorgid; + } + } + + if (orgId.length === 0) { + toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); + return; + } + + fetch(`${globalUrl}/api/v1/subOrgs/${orgId}`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + return response.json(); + }) + .then((responseJson) => { + setSubOrgs(responseJson); + }) + .catch((error) => { + console.log("Error getting sub orgs: ", error); + toast("Error getting sub organizations"); + }); +}; const inviteUser = (data) => { //console.log("INPUT: ", data); @@ -4678,6 +4712,84 @@ If you're interested, please let me know a time that works for you, or set up a backgroundColor: theme.palette.inputColor, }} /> + +{subOrgs.length > 0 ? ( + +
+

Your Sub Organizations of the Current Organization

+
+ + + + + + + + + + + + {subOrgs.map((data, index) => { + const imagesize = 40; + const imageStyle = { + width: imagesize, + height: imagesize, + pointerEvents: "none", + }; + const image = + data.image === "" ? ( + {data.name} + ) : ( + {data.name} + ); + + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + return ( + + + + + + + ); + })} + + + + +
+) : ( + + No Sub-Organizations available. + +)} +
+

All Tenants

+
+ + Date: Mon, 4 Mar 2024 12:47:01 +0000 Subject: [PATCH 028/142] Revert "fixed overflow in conditions" This reverts commit 906333d8a2dea3d8a2d486c3b20803edf001c059. --- frontend/src/views/AngularWorkflow.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3f397674..2f461690 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -9767,8 +9767,6 @@ const AngularWorkflow = (defaultprops) => { marginTop: "15px", marginLeft: "10px", overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", maxWidth: 72, }} > @@ -9788,7 +9786,7 @@ const AngularWorkflow = (defaultprops) => { flex: 1, textAlign: "center", marginTop: "15px", - overflow: "hidden", + overflow: "hidden", maxWidth: 72, }} onClick={() => { }} @@ -9812,8 +9810,6 @@ const AngularWorkflow = (defaultprops) => { marginBottom: "auto", marginLeft: "10px", overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", maxWidth: 72, }} > From b2a126e6b174526c2b3911eab468ba5ca0832c4f Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Tue, 5 Mar 2024 05:46:46 +0000 Subject: [PATCH 029/142] dynamic app loading for notification workflow --- frontend/src/components/OrgHeaderexpanded.jsx | 389 +++++++++++++++--- 1 file changed, 342 insertions(+), 47 deletions(-) diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 7729057d..350b456c 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -5,6 +5,7 @@ import theme from '../theme.jsx'; import { toast } from "react-toastify" import Chip from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; +import AuthenticationData from "./AuthenticationWindow"; import { FormControl, @@ -173,10 +174,32 @@ const OrgHeaderexpanded = (props) => { const [webhookInputValue, setWebhookInputValue] = React.useState(""); const [authOptions, setAuthOptions] = React.useState([]); const [selectedAuth, setSelectedAuth] = React.useState(''); + // const [selectedAppAuth, setSelectedAppAuth] = React.useState({}); // for getting selected app auth parameters + const [authenticationModal, setAuthenticationModal] = React.useState(false); - // for jira modal - const [jiraIssueType, setJiraIssueType] = React.useState(""); - const [jiraProjectKey, setJiraProjectKey] = React.useState(""); + const [notificationAppDetails, setNotificationAppDetails] = React.useState([]); + + // const [tempResult, setTempResult] = React.useState([]) + + // useEffect(() => { + // if (authOptions.length < 1) { + // {getAppConfig(selectedAppDetails.id)} + // } + // }, [authOptions.length]); // not using this now + + useEffect(() => { + if (notificationAppList.length > 0) { + (async () => { + const nameList = notificationAppList.map(item => item.name); + await prepareNotificationAppList(nameList); + })(); + } + }, [notificationAppList]); + + + // for jira & email modal + const [textFieldValue, setTextFieldValue] = React.useState(""); + const [textFieldOneValue, setTextFieldOneValue] = React.useState(""); const getAvailableWorkflows = (trigger_index) => { fetch(globalUrl + "/api/v1/workflows", { @@ -315,6 +338,146 @@ const OrgHeaderexpanded = (props) => { ); + 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); + }) +} + + // getting comms & cases app from app framework + var notificationAppList = []; + if (selectedOrganization.security_framework.cases && selectedOrganization.security_framework.cases.name.length > 0) { + notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.cases); + } + if (selectedOrganization.security_framework.communication && selectedOrganization.security_framework.communication.name.length > 0) { + notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.communication); + } + + const mergeAuthData = (result, responseJson) => { + const updatedResult = result.map(item => { + const matches = responseJson.filter(authItem => authItem.app.name === item.name); + return { + ...item, + authentication_data: matches.length > 0 ? matches : null + }; + }); + return updatedResult; + }; + + const prepareNotificationAppList = async (appList) => { + // getting App ID,Authentication fields and saved auths for each app + var result = [] + if (appList.length > 0) { + 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) { + const filteredApps = responseJson.filter(app => appList.includes(app.name)); + const appDetails = filteredApps.map(app => ({ name: app.name, id: app.id })); //mapped apps with IDs as sometime Ids were not correct in security framework + // console.log("appDetails: ", appDetails) + // result = appDetails + + fetch(globalUrl + "/api/v1/apps/authentication", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + toast(`Failed getting auth for : `, response.reason); + console.log("Status not 200 for app auth :O!"); + return; + } + return response.json(); + }).then(async (responseJson) => { + if (!responseJson.success) { + console.log("Could not get app auth") + return; + } + // console.log("responseJson of auth: ", responseJson.data) + result = await mergeAuthData(appDetails, responseJson.data) + console.log("merged auth data: ", result) + // console.log("result", result) + result.map(item => { + fetch(globalUrl + `/api/v1/apps/${item.id}/config`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }).then((response) => { + if (response.status !== 200) { + toast(`Failed getting config for ${item.id}: `, response.reason); + console.log("Status not 200 for app config :O!"); + return; + } + return response.json(); + }).then((responseJson) => { + if (!responseJson.success) { + console.log("Could not get app config") + return; + } + var decodedString = JSON.parse(atob(responseJson.app)); + // console.log("dcodedString: ",decodedString) + item.auth_config = decodedString.authentication + item.large_image = decodedString.large_image + setNotificationAppDetails(result) + console.log("notificationAppDetails: ", notificationAppDetails) + // setSelectedAppAuth(decodedString.authentication) + }).catch((error) => { + console.log("Error getting app config: " + error); + toast("Error getting app config: " + error); + }) + }) + // get auth config for each app as it is required to render the modal when auth is not available + }) + } + }).catch((error) => { + console.log("Error getting app ids: " + error); + }) + } + } + + const executeTestWorkflow = async (workflowid) => { const data = { "execution_argument": '{"title":"THIS IS TEST ALERT","description":"TEST ALERT FROM SHUFFLE","reference_url": "shuffler.io"}' } fetch(globalUrl + `/api/v1/workflows/${workflowid}/execute`, { @@ -342,7 +505,7 @@ const OrgHeaderexpanded = (props) => { const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId,issuetype) => { //currently only supports JIRA figure out a way to support more apps var workflowName = `[GENARATED] ${appname} notification workflow` - var workflowDescription = "Generated by Shuffle for sending error notifications." + var workflowDescription = "Generated by Shuffle for sending info/error notifications." var data = { "name": workflowName, "description": workflowDescription, @@ -467,6 +630,121 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId }) } +const generateEmailNotificationWorkflow = async (appname,appImage,shuffleAPIKey,recepients) => { + //currently only supports figure out a way to support more apps + var workflowName = `[GENARATED] ${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() === "email"){ + console.log("updating workflow for email") + var workflowBody = { + "name": workflowName, + "Description": workflowDescription, + "id": workflow_id, + "actions": [ + { + "app_name": "email", + "name": "send_email_shuffle", + "large_image":appImage, + "isStartNode": true, + "label": "send_email_shuffle", + "app_version": "1.3.0", + "parameters": [ + { + "name": "apikey", + "value": shuffleAPIKey + }, + { + "name": "recipients", + "value": recepients + }, + { + "name": "subject", + "value": "$exec.title" + }, + { + "name":"body", + "value":"$exec.description" + } + ] + } + ] + } + } + fetch(globalUrl + `/api/v1/workflows/${workflow_id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(workflowBody), + 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) { + handleEditOrg( + orgName, + orgDescription, + selectedOrganization.id, + selectedOrganization.image, + { + app_download_repo: appDownloadUrl, + app_download_branch: appDownloadBranch, + workflow_download_repo: workflowDownloadUrl, + workflow_download_branch: workflowDownloadBranch, + notification_workflow: workflow_id, + documentation_reference: documentationReference, + }, + { + sso_entrypoint: ssoEntrypoint, + sso_certificate: ssoCertificate, + client_id: openidClientId, + client_secret: openidClientSecret, + openid_authorization: openidAuthorization, + openid_token: openidToken, + } + ) + console.log("Notification workflow updated successfully") + toast("Notification workflow updated successfully") + } + }) + } + }).catch((error) => { + console.log("Error setting workflows: " + error); + }) +} + const getAppAuth = async (appName) => { fetch(globalUrl + "/api/v1/apps/authentication", { method: "GET", @@ -497,6 +775,35 @@ const getAppAuth = async (appName) => { }) } +const getAppConfig = async (appId) => { + fetch(globalUrl + `/api/v1/apps/${appId}/config`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }).then((response) => { + if (response.status !== 200) { + toast(`Failed getting config for ${appId}: `, response.reason); + console.log("Status not 200 for app config :O!"); + return; + } + return response.json(); + }).then((responseJson) => { + if (!responseJson.success) { + console.log("Could not get app config") + return; + } + var decodedString = JSON.parse(atob(responseJson.app)); + console.log("dcodedString: ",decodedString) + return decodedString.authentication + // setSelectedAppAuth(decodedString.authentication) + }).catch((error) => { + console.log("Error getting app config: " + error); + }) +} + const testWorkflowModal = notificationWorkflowTestModal ? ( - - {authOptions.length > 0 ? + {console.log("len Selected app details: ", selectedAppDetails)} + {(selectedAppDetails.authentication_data || selectedAppDetails.auth_config.required == false) ? <> + {selectedAppDetails.auth_config.required == false ? "No authentication required": + <> Pick an authentication method from the list @@ -582,14 +891,15 @@ const modalView = notificationWorkflowModal ? ( - + } @@ -616,10 +926,10 @@ const modalView = notificationWorkflowModal ? ( id="outlined-with-placeholder" margin="normal" variant="outlined" - placeholder="Project key" + placeholder={ selectedAppDetails.name.toLowerCase() === "jira" ? "Project key" : "Shuffle API key"} // value={webhookInputValue} onChange={(e) => { - setJiraProjectKey(e.target.value) + setTextFieldOneValue(e.target.value) }} InputProps={{ classes: { @@ -642,10 +952,10 @@ const modalView = notificationWorkflowModal ? ( id="outlined-with-placeholder" margin="normal" variant="outlined" - placeholder="Issue type" + placeholder={selectedAppDetails.name.toLowerCase() === "jira" ? "Issue type" : "Recepients (comma separated)"} // value={webhookInputValue} onChange={(e) => { - setJiraIssueType(e.target.value) + setTextFieldValue(e.target.value) }} InputProps={{ classes: { @@ -654,28 +964,18 @@ const modalView = notificationWorkflowModal ? ( style: { color: "white", }, - }} /> + }} /> - : <> - - {`No ${selectedAppDetails.name} auth found. Click below to set one up.`} - - + : + <> + 0) ? false : true} + // // setAuthenticationModalOpen={false} + selectedApp={{...selectedAppDetails,authentication: selectedAppDetails.auth_config}} + // getAppAuthentication={selectedAppDetails.name} + /> } @@ -692,11 +992,12 @@ const modalView = notificationWorkflowModal ? ( ) : null -// getting comms & cases app from app framework -var notificationAppList = []; -if (selectedOrganization.security_framework.cases && selectedOrganization.security_framework.cases.name.length > 0) { -notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.cases); -} -if (selectedOrganization.security_framework.communication && selectedOrganization.security_framework.communication.name.length > 0) { -notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.communication); -} const renderChips = (apps) => { if (!apps || apps.length === 0) { @@ -725,7 +1018,9 @@ if (!apps || apps.length === 0) { variant="outlined" onClick={() => { console.log(`Clicked EMAIL`) + setSelectedAppDetails("email") setNotificationWorkflowModal(true) + // setNotificationWorkflowModal(true) }} avatar={{"email} /> @@ -743,8 +1038,8 @@ return ( console.log(`Clicked ${app.name}`) setSelectedAppDetails(app) setNotificationWorkflowModal(true) - getAppAuth(app.name) - console.log(selectedAppDetails) + // getAppAuth(app.name) + console.log("selectedAppDEtails",selectedAppDetails) }} avatar={{app.name}} /> @@ -763,7 +1058,7 @@ return ( {modalView} {/*{testWorkflowModal} */}
- {renderChips(notificationAppList)}
+ {renderChips(notificationAppDetails)}
{/* Add a Workflow that receives notifications from Shuffle when an error occurs in one of your workflows From 1e510c6b84a3936c65348ec0a3337ece99da925e Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Sat, 24 Feb 2024 11:37:36 +0000 Subject: [PATCH 030/142] Added a way to get the sub orgs of the current org --- backend/go-app/main.go | 1 + frontend/src/views/Admin.jsx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b2e901d5..8b4264d5 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4923,6 +4923,7 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") + r.HandleFunc("/api/v1/subOrg/{orgId}", shuffle.HandleGetSubOrg).Methods("GET", "OPTIONS") // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index e6dc8bc0..674eaeb0 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -162,6 +162,7 @@ const Admin = (props) => { const [loginInfo, setLoginInfo] = React.useState(""); const [curTab, setCurTab] = React.useState(0); const [users, setUsers] = React.useState([]); + const [subOrgs, setSubOrgs] = useState([]); const [organizations, setOrganizations] = React.useState([]); const [orgSyncResponse, setOrgSyncResponse] = React.useState(""); const [userSettings, setUserSettings] = React.useState({}); @@ -204,6 +205,10 @@ const Admin = (props) => { } }, [isDropzone]); + useEffect(() => { + handleGetSubOrgs(userdata.active_org.id); + }, []); + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const get2faCode = (userId) => { From 4a3d956023ddd7b8b3b502244174878c89243727 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Mon, 4 Mar 2024 12:28:21 +0000 Subject: [PATCH 031/142] added list of sub orgs --- frontend/src/views/Admin.jsx | 68 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 674eaeb0..629b5a4b 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1142,6 +1142,57 @@ const handleGetSubOrgs = (orgId) => { }); }; +const handleClickChangeOrg = (orgId) => { + // Don't really care about the logout + //name: org.name, + //orgId = "asd" + const data = { + org_id: orgId, + } + + localStorage.setItem("globalUrl", "") + localStorage.setItem("getting_started_sidebar", "open"); + + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: 'cors', + credentials: 'include', + crossDomain: true, + method: 'POST', + body: JSON.stringify(data), + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then(function(response) { + if (response.status !== 200) { + console.log("Error in response") + } + + return response.json(); + }).then(function(responseJson) { + if (responseJson.success === true) { + if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { + console.log("Region Change: ", responseJson.region_url) + localStorage.setItem("globalUrl", responseJson.region_url) + //globalUrl = responseJson.region_url + } + + setTimeout(() => { + window.location.reload() + }, 2000) + toast("Successfully changed active organization - refreshing!") + } else { + toast("Failed changing org: ", responseJson.reason) + } + }) + .catch(error => { + console.log("error changing: ", error) + //removeCookie("session_token", {path: "/"}) + }) +} + + const inviteUser = (data) => { //console.log("INPUT: ", data); setLoginInfo(""); @@ -4761,10 +4812,19 @@ const handleGetSubOrgs = (orgId) => { return ( - - - - + + + + + ); })} From c581618817840e836d27ed5543092107c91f7286 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 5 Mar 2024 07:29:22 +0000 Subject: [PATCH 032/142] fixed few issues with the frontend --- frontend/src/views/Admin.jsx | 77 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 629b5a4b..63800693 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -206,8 +206,12 @@ const Admin = (props) => { }, [isDropzone]); useEffect(() => { - handleGetSubOrgs(userdata.active_org.id); - }, []); + if (userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0) { + handleGetSubOrgs(userdata.active_org.id); + } + else console.log("error in user data") + }, [userdata]); + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; @@ -4770,10 +4774,10 @@ const handleClickChangeOrg = (orgId) => { /> {subOrgs.length > 0 ? ( - -
-

Your Sub Organizations of the Current Organization

-
+ +
+

Sub Organizations of the Current Organization

+
{ return ( - - - - - + + ); })}
- +) : ( +
+

No Sub-Organizations found for the Current Organization

+
+)} + + { }} /> -
-) : ( - - No Sub-Organizations available. - -)} -
-

All Tenants

-
+
+

All Tenants

+
- + Date: Tue, 5 Mar 2024 15:27:11 +0530 Subject: [PATCH 033/142] fixed a typo subOrg --> subOrgs --- backend/go-app/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 8b4264d5..26235d13 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4923,7 +4923,7 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") - r.HandleFunc("/api/v1/subOrg/{orgId}", shuffle.HandleGetSubOrg).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/subOrgs/{orgId}", shuffle.HandleGetSubOrg).Methods("GET", "OPTIONS") // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. From 4b225ab8eb33fef62c10d340e3740f1275ad325e Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 5 Mar 2024 14:35:40 +0100 Subject: [PATCH 034/142] Added a liquid matcher that fixes bad liquid --- backend/app_sdk/app_base.py | 48 ++++++------- backend/app_sdk/autocorrect_test.py | 102 ++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 backend/app_sdk/autocorrect_test.py diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index d37a3934..5ec1703e 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -361,6 +361,7 @@ class AppBase: if len(self.base_url) == 0: self.base_url = self.url + self.local_storage = [] # Checks output for whether it should be automatically parsed or not @@ -718,9 +719,6 @@ class AppBase: #self.logger.info(f"DATA: {data}") # 1594869a676630b397bc34f7dc0951a3 - #self.logger.info(f"VALUE URL: {url}") - #self.logger.info(f"RET: {ret.text}") - #self.logger.info(f"ID: {ret.status_code}") url = f"{self.url}/api/v1/orgs/{org_id}/validate_app_values" ret = requests.post(url, json=data, verify=False, proxies=self.proxy_config) if ret.status_code == 200: @@ -1108,7 +1106,6 @@ class AppBase: except Exception as e: self.logger.warning("[ERROR] Failed to parse coroutine value for old app: {e}") - #self.logger.info("RET from execution: %s" % ret) new_value = tmp if tmp == None: new_value = "" @@ -1244,8 +1241,6 @@ class AppBase: full_execution = self.full_execution org_id = full_execution["workflow"]["execution_org"]["id"] - self.logger.info("SHOULD GET FILES BASED ON ORG %s, workflow %s and value(s) %s" % (org_id, full_execution["workflow"]["id"], value)) - if isinstance(value, list): self.logger.info("IS LIST!") #if len(value) == 1: @@ -1273,7 +1268,6 @@ class AppBase: } ret1 = requests.get("%s%s" % (self.url, get_path), headers=headers, verify=False, proxies=self.proxy_config) - self.logger.info("RET1 (file get): %s" % ret1.text) if ret1.status_code != 200: returns.append({ "filename": "", @@ -1284,7 +1278,6 @@ class AppBase: content_path = "/api/v1/files/%s/content?execution_id=%s" % (item, full_execution["execution_id"]) ret2 = requests.get("%s%s" % (self.url, content_path), headers=headers, verify=False, proxies=self.proxy_config) - self.logger.info("RET2 (file get) done") if ret2.status_code == 200: tmpdata = ret1.json() returndata = { @@ -1294,8 +1287,6 @@ class AppBase: } returns.append(returndata) - self.logger.info("RET3 (file get done)") - if len(returns) == 0: return { "success": False, @@ -1465,7 +1456,6 @@ class AppBase: #self.logger.info(f"Ret CREATE: {ret.text}") cur_id = "" if ret.status_code == 200: - #self.logger.info("RET: %s" % ret.text) ret_json = ret.json() if not ret_json["success"]: self.logger.info("Not success in file upload creation.") @@ -1488,14 +1478,11 @@ class AppBase: } upload_path = "/api/v1/files/%s/upload?execution_id=%s" % (cur_id, full_execution["execution_id"]) - self.logger.info("Create path: %s" % create_path) files={"shuffle_file": (filename, curfile["data"])} #open(filename,'rb')} ret = requests.post("%s%s" % (self.url, upload_path), files=files, headers=new_headers, verify=False, proxies=self.proxy_config) - self.logger.info("Ret UPLOAD: %s" % ret.text) - self.logger.info("Ret2 UPLOAD: %d" % ret.status_code) return file_ids @@ -1657,12 +1644,28 @@ class AppBase: self.full_execution = fullexecution - #try: - # if "backend_url" in self.full_execution: - # self.url = self.full_execution["backend_url"] - # self.base_url = self.full_execution["backend_url"] - #except KeyError: - # pass + found_id = "" + try: + if "execution_id" in self.full_execution and len(self.full_execution["execution_id"]) > 0: + found_id = self.full_execution["execution_id"] + elif len(self.current_execution_id) > 0: + found_id = self.current_execution_id + except Exception as e: + print("[ERROR] Failed in get full exec") + + try: + contains_body = False + parameter_count = 0 + + if "parameters" in self.action: + parameter_count = len(self.action["parameters"]) + for param in self.action["parameters"]: + if param["name"] == "body": + contains_body = True + + print("[DEBUG][%s] Action name: %s, Params: %d, Has Body: %s" % (self.current_execution_id, self.action["name"], parameter_count, str(contains_body))) + except Exception as e: + print("[ERROR] Failed in init print handler: %s" % e) try: if replace_params == True: @@ -1670,7 +1673,8 @@ class AppBase: self.logger.info("[DEBUG] ID: %s vs %s" % (inner_action["id"], self.action["id"])) # In case of some kind of magic, we're just doing params - if inner_action["id"] == self.action["id"]: + if inner_action["id"] != self.action["id"]: + continue self.logger.info("FOUND!") if isinstance(self.action, str): @@ -2366,7 +2370,6 @@ class AppBase: returndata = str(parseditem)+str(appendresult) # New in 0.8.97: Don't return items without lists - #self.logger.info("RETURNDATA: %s" % returndata) #return returndata, is_loop # 0.9.70: @@ -3747,7 +3750,6 @@ class AppBase: # Handles files. filedata = "" file_ids = [] - self.logger.info("TUPLE: %s" % newres[1]) if isinstance(newres[1], list): self.logger.info("[INFO] HANDLING LIST FROM RET") file_ids = self.set_files(newres[1]) diff --git a/backend/app_sdk/autocorrect_test.py b/backend/app_sdk/autocorrect_test.py new file mode 100644 index 00000000..bdcde81a --- /dev/null +++ b/backend/app_sdk/autocorrect_test.py @@ -0,0 +1,102 @@ +import re + +input_data = """{ + "test0": {{ '' | default: [] }}, + "test": {{ | default: [] }}, + "test2": {{ $test.asd | default: [] }}, + "test3": {{ {"key": "val} | default: [] }}, +}""" + +# +# "test4": $test, +# "test5": , +# "test6": "what" +#}""" + + +liquiddata = "{{ $test.asd | some other stuff {{ $test.xyz | more stuff" +pattern = r'\{\{\s*\$[^|}]+\s*\|' + +replaced_data = re.sub(pattern, "{{ '' |", liquiddata) +print(replaced_data) + + +def patternfix_liquid(liquiddata): + if "{{" not in liquiddata or "}}" not in liquiddata: + return liquiddata + + patterns = { + "{{|": "{{ '' |", + } + + regex_patterns = { + r'\{\{\s*\$[^|}]+\s*\|': "{{ '' |", + } + + skipkeys = [" "] + newoutput = liquiddata[:] + for pattern in patterns: + keylocations = [] + parsedvalue = "" + record = False + index = -1 + for key in liquiddata: + index += 1 + if not key: + if record: + keylocations.append(index) + parsedvalue += key + + continue + + if key in skipkeys: + if record: + keylocations.append(index) + parsedvalue += key + + continue + + if key == pattern[0] and not record: + record = True + + if key not in pattern: + keylocations = [] + parsedvalue = "" + record = False + + if record: + keylocations.append(index) + parsedvalue += key + + if len(parsedvalue) == 0: + continue + + evaluated_value = parsedvalue[:] + for skipkey in skipkeys: + evaluated_value = "".join(evaluated_value.split(skipkey)) + + if evaluated_value == pattern: + print("Found matching: %s (%s)" % (parsedvalue, keylocations)) + print("Should replace with: %s" % patterns[pattern]) + + newoutput = newoutput.replace(parsedvalue, patterns[pattern], -1) + break + + for pattern in regex_patterns: + newlines = [] + for line in newoutput.split("\n"): + replaced_line = re.sub(pattern, regex_patterns[pattern], line) + newlines.append(replaced_line) + + newoutput = "\n".join(newlines) + + return newoutput + +print("Start:\n%s" % input_data) +try: + newinput = patternfix_liquid(input_data) +except Exception as e: + print("[ERROR} Failed liquid parsing fix: %s" % e) + newinput = input_data + +print("\nEnd:\n%s" % newinput) From 66a5260cdb8ee8af2e59eb3391e0535f387c3bad Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 5 Mar 2024 14:55:46 +0100 Subject: [PATCH 035/142] Fixed more pattern matching things. Will implement to test for liquid formatting --- backend/app_sdk/autocorrect_test.py | 116 +++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 20 deletions(-) diff --git a/backend/app_sdk/autocorrect_test.py b/backend/app_sdk/autocorrect_test.py index bdcde81a..d8c5dbbc 100644 --- a/backend/app_sdk/autocorrect_test.py +++ b/backend/app_sdk/autocorrect_test.py @@ -1,17 +1,23 @@ import re +import json + +input_data = """{ + "test4": $test, + "test5": , + "test6": "what" + } +""" input_data = """{ "test0": {{ '' | default: [] }}, "test": {{ | default: [] }}, "test2": {{ $test.asd | default: [] }}, "test3": {{ {"key": "val} | default: [] }}, -}""" - -# -# "test4": $test, -# "test5": , -# "test6": "what" -#}""" + "test4": $test, + "test5": , + "test6": "what" + } +""" liquiddata = "{{ $test.asd | some other stuff {{ $test.xyz | more stuff" @@ -21,17 +27,31 @@ replaced_data = re.sub(pattern, "{{ '' |", liquiddata) print(replaced_data) -def patternfix_liquid(liquiddata): - if "{{" not in liquiddata or "}}" not in liquiddata: - return liquiddata +def patternfix_string(liquiddata, patterns, regex_patterns, inputtype="liquid"): + if not inputtype or inputtype == "liquid": + if "{{" not in liquiddata or "}}" not in liquiddata: + return liquiddata + elif inputtype == "json": + liquiddata = liquiddata.strip() - patterns = { - "{{|": "{{ '' |", - } + # Validating if it looks like json or not + if liquiddata[0] == "{" and liquiddata[len(liquiddata)-1] == "}": + pass + else: + if liquiddata[0] == "[" and liquiddata[len(liquiddata)-1] == "]": + pass + else: + return liquiddata - regex_patterns = { - r'\{\{\s*\$[^|}]+\s*\|': "{{ '' |", - } + # If it's already json, don't touch it + try: + json.loads(liquiddata) + return liquiddata + except Exception as e: + pass + else: + print("No replace handler for %s" % inputtype) + return liquiddata skipkeys = [" "] newoutput = liquiddata[:] @@ -41,6 +61,15 @@ def patternfix_liquid(liquiddata): record = False index = -1 for key in liquiddata: + + # Return instant if possible + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + pass + index += 1 if not key: if record: @@ -76,11 +105,19 @@ def patternfix_liquid(liquiddata): evaluated_value = "".join(evaluated_value.split(skipkey)) if evaluated_value == pattern: - print("Found matching: %s (%s)" % (parsedvalue, keylocations)) - print("Should replace with: %s" % patterns[pattern]) + #print("Found matching: %s (%s)" % (parsedvalue, keylocations)) + #print("Should replace with: %s" % patterns[pattern]) newoutput = newoutput.replace(parsedvalue, patterns[pattern], -1) - break + + # Return instant if possible + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + pass + for pattern in regex_patterns: newlines = [] @@ -90,13 +127,52 @@ def patternfix_liquid(liquiddata): newoutput = "\n".join(newlines) + # Return instant if possible + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + pass + return newoutput print("Start:\n%s" % input_data) + try: - newinput = patternfix_liquid(input_data) + newinput = patternfix_string(input_data, + { + "{{|": '{{ "" |', + }, + { + #r'\{\{\s*|': "{{ '' |", + r'\{\{\s*\$[^|}]+\s*\|': '{{ "" |', + } + , + inputtype="liquid" + ) except Exception as e: print("[ERROR} Failed liquid parsing fix: %s" % e) newinput = input_data +try: + newinput = patternfix_string(newinput, + { + }, + { + r'\"\s*\:\s*,': '\": "",', + r'\"\s*\:\s*\$[^,]+\w*\,': '\": "",', + } + , + inputtype="json" + ) + + try: + json.loads(newinput) + print("It's json! Override.") + except Exception as e: + print("Bad json. DONT use the value at all: %s" % e) +except Exception as e: + print("[ERROR} Failed json parsing fix: %s" % e) + print("\nEnd:\n%s" % newinput) From 0333c0ab6a5e0c5774069c15ebbc35b8db63f517 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 5 Mar 2024 15:25:11 +0100 Subject: [PATCH 036/142] Added liquid (and future json) pattern fix to sdk to autofix some normal issues --- backend/app_sdk/app_base.py | 138 +++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 10 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 5ec1703e..a8ec11be 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -476,6 +476,117 @@ class AppBase: except Exception as e: return request.text + # Fixes pattern issues in json/liquid based on input and supplied patterns + def patternfix_string(self, liquiddata, patterns, regex_patterns, inputtype="liquid"): + if not inputtype or inputtype == "liquid": + if "{{" not in liquiddata or "}}" not in liquiddata: + return liquiddata + elif inputtype == "json": + liquiddata = liquiddata.strip() + + # Validating if it looks like json or not + if liquiddata[0] == "{" and liquiddata[len(liquiddata)-1] == "}": + pass + else: + if liquiddata[0] == "[" and liquiddata[len(liquiddata)-1] == "]": + pass + else: + return liquiddata + + # If it's already json, don't touch it + try: + json.loads(liquiddata) + return liquiddata + except Exception as e: + pass + else: + print("No replace handler for %s" % inputtype) + return liquiddata + + skipkeys = [" "] + newoutput = liquiddata[:] + for pattern in patterns: + keylocations = [] + parsedvalue = "" + record = False + index = -1 + for key in liquiddata: + + # Return instant if possible + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + pass + + index += 1 + if not key: + if record: + keylocations.append(index) + parsedvalue += key + + continue + + if key in skipkeys: + if record: + keylocations.append(index) + parsedvalue += key + + continue + + if key == pattern[0] and not record: + record = True + + if key not in pattern: + keylocations = [] + parsedvalue = "" + record = False + + if record: + keylocations.append(index) + parsedvalue += key + + if len(parsedvalue) == 0: + continue + + evaluated_value = parsedvalue[:] + for skipkey in skipkeys: + evaluated_value = "".join(evaluated_value.split(skipkey)) + + if evaluated_value == pattern: + #print("Found matching: %s (%s)" % (parsedvalue, keylocations)) + #print("Should replace with: %s" % patterns[pattern]) + + newoutput = newoutput.replace(parsedvalue, patterns[pattern], -1) + + # Return instant if possible + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + pass + + + for pattern in regex_patterns: + newlines = [] + for line in newoutput.split("\n"): + replaced_line = re.sub(pattern, regex_patterns[pattern], line) + newlines.append(replaced_line) + + newoutput = "\n".join(newlines) + + # Return instant if possible + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + pass + + return newoutput + # FIXME: Add more info like logs in here. # Docker logs: https://forums.docker.com/t/docker-logs-inside-the-docker-container/68190/2 def send_result(self, action_result, headers, stream_path): @@ -2383,6 +2494,8 @@ class AppBase: except json.decoder.JSONDecodeError as e: return returndata, is_loop + + # Sending self as it's not a normal function def parse_liquid(template, self): @@ -2397,18 +2510,23 @@ class AppBase: self.logger.info("[DEBUG] Shuffle loop shouldn't run in liquid. Data length: %d" % len(template)) return template - #if not "{{" in template or not "}}" in template: - # if not "{%" in template or not "%}" in template: - # self.logger.info("Skipping liquid - missing {{ }} and {% %}") - # return template - #if not "{{" in template or not "}}" in template: - # return template + # New pattern fixer to help with bad liquid formats + try: + newoutput = self.patternfix_string(template, + { + "{{|": '{{ "" |', + }, + { + r'\{\{\s*\$[^|}]+\s*\|': '{{ "" |', + } + , + inputtype="liquid" + ) - #self.logger.info(globals()) - #if len(template) > 100: - # self.logger.info("[DEBUG] Running liquid with data of length %d" % len(template)) - #self.logger.info(f"[DEBUG] Data: {template}") + template = newoutput + except Exception as e: + print("[ERROR] Failed liquid parsing fix: %s" % e) all_globals = globals() all_globals["self"] = self From 1da44d12ecf8ec871e8efa6227b36492ec14e414 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 5 Mar 2024 15:30:59 +0100 Subject: [PATCH 037/142] Minor fix to make JSON only return json if it's actually json --- backend/app_sdk/app_base.py | 9 +++++++++ backend/app_sdk/autocorrect_test.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index a8ec11be..c64939ce 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -584,6 +584,15 @@ class AppBase: return newoutput except: pass + + # Dont return json properly unless actually json + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + # Returns original if json fixing didn't work + return liquiddata return newoutput diff --git a/backend/app_sdk/autocorrect_test.py b/backend/app_sdk/autocorrect_test.py index d8c5dbbc..a7624c76 100644 --- a/backend/app_sdk/autocorrect_test.py +++ b/backend/app_sdk/autocorrect_test.py @@ -135,6 +135,15 @@ def patternfix_string(liquiddata, patterns, regex_patterns, inputtype="liquid"): except: pass + # Dont return json properly unless actually json + if inputtype == "json": + try: + json.loads(newoutput) + return newoutput + except: + # Returns original if json fixing didn't work + return liquiddata + return newoutput print("Start:\n%s" % input_data) From 294c8060ef1c8ad275c099b7200ac62b595777de Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 5 Mar 2024 16:31:16 +0100 Subject: [PATCH 038/142] Updated billing list with links to track users --- frontend/src/components/Billing.jsx | 31 +++---- frontend/src/components/BillingStats.jsx | 4 - frontend/src/components/ParsedAction.jsx | 104 ++++++++++++----------- frontend/src/views/AngularWorkflow.jsx | 60 +++++++------ frontend/src/views/Workflows.jsx | 2 +- functions/onprem/orborus/orborus.go | 4 - 6 files changed, 105 insertions(+), 100 deletions(-) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 5ddf7d86..817e6295 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -431,6 +431,18 @@ const Billing = (props) => { {top_text} + + {top_text === "Base Cloud Access" && userdata.has_card_available === false ? + + : null} {isCloud && highlight === true && top_text !== "Base Cloud Access" ? { userdata.has_card_available === true ? "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." : - `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit. Your organisations manager email is ${selectedOrganization.org}.` + `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` }
+ Billing email: {selectedOrganization.org} {/*isCloud ? : null} - {userdata.has_card_available === false ? - - : null} : null} {showSupport ? diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx index e4354db0..e3311986 100644 --- a/frontend/src/components/BillingStats.jsx +++ b/frontend/src/components/BillingStats.jsx @@ -113,8 +113,6 @@ const AppStats = (defaultprops) => { return } - console.log("START TIME", starttime, endtime) - var url = `${globalUrl}/api/v1/workflows/${workflow.id}/executions/count` if (starttime !== "") { @@ -178,8 +176,6 @@ const AppStats = (defaultprops) => { const allData = Promise.all(promises); allData.then((data) => { - console.log("IN ALL DATA") - var total = 0 for (var i = 0; i < data.length; i++) { if (data[i].runcount !== undefined) { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 3c4f37d6..ab2ead31 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -454,12 +454,7 @@ const ParsedAction = (props) => { } } - // FIXME: Add values from previous executions if they exist - if ( - workflow.execution_variables !== null && - workflow.execution_variables !== undefined && - workflow.execution_variables.length > 0 - ) { + if (workflow.execution_variables !== null && workflow.execution_variables !== undefined && workflow.execution_variables.length > 0) { for (let [key,keyval] in Object.entries(workflow.execution_variables)) { const item = workflow.execution_variables[key]; actionlist.push({ @@ -474,76 +469,85 @@ const ParsedAction = (props) => { } // Loops parent nodes' old results to fix autocomplete - if (getParents !== undefined) { - var parents = getParents(selectedAction); + if (getParents !== undefined) { + var parents = getParents(selectedAction) if (parents.length > 1) { - for (let [key,keyval] in Object.entries(parents)) { - const item = parents[key]; - if (item.label === "Execution Argument") { - continue; + var labels = [] + //for (let [parentkey, parentkeyval] in Object.entries(parents)) { + for (let parentkey in parents) { + const parentNode = parents[parentkey] + if (parentNode.label === "Execution Argument") { + continue } - var exampledata = item.example === undefined || item.example === null ? "" : item.example; + //if (labels.includes(item.label)) { + // continue + //} + + labels.push(parentNode.label) + + var exampledata = parentNode.example === undefined || parentNode.example === null ? "" : parentNode.example // Find previous execution and their variables //exampledata === "" && if (workflowExecutions.length > 0) { // Look for the ID const found = false; - for (let [key,keyval] in Object.entries(workflowExecutions)) { - if ( - workflowExecutions[key].results === undefined || - workflowExecutions[key].results === null - ) { + for (let wfkey in workflowExecutions) { + if (workflowExecutions[wfkey].results === undefined || workflowExecutions[wfkey].results === null) { + continue; } - var foundResult = workflowExecutions[key].results.find( - (result) => result.action.id === item.id - ); + var foundResult = workflowExecutions[wfkey].results.find((result) => result.action.id === parentNode.id) + if (foundResult === undefined || foundResult === null) { - continue; + continue } - if (foundResult.result !== undefined && foundResult.result !== null) { - foundResult = foundResult.result - } + if (foundResult.result !== undefined && foundResult.result !== null) { + foundResult = foundResult.result + } - const valid = validateJson(foundResult) - if (valid.valid) { - if (valid.result.success === false) { - //console.log("Skipping success false autocomplete") - } else { - exampledata = valid.result; - break; - } + const valid = validateJson(foundResult) + if (valid.valid) { + if (valid.result.success === false) { + //console.log("Skipping success false autocomplete") + } else { + + // FIXME: Have a merge system to allow to use kind of any key from that node in the last 10-20 execs + //if (exampledata.length > 0) { + // exampledata = valid.result + //} else { + // exampledata = valid.result + //} + + exampledata = valid.result + break + } } else { - exampledata = foundResult; - } + exampledata = foundResult + } } } // 1. Take - const itemlabelComplete = - item.label === null || item.label === undefined - ? "" - : item.label.split(" ").join("_"); + const itemlabelComplete = parentNode.label === null || parentNode.label === undefined ? "" : parentNode.label.split(" ").join("_"); const actionvalue = { type: "action", - id: item.id, - name: item.label, + id: parentNode.id, + name: parentNode.label, autocomplete: itemlabelComplete, example: exampledata, - }; + } - actionlist.push(actionvalue); + actionlist.push(actionvalue) } } - //console.log("ACTIONLIST: ", actionlist) setActionlist(actionlist); - } + } } }); @@ -672,7 +676,6 @@ const ParsedAction = (props) => { selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"]; } - console.log("RESULT: ", selectedAction); setSelectedAction(selectedAction); //setUpdate(Math.random()) return; @@ -2550,9 +2553,9 @@ const ParsedAction = (props) => { {datafield} {/*shufflecode*/} {showDropdown && - showDropdownNumber === count && - data.variant === "STATIC_VALUE" && - jsonList.length > 0 ? ( + showDropdownNumber === count && + data.variant === "STATIC_VALUE" && + jsonList.length > 0 ? ( { ) : ( - ); + ) + return ( { return response.json(); }) .then((responseJson) => { - //console.log("RESPONSE: ", responseJson) handleUpdateResults(responseJson, executionRequest); }) .catch((error) => { @@ -1349,12 +1348,10 @@ const AngularWorkflow = (defaultprops) => { const sendStreamRequest = (body) => { //console.log("Stream not activated yet.") if (!isCloud) { - console.log("Stream not activated yet for onprem") return } if (streamDisabled) { - console.log("Stream disabled - send") return } @@ -1365,7 +1362,7 @@ const AngularWorkflow = (defaultprops) => { //const url = ${globalUrl}/api/v1/workflows/${props.match.params.key}/stream //const streamUrl = "http://localhost:5002" - console.log("Stream request: ", body) + //console.log("Stream request: ", body) const streamUrl = "https://stream.shuffler.io" const url = `${streamUrl}/api/v1/workflows/${props.match.params.key}/stream` @@ -1397,7 +1394,7 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - console.log("Stream resp: ", responseJson) + //console.log("Stream resp: ", responseJson) }) .catch((error) => { console.log("Stream send error: ", error.toString()) @@ -1410,11 +1407,21 @@ const AngularWorkflow = (defaultprops) => { var success = false; if (isCloud && !isLoggedIn) { - console.log("Should redirect to register with redirect."); - window.location.href = `/register?view=/workflows/${props.match.params.key}&message=You need sign up to use workflows with Shuffle`; - return; + console.log("Should redirect to register with redirect.") + window.location.href = `/register?view=/workflows/${props.match.params.key}&message=You need sign up to use workflows with Shuffle` + return } + if (curworkflow === undefined || curworkflow === null) { + console.log("No workflow during save") + return + } + + if (curworkflow.actions === undefined || curworkflow.actions === null || curworkflow.actions.length === 0) { + console.log("Can't save without actions") + return + } + setSavingState(2); // This might not be the right course of action, but seems logical, as items could be running already @@ -4467,7 +4474,6 @@ const AngularWorkflow = (defaultprops) => { const foundresult = GetParamMatch(paramname, exampledata, ""); if (foundresult.length > 0) { - console.log("FOUND ReS for field: ", dstdata.parameters[dstdataParamKey].name, foundresult) if (dstdata.parameters[dstdataParamKey].value.length === 0) { dstdata.parameters[dstdataParamKey].value = `$${parentlabel}${foundresult}`; dstdata.parameters[dstdataParamKey].autocompleted = true @@ -4550,7 +4556,6 @@ const AngularWorkflow = (defaultprops) => { if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") { setTimeout(() => { const alledges = cy.edges().jsons() - console.log("edges: ", alledges, edge) var targetedge = alledges.findIndex( (data) => data.data.source === edge.source && data.data.id !== edge.id ) @@ -4576,7 +4581,6 @@ const AngularWorkflow = (defaultprops) => { const edgeCurve = calculateEdgeCurve(sourcenode.position(), destinationnode.position()) const currentedge = cy.getElementById(edge.id) if (currentedge !== undefined && currentedge !== null) { - console.log("Setting edge curve: ", edgeCurve) currentedge.style('control-point-distance', edgeCurve.distance) currentedge.style('control-point-weight', edgeCurve.weight) } @@ -4587,7 +4591,6 @@ const AngularWorkflow = (defaultprops) => { (data) => data.id === edge.target ); if (targetnode !== -1) { - console.log("TARGETNODE: ", targetnode); if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") { } else { toast("Can't have triggers as target of branch"); @@ -4689,7 +4692,6 @@ const AngularWorkflow = (defaultprops) => { /* targetnode = workflow.triggers.findIndex(data => data.id === edge.target) if (targetnode !== -1) { - console.log("TARGETNODE: ", targetnode) if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow") { } else { toast("Can't have triggers as target of branch") @@ -4883,7 +4885,6 @@ const AngularWorkflow = (defaultprops) => { // toast("Recommendations to show??") //} - console.log("Added to workflow!!") setWorkflow(workflow); fetchRecommendations(workflow) } else if (nodedata.type === "TRIGGER") { @@ -6180,7 +6181,6 @@ const AngularWorkflow = (defaultprops) => { if (inputworkflow.actions !== undefined && inputworkflow.actions !== null && inputworkflow.actions.length > 0) { cy.remove('*') } - console.log("INPUT: ", inputworkflow) } if (inputworkflow.actions === undefined || inputworkflow.actions === null) { @@ -6482,6 +6482,7 @@ const AngularWorkflow = (defaultprops) => { // Get selected node if (selectedNode.data().type === "TRIGGER") { + console.log("Should remove trigger!"); console.log(selectedNode.data()); const triggerindex = workflow.triggers.findIndex( @@ -8780,7 +8781,7 @@ const AngularWorkflow = (defaultprops) => { results.push({ id: allkeys[parentkey], type: "TRIGGER" }); } else { if (handled.includes(currentnode.data().id)) { - continue; + continue } else { handled.push(currentnode.data().id); results.push(currentnode.data()); @@ -8817,11 +8818,12 @@ const AngularWorkflow = (defaultprops) => { } // Remove on the end as we don't want to remove everything - results = results.filter((data) => data.id !== action.id); - results = results.filter((data) => data.type === "ACTION" || data.app_name === "Shuffle Workflow" || data.app_name === "User Input"); - results.push({ label: "Execution Argument", type: "INTERNAL" }); - return results; - }; + results = results.filter((data) => data.id !== action.id) + results = results.filter((data) => data.type === "ACTION" || data.app_name === "Shuffle Workflow" || data.app_name === "User Input") + results.push({ label: "Execution Argument", type: "INTERNAL" }) + + return results + } // BOLD name: type: required? // FORM @@ -10290,7 +10292,6 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - //console.log("RESPONSE: "); setTriggerAuthentication(responseJson); clearInterval(id); newwin.close(); @@ -14290,7 +14291,7 @@ const AngularWorkflow = (defaultprops) => { lastSaved && !workflow.public ? "outlined" : "contained" } onClick={() => { - saveWorkflow() + saveWorkflow(workflow) if (workflow.public === true) { console.log("Public!") @@ -14773,7 +14774,7 @@ const AngularWorkflow = (defaultprops) => { This workflow is public and { - saveWorkflow() + saveWorkflow(workflow) }}>must be saved or exported before use. {Object.getOwnPropertyNames(creatorProfile).length !== 0 && creatorProfile.github_avatar !== undefined && creatorProfile.github_avatar !== null ? @@ -15563,7 +15564,7 @@ const AngularWorkflow = (defaultprops) => { style={{ color: "white", fontSize: 16 }} >

- + All Workflow Runs

@@ -18394,9 +18395,16 @@ const AngularWorkflow = (defaultprops) => { cy.removeListener("drag"); cy.removeListener("free"); cy.removeListener("cxttap"); + + //cy.remove('*') + setElements([]) } - setupGraph(newrevision) + // Remove all cy nodes + setTimeout(() => { + setupGraph(newrevision) + }, 100) + // Re-adding cytoscape triggers if (cy !== undefined && cy !== null) { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index f453c98a..03fab69d 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -518,7 +518,7 @@ export const validateJson = (showResult) => { } } } catch (e) { - console.log("Failed parsing inside json subvalues: ", e) + //console.log("Failed parsing inside json subvalues: ", e) } } diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 05697e13..2d761857 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -570,12 +570,10 @@ func deployServiceWorkers(image string) { overrideHttpProxy := os.Getenv("SHUFFLE_INTERNAL_HTTP_PROXY") overrideHttpsProxy := os.Getenv("SHUFFLE_INTERNAL_HTTPS_PROXY") if len(overrideHttpProxy) > 0 { - log.Printf("[DEBUG] Added internal proxy: %s", overrideHttpProxy) serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTP_PROXY=%s", overrideHttpProxy)) } if len(overrideHttpsProxy) > 0 { - log.Printf("[DEBUG] Added internal proxy: %s", overrideHttpsProxy) serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTPS_PROXY=%s", overrideHttpsProxy)) } @@ -1758,12 +1756,10 @@ func main() { overrideHttpProxy := os.Getenv("SHUFFLE_INTERNAL_HTTP_PROXY") overrideHttpsProxy := os.Getenv("SHUFFLE_INTERNAL_HTTPS_PROXY") if len(overrideHttpProxy) > 0 { - log.Printf("[DEBUG] Added internal proxy: %s", overrideHttpProxy) env = append(env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTP_PROXY=%s", overrideHttpProxy)) } if len(overrideHttpsProxy) > 0 { - log.Printf("[DEBUG] Added internal proxy: %s", overrideHttpsProxy) env = append(env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTPS_PROXY=%s", overrideHttpsProxy)) } From c258e3688bf01df30153eb8db746f574031090a2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 5 Mar 2024 17:45:08 +0100 Subject: [PATCH 039/142] Fixed worker k8s bug --- functions/onprem/worker/go.mod | 8 ++- functions/onprem/worker/go.sum | 47 ++++++--------- functions/onprem/worker/worker.go | 97 ++++++++++++++++++++++--------- 3 files changed, 91 insertions(+), 61 deletions(-) diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 72a6b15e..9bc53ad4 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/docker v23.0.3+incompatible github.com/gorilla/mux v1.8.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.68 + github.com/shuffle/shuffle-shared v0.5.86 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 @@ -27,7 +27,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/adrg/strutil v0.2.3 // indirect github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect - github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 // indirect + github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect @@ -38,6 +38,7 @@ require ( github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/frikky/kin-openapi v0.41.0 // indirect + github.com/frikky/schemaless v0.0.6 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect @@ -75,6 +76,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/sashabaranov/go-openai v1.19.2 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/skeema/knownhosts v1.2.1 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect @@ -93,7 +95,7 @@ require ( golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.106.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.30.0 // indirect diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index 5abfc718..f2520ce2 100644 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -63,10 +63,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCv github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -85,8 +83,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3w github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw= -github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -99,7 +97,6 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -119,7 +116,6 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/El github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -127,14 +123,14 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= +github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= +github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -241,7 +237,6 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -262,7 +257,6 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -271,7 +265,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -281,16 +274,13 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/libgit2/git2go/v34 v34.0.0/go.mod h1:blVco2jDAw6YTXkErMMqzHLcAjKkwF0aWIRHBqiJkZ0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= @@ -353,11 +343,9 @@ github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsr github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -369,15 +357,23 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= +github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shuffle/shuffle-shared v0.5.86 h1:ZHQgZ4siSWgi5gttxeSMdjsaH9SbGTzrA/GO6aICO2U= +github.com/shuffle/shuffle-shared v0.5.86/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -392,7 +388,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -412,14 +407,11 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -555,7 +547,6 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -588,7 +579,6 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -616,7 +606,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -639,6 +628,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -666,7 +656,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -751,8 +740,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -835,9 +825,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index ee0349ce..548e6b92 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -52,8 +52,9 @@ var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION")) -//var baseimagename = "frikky/shuffle" -var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") +var baseimagename = "frikky/shuffle" + +// var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") // var baseimagename = "registry.hub.docker.com/frikky/shuffle" var registryName = "registry.hub.docker.com" @@ -88,6 +89,7 @@ type ImageRequest struct { } var finishedExecutions []string +var imagesDistributed []string // Images to be autodeployed in the latest version of Shuffle. @@ -399,18 +401,17 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] return err } - log.Printf("[DEBUG] Got kubernetes client") str := strings.ToLower(identifier) strSplit := strings.Split(str, "_") value := strSplit[0] value = strings.ReplaceAll(value, "_", "-") // checking if app is generated or not + /* appDetails := strings.Split(image, ":")[1] appDetailsSplit := strings.Split(appDetails, "_") appName := strings.Join(appDetailsSplit[:len(appDetailsSplit)-1], "_") appVersion := appDetailsSplit[len(appDetailsSplit)-1] - for _, app := range workflowExecution.Workflow.Actions { // log.Printf("[DEBUG] App: %s, Version: %s", appName, appVersion) // log.Printf("[DEBUG] Checking app %s with version %s", app.AppName, app.AppVersion) @@ -424,6 +425,22 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] } } } + */ + + if len(localRegistry) == 0 && len(os.Getenv("SHUFFLE_BASE_IMAGE_REGISTRY")) > 0 { + localRegistry = os.Getenv("SHUFFLE_BASE_IMAGE_REGISTRY") + } + + if len(localRegistry) > 0 && strings.Count(image, "/") <= 2 { + log.Printf("[DEBUG] Using REGISTRY_URL %s", localRegistry) + image = fmt.Sprintf("%s/%s", localRegistry, image) + } else { + if strings.Count(image, "/") <= 2 { + image = fmt.Sprintf("frikky/shuffle:%s", image) + } + } + + log.Printf("[DEBUG] Got kubernetes client to run image '%s'", image) //fix naming convention podUuid := uuid.NewV4().String() @@ -623,7 +640,7 @@ func cleanupExecution(clientset *kubernetes.Clientset, workflowExecution shuffle return nil } -func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *container.Config, hostConfig *container.HostConfig, identifier string, workflowExecution shuffle.WorkflowExecution, newExecId string) error { +func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *container.Config, hostConfig *container.HostConfig, identifier string, workflowExecution shuffle.WorkflowExecution, actionExecId string) error { cont, err := cli.ContainerCreate( ctx, config, @@ -640,9 +657,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont if !strings.Contains(err.Error(), "Conflict. The container name") { log.Printf("[ERROR] Container CREATE error (1): %s", err) - cacheErr := shuffle.DeleteCache(ctx, newExecId) + cacheErr := shuffle.DeleteCache(ctx, actionExecId) if cacheErr != nil { - log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", newExecId, cacheErr) + log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", actionExecId, cacheErr) } return err @@ -664,9 +681,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont if err != nil { log.Printf("[ERROR] Container create error (2): %s", err) - cacheErr := shuffle.DeleteCache(ctx, newExecId) + cacheErr := shuffle.DeleteCache(ctx, actionExecId) if cacheErr != nil { - log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", newExecId, cacheErr) + log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", actionExecId, cacheErr) } return err @@ -703,9 +720,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont if err != nil { log.Printf("[ERROR] Container create error (3): %s", err) - cacheErr := shuffle.DeleteCache(ctx, newExecId) + cacheErr := shuffle.DeleteCache(ctx, actionExecId) if cacheErr != nil { - log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", newExecId, cacheErr) + log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", actionExecId, cacheErr) } return err @@ -718,9 +735,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont if err != nil { log.Printf("[ERROR] Failed to start container in environment %s: %s", environment, err) - cacheErr := shuffle.DeleteCache(ctx, newExecId) + cacheErr := shuffle.DeleteCache(ctx, actionExecId) if cacheErr != nil { - log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", newExecId, cacheErr) + log.Printf("[ERROR] FAILURE Deleting cache for %s: %s", actionExecId, cacheErr) } //shutdown(workflowExecution, workflowExecution.Workflow.ID, true) @@ -844,24 +861,33 @@ func askOtherWorkersToDownloadImage(image string) { return } + if shuffle.ArrayContains(imagesDistributed, image) { + return + } + urls, err := getWorkerURLs() if err != nil { log.Printf("[ERROR] Error in listing worker urls: %s", err) return } + if len(urls) < 2{ + return + } + + httpClient := &http.Client{} + distributed := false for _, url := range urls { - log.Printf("[DEBUG] Trying to speak to: %s", url) + //log.Printf("[DEBUG] Trying to speak to: %s", url) imagesRequest := ImageRequest{ Image: image, } url = fmt.Sprintf("%s/api/v1/download", url) + //log.Printf("[INFO] Making a request to %s to download images", url) imageJSON, err := json.Marshal(imagesRequest) - - log.Printf("[INFO] Making a request to %s to download images", url) req, err := http.NewRequest( "POST", url, @@ -887,6 +913,11 @@ func askOtherWorkersToDownloadImage(image string) { } log.Printf("[INFO] Response body when tried sending images for nodes to download: %s", respBody) + distributed = true + } + + if distributed { + imagesDistributed = append(imagesDistributed, image) } } @@ -922,6 +953,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { if strings.Contains(image, " ") { image = strings.ReplaceAll(image, " ", "-") } + askOtherWorkersToDownloadImage(image) // Added UUID to identifier just in case @@ -1908,7 +1940,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { // 3. Add to and update actionResult in workflowExecution // 4. Push to db // IF FAIL: Set executionstatus: abort or cancel - ctx := context.Background() workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) if err != nil { @@ -1962,7 +1993,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) if err != nil { log.Printf("[ERROR] Failed getting execution cache: %s", err) - resp.WriteHeader(401) + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) return } @@ -2003,7 +2034,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) if err != nil { log.Printf("[ERROR][%s] Failed getting execution cache (2): %s", workflowExecution.ExecutionId, err) - resp.WriteHeader(401) + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) return } @@ -2211,6 +2242,7 @@ func validateFinished(workflowExecution shuffle.WorkflowExecution) bool { newexec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) if err != nil { log.Printf("[ERROR][%s] Failed getting workflow execution: %s", workflowExecution.ExecutionId, err) + return false } else { workflowExecution = *newexec } @@ -2288,7 +2320,7 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) if err != nil { log.Printf("[INFO] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err) - resp.WriteHeader(401) + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) return } @@ -2303,7 +2335,7 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { newjson, err := json.Marshal(workflowExecution) if err != nil { - resp.WriteHeader(401) + resp.WriteHeader(500) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow execution"}`))) return } @@ -2375,11 +2407,9 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener { } func downloadDockerImageBackend(client *http.Client, imageName string) error { - log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages) - // Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" { - log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName) + //log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName) return nil } @@ -2388,6 +2418,8 @@ func downloadDockerImageBackend(client *http.Client, imageName string) error { return nil } + log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages) + downloadedImages = append(downloadedImages, imageName) data := fmt.Sprintf(`{"name": "%s"}`, imageName) @@ -2598,7 +2630,7 @@ func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, } streamUrl := fmt.Sprintf("http://%s:%d/api/v1/run", appName, port) - log.Printf("[DEBUG][%s] Worker URL: %s, Backend URL: %s, Target App: %s", workflowExecution.ExecutionId, parsedRequest.BaseUrl, parsedRequest.Url, streamUrl) + //log.Printf("[DEBUG][%s] Worker URL: %s, Backend URL: %s, Target App: %s", workflowExecution.ExecutionId, parsedRequest.BaseUrl, parsedRequest.Url, streamUrl) req, err := http.NewRequest( "POST", streamUrl, @@ -2619,13 +2651,22 @@ func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, if err != nil { log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) } else { - log.Printf("[DEBUG][%s] Adding %s to cache (%#v)", workflowExecution.ExecutionId, newExecId, action.Name) + //log.Printf("[DEBUG][%s] Adding %s to cache (%#v)", workflowExecution.ExecutionId, newExecId, action.Name) } client := shuffle.GetExternalClient(streamUrl) + customTimeout := os.Getenv("SHUFFLE_APP_REQUEST_TIMEOUT") + if len(customTimeout) > 0 { + // convert to int + timeoutInt, err := strconv.Atoi(customTimeout) + if err != nil { + log.Printf("[ERROR] Failed converting SHUFFLE_APP_REQUEST_TIMEOUT to int: %s", err) + } else { + log.Printf("[DEBUG] Setting client timeout to %d seconds for app request", timeoutInt) + client.Timeout = time.Duration(timeoutInt) * time.Second + } + } - // Set client timeout to 5 seconds - //client.Timeout = time.Duration(10) * time.Second newresp, err := client.Do(req) if err != nil { // Another timeout issue here somewhere From 6bc64f3657c672f9b32436fb7f7187bfdb4aca3f Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Wed, 6 Mar 2024 16:19:51 +0530 Subject: [PATCH 040/142] Merge pull request #1 from tesla999936/main added an endpoint to get user apps --- backend/go-app/main.go | 1 + backend/go-app/walkoff.go | 64 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 43d9eb53..71d30988 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4785,6 +4785,7 @@ func initHandlers() { r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/users/apps", getUserApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 40f9ba3a..ed32e90d 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -2404,6 +2404,70 @@ func setExampleresult(ctx context.Context, result shuffle.AppExecutionExample) e return nil } +func getUserApps(resp http.ResponseWriter, request *http.Request) { + cors := shuffle.HandleCors(resp, request) + if cors { + return + } + + ctx := context.Background() + user, userErr := shuffle.HandleApiAuthentication(resp, request) + if userErr != nil { + log.Printf("[WARNING] Api authentication failed in get all apps - this does NOT require auth in the cloud.: %s", userErr) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000, 0) + if err != nil { + log.Printf("[WARNING] Failed getting apps (getworkflowapps): %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + filteredApps := workflowapps[:0] + for _, app := range workflowapps { + if app.Owner == user.Id { + filteredApps = append(filteredApps, app) + } else if app.Contributors != nil { + for _, contributor := range app.Contributors { + if contributor == user.Id { + filteredApps = append(filteredApps, app) + } + } + } + } + + if len(user.PrivateApps) > 0 { + for _, item := range user.PrivateApps { + found := false + for _, app := range filteredApps { + if item.ID == app.ID || !(item.Owner == user.Id) { + found = true + break + } + } + + if !found { + filteredApps = append(filteredApps, item) + } + } + } + + newbody, err := json.Marshal(filteredApps) + if err != nil { + log.Printf("[ERROR] Failed unmarshalling all newapps: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow apps"}`))) + return + } + + resp.WriteHeader(200) + resp.Write(newbody) +} + func getWorkflowApps(resp http.ResponseWriter, request *http.Request) { cors := shuffle.HandleCors(resp, request) if cors { From b71d008edcb74622973cac0061109235cb6310e3 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Wed, 6 Mar 2024 11:09:47 +0000 Subject: [PATCH 041/142] changed HandleGetSubOrg -> HandleGetSubOrgs --- backend/go-app/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 26235d13..62652d92 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4923,7 +4923,7 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") - r.HandleFunc("/api/v1/subOrgs/{orgId}", shuffle.HandleGetSubOrg).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/subOrgs/{orgId}", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS") // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. From 403acbc984d3915c0174cd3bfe942a9506a87581 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 7 Mar 2024 11:16:48 +0100 Subject: [PATCH 042/142] KMS fixes and updates with go.mod --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 ++ backend/go-app/main.go | 7 ------- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c7094cb8..d1678703 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.81 + github.com/shuffle/shuffle-shared v0.5.88 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 8033f69e..a86377b2 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -459,6 +459,8 @@ github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYj github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shuffle/shuffle-shared v0.5.81 h1:pt4lT42FrXN/kd/vlYtm7nXShZI0mOamXUMjpWLH7qI= github.com/shuffle/shuffle-shared v0.5.81/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.5.88 h1:YNM6xtnKg0BoMmw2pqV/LnNAsMWMrAzYIAy+o+ChdfI= +github.com/shuffle/shuffle-shared v0.5.88/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 43d9eb53..d0b99620 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -881,13 +881,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { } - // Do it per user? - if err == nil && len(org.SubscriptionUserId) == 0 { - manageOrgSubSignup(org) - - func manageOrgSubSignup(org shuffle.Organization) - } - //if err == nil { if len(org.Id) > 0 { userInfo.ActiveOrg = shuffle.OrgMini{ From 40d84087cdc0fad5b338538d99f63459cc27e7d6 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Sat, 9 Mar 2024 18:00:10 +0530 Subject: [PATCH 043/142] Merge pull request #2 from tesla999936/main reverting getUserApps --- backend/go-app/main.go | 2 +- backend/go-app/walkoff.go | 64 --------------------------------------- 2 files changed, 1 insertion(+), 65 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 71d30988..0c9da252 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4785,7 +4785,7 @@ func initHandlers() { r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/users/apps", getUserApps).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index ed32e90d..40f9ba3a 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -2404,70 +2404,6 @@ func setExampleresult(ctx context.Context, result shuffle.AppExecutionExample) e return nil } -func getUserApps(resp http.ResponseWriter, request *http.Request) { - cors := shuffle.HandleCors(resp, request) - if cors { - return - } - - ctx := context.Background() - user, userErr := shuffle.HandleApiAuthentication(resp, request) - if userErr != nil { - log.Printf("[WARNING] Api authentication failed in get all apps - this does NOT require auth in the cloud.: %s", userErr) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000, 0) - if err != nil { - log.Printf("[WARNING] Failed getting apps (getworkflowapps): %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - filteredApps := workflowapps[:0] - for _, app := range workflowapps { - if app.Owner == user.Id { - filteredApps = append(filteredApps, app) - } else if app.Contributors != nil { - for _, contributor := range app.Contributors { - if contributor == user.Id { - filteredApps = append(filteredApps, app) - } - } - } - } - - if len(user.PrivateApps) > 0 { - for _, item := range user.PrivateApps { - found := false - for _, app := range filteredApps { - if item.ID == app.ID || !(item.Owner == user.Id) { - found = true - break - } - } - - if !found { - filteredApps = append(filteredApps, item) - } - } - } - - newbody, err := json.Marshal(filteredApps) - if err != nil { - log.Printf("[ERROR] Failed unmarshalling all newapps: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow apps"}`))) - return - } - - resp.WriteHeader(200) - resp.Write(newbody) -} - func getWorkflowApps(resp http.ResponseWriter, request *http.Request) { cors := shuffle.HandleCors(resp, request) if cors { From 7eda67bc443c16b4fdfa46a087da407113a6c615 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Tue, 12 Mar 2024 12:29:50 +0000 Subject: [PATCH 044/142] added highlighting to app chips --- frontend/src/components/OrgHeaderexpanded.jsx | 214 ++++++------------ 1 file changed, 75 insertions(+), 139 deletions(-) diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 350b456c..6946de94 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -167,41 +167,25 @@ const OrgHeaderexpanded = (props) => { const [workflow, setWorkflow] = React.useState({}) // notification workflow - const [notificationApp, setNotificationApp] = React.useState("") const [notificationWorkflowModal, setNotificationWorkflowModal] = React.useState(false); const [selectedAppDetails, setSelectedAppDetails] = React.useState({}); const [notificationWorkflowTestModal, setNotificationWorkflowTestModal] = React.useState(false); - const [webhookInputValue, setWebhookInputValue] = React.useState(""); - const [authOptions, setAuthOptions] = React.useState([]); const [selectedAuth, setSelectedAuth] = React.useState(''); - // const [selectedAppAuth, setSelectedAppAuth] = React.useState({}); // for getting selected app auth parameters - const [authenticationModal, setAuthenticationModal] = React.useState(false); - + const [emailData,setEmailData] = React.useState([]); const [notificationAppDetails, setNotificationAppDetails] = React.useState([]); + const [generatedWorkflow, setGeneatedWorkflow] = React.useState({}); - // const [tempResult, setTempResult] = React.useState([]) - - // useEffect(() => { - // if (authOptions.length < 1) { - // {getAppConfig(selectedAppDetails.id)} - // } - // }, [authOptions.length]); // not using this now - - useEffect(() => { - if (notificationAppList.length > 0) { - (async () => { - const nameList = notificationAppList.map(item => item.name); - await prepareNotificationAppList(nameList); - })(); - } - }, [notificationAppList]); - // for jira & email modal const [textFieldValue, setTextFieldValue] = React.useState(""); const [textFieldOneValue, setTextFieldOneValue] = React.useState(""); - const getAvailableWorkflows = (trigger_index) => { + useEffect(() => { + let nameList = notificationAppList.length > 0? notificationAppList.map(item => item.name): ["email"]; + prepareNotificationAppList(nameList) + }, [notificationAppList,workflows]); + + const getAvailableWorkflows = (trigger_index) => { fetch(globalUrl + "/api/v1/workflows", { method: "GET", headers: { @@ -390,7 +374,6 @@ const OrgHeaderexpanded = (props) => { const prepareNotificationAppList = async (appList) => { // getting App ID,Authentication fields and saved auths for each app var result = [] - if (appList.length > 0) { fetch(globalUrl + "/api/v1/apps", { method: "GET", headers: { @@ -408,6 +391,8 @@ const OrgHeaderexpanded = (props) => { }).then((responseJson) => { if (responseJson !== undefined) { const filteredApps = responseJson.filter(app => appList.includes(app.name)); + const emailData = responseJson.filter(app => app.name === "email") + setEmailData(emailData) const appDetails = filteredApps.map(app => ({ name: app.name, id: app.id })); //mapped apps with IDs as sometime Ids were not correct in security framework // console.log("appDetails: ", appDetails) // result = appDetails @@ -434,7 +419,7 @@ const OrgHeaderexpanded = (props) => { } // console.log("responseJson of auth: ", responseJson.data) result = await mergeAuthData(appDetails, responseJson.data) - console.log("merged auth data: ", result) + // console.log("merged auth data: ", result) // console.log("result", result) result.map(item => { fetch(globalUrl + `/api/v1/apps/${item.id}/config`, { @@ -462,19 +447,19 @@ const OrgHeaderexpanded = (props) => { item.large_image = decodedString.large_image setNotificationAppDetails(result) console.log("notificationAppDetails: ", notificationAppDetails) - // setSelectedAppAuth(decodedString.authentication) + }).then(async()=>{ + await checkIfAlreadyGenerated(appList,workflows); + }).catch((error) => { console.log("Error getting app config: " + error); toast("Error getting app config: " + error); }) }) - // get auth config for each app as it is required to render the modal when auth is not available }) } }).catch((error) => { console.log("Error getting app ids: " + error); - }) - } + }) } @@ -504,7 +489,7 @@ const OrgHeaderexpanded = (props) => { const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId,issuetype) => { //currently only supports JIRA figure out a way to support more apps - var workflowName = `[GENARATED] ${appname} notification workflow` + var workflowName = `[GENERATED] ${appname} notification workflow` var workflowDescription = "Generated by Shuffle for sending info/error notifications." var data = { "name": workflowName, @@ -632,7 +617,7 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId const generateEmailNotificationWorkflow = async (appname,appImage,shuffleAPIKey,recepients) => { //currently only supports figure out a way to support more apps - var workflowName = `[GENARATED] ${appname} notification workflow` + var workflowName = `[GENERATED] ${appname} notification workflow` var workflowDescription = "Generated by Shuffle for sending info/error notifications." var data = { "name": workflowName, @@ -745,64 +730,6 @@ const generateEmailNotificationWorkflow = async (appname,appImage,shuffleAPIKey, }) } -const getAppAuth = async (appName) => { - fetch(globalUrl + "/api/v1/apps/authentication", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - toast(`Failed getting auth for ${appName}: `, response.reason); - console.log("Status not 200 for app auth :O!"); - return; - } - return response.json(); - }).then((responseJson) => { - if (!responseJson.success) { - console.log("Could not get app auth") - return; - } - var authList = responseJson.data.filter(entry => entry.app.name === appName) - console.log("authList: ", authList) - setAuthOptions(authList); - - }).catch((error) => { - console.log("Error getting app auth: " + error); - }) -} - -const getAppConfig = async (appId) => { - fetch(globalUrl + `/api/v1/apps/${appId}/config`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }).then((response) => { - if (response.status !== 200) { - toast(`Failed getting config for ${appId}: `, response.reason); - console.log("Status not 200 for app config :O!"); - return; - } - return response.json(); - }).then((responseJson) => { - if (!responseJson.success) { - console.log("Could not get app config") - return; - } - var decodedString = JSON.parse(atob(responseJson.app)); - console.log("dcodedString: ",decodedString) - return decodedString.authentication - // setSelectedAppAuth(decodedString.authentication) - }).catch((error) => { - console.log("Error getting app config: " + error); - }) -} const testWorkflowModal = notificationWorkflowTestModal ? ( ) : null -const notificationWorkflowModalValid = () => { -return selectedAuth && webhookInputValue; -}; - const modalView = notificationWorkflowModal ? ( {console.log("len Selected app details: ", selectedAppDetails)} - {(selectedAppDetails.authentication_data || selectedAppDetails.auth_config.required == false) ? + {(selectedAppDetails.authentication_data || (selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false) || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? <> - {selectedAppDetails.auth_config.required == false ? "No authentication required": + {(selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? "No authentication required": <> Pick an authentication method from the list @@ -892,14 +815,15 @@ const modalView = notificationWorkflowModal ? ( labelId="demo-simple-select-label" id="demo-simple-select" value={selectedAuth} - disabled = {selectedAppDetails.auth_config.required == false ? true : false} + disabled = {(selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false))} onChange={(event) => {setSelectedAuth(event.target.value) + console.log("event.target.value: ",event.target.value) console.log("Selected auth: ", selectedAuth) }} label="Available authentications" required={true} > - {selectedAppDetails.auth_config.required == false ? "No authentication required" : selectedAppDetails.authentication_data.map((option) => ( + {(selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? "No authentication required" : selectedAppDetails.authentication_data.map((option) => (
@@ -1010,45 +934,61 @@ const modalView = notificationWorkflowModal ? ( ) : null + const checkIfAlreadyGenerated = async (appList, workflows) => { // fixxxxxxxxxxxxxxxxxxxxx + + var workflowName = workflows.find(workflow => workflow.id === notificationWorkflow) + if (workflowName) { + workflowName = workflowName.name + } + else { + console.log("no workflow set") + return + } + if (workflowName) { + const parts = workflowName.split(' '); + console.log("parts", parts) + if (parts[0].toString() === "[GENERATED]" && parts.length > 1) { + console.log("parts1", parts[1]) + if ((appList.includes(parts[1]))) { + console.log("workflow already generated") + setGeneatedWorkflow({"app_name": parts[1]}) + } + } + } + else { + return + } + } + const renderChips = (apps) => { -if (!apps || apps.length === 0) { - return ( - { - console.log(`Clicked EMAIL`) - setSelectedAppDetails("email") - setNotificationWorkflowModal(true) - // setNotificationWorkflowModal(true) - }} - avatar={{"email} - /> - ) -} + return ( + + {apps.map((app) => ( + { + console.log(`Clicked ${app.name}`) + console.log("app: ",app) + setSelectedAppDetails(app) + if (app.authentication_data && app.authentication_data.length > 0){ //fixxxxxxxx + console.log("authdata: ",app.authentication_data[0]) + setSelectedAuth(app.authentication_data[app.authentication_data.length-1].id) + } + setNotificationWorkflowModal(true) + // getAppAuth(app.name) + console.log("selectedAppDEtails",selectedAppDetails) + }} + avatar={{app.name}} -return ( - - {apps.map((app) => ( - { - console.log(`Clicked ${app.name}`) - setSelectedAppDetails(app) - setNotificationWorkflowModal(true) - // getAppAuth(app.name) - console.log("selectedAppDEtails",selectedAppDetails) - }} - avatar={{app.name}} - /> - ))} - -); + /> + ))} + + ); }; - return (
@@ -1058,12 +998,8 @@ return ( {modalView} {/*{testWorkflowModal} */}
- {renderChips(notificationAppDetails)}
- {/* - - Add a Workflow that receives notifications from Shuffle when an error occurs in one of your workflows - - */} + {renderChips(notificationAppDetails.length > 0 ? notificationAppDetails : emailData)} +
{workflows !== undefined && workflows !== null && workflows.length > 0 ? Date: Thu, 14 Mar 2024 06:22:38 +0000 Subject: [PATCH 045/142] implemented changes to show parent of the suborg --- backend/go-app/main.go | 2 +- frontend/src/views/Admin.jsx | 381 ++++++++++++++++++++++++----------- 2 files changed, 268 insertions(+), 115 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 62652d92..97aede9d 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4923,7 +4923,7 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") - r.HandleFunc("/api/v1/subOrgs/{orgId}", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/sub_orgs/{orgId}", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS") // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 63800693..15351d38 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -163,6 +163,7 @@ const Admin = (props) => { const [curTab, setCurTab] = React.useState(0); const [users, setUsers] = React.useState([]); const [subOrgs, setSubOrgs] = useState([]); + const [parentOrg, setParentOrg] = React.useState(null); const [organizations, setOrganizations] = React.useState([]); const [orgSyncResponse, setOrgSyncResponse] = React.useState(""); const [userSettings, setUserSettings] = React.useState({}); @@ -1111,40 +1112,41 @@ If you're interested, please let me know a time that works for you, or set up a toast("Error getting current organization"); }); }; -const handleGetSubOrgs = (orgId) => { - if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { - const urlSearchParams = new URLSearchParams(window.location.search); - const params = Object.fromEntries(urlSearchParams.entries()); - const foundorgid = params["org_id"]; - if (foundorgid !== undefined && foundorgid !== null) { - orgId = foundorgid; - } - } - - if (orgId.length === 0) { - toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); - return; - } + const handleGetSubOrgs = (orgId) => { - fetch(`${globalUrl}/api/v1/subOrgs/${orgId}`, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }) + if (orgId.length === 0) { + toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); + return; + } + + fetch(`${globalUrl}/api/v1/sub_orgs/${orgId}`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) .then((response) => { + if (!response.ok) { + throw new Error('Failed to fetch sub organizations'); + } return response.json(); }) .then((responseJson) => { - setSubOrgs(responseJson); + if (responseJson.success === false) { + toast("Failed getting your org. If this persists, please contact support."); + } else { + const { subOrgs, parentOrg } = responseJson; + setSubOrgs(subOrgs); + setParentOrg(parentOrg); + } }) - .catch((error) => { - console.log("Error getting sub orgs: ", error); - toast("Error getting sub organizations"); - }); -}; + .catch((error) => { + console.log("Error getting sub orgs: ", error); + toast("Error getting sub organizations"); + }); + }; const handleClickChangeOrg = (orgId) => { // Don't really care about the logout @@ -4745,6 +4747,13 @@ const handleClickChangeOrg = (orgId) => {
) : null; + const imagesize = 40; + const imageStyle = { + width: imagesize, + height: imagesize, + pointerEvents: "none", + }; + const organizationsTab = curTab === 7 ? (
@@ -4765,95 +4774,245 @@ const handleClickChangeOrg = (orgId) => { > Add suborganization - -{subOrgs.length > 0 ? ( - -
-

Sub Organizations of the Current Organization

-
- - + - - - - - - - - - {subOrgs.map((data, index) => { - const imagesize = 40; - const imageStyle = { - width: imagesize, - height: imagesize, - pointerEvents: "none", - }; - const image = - data.image === "" ? ( - {data.name} - ) : ( - {data.name} - ); - - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } - - return ( - - - - - - - - - +
+

+ {" "} + Your Parent Organization +

+
+ + {(() => { + const image = + parentOrg.image === "" ? ( + {parentOrg.name} + ) : ( + {parentOrg.name} ); - })} -
-
+ const bgColor = "#27292d"; + + return ( + + + + + + + + + + + + + + + + + ); + })()} +
+ ) : null + } + + {subOrgs.length > 0 ? ( + + +
+

+ Sub Organizations of the Current Organization +

+
+ + + + + + + + + + + + {subOrgs.map((data, index) => { + const image = + data.image === "" ? ( + {data.name} + ) : ( + {data.name} + ); + + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + return ( + + + + + + + + + ); + })} + + +
+ ) : null + } - -) : ( -
-

No Sub-Organizations found for the Current Organization

-
-)} + style={{ + marginTop: 20, + marginBottom: 20, + backgroundColor: theme.palette.inputColor, + }} +/> -
-

All Tenants

-
+
+

+ All Tenants +

+
{ ? "True" : "False"; - const imagesize = 40; - const imageStyle = { - width: imagesize, - height: imagesize, - pointerEvents: "none", - }; const image = data.image === "" ? ( Date: Thu, 14 Mar 2024 09:47:01 +0100 Subject: [PATCH 046/142] Synced over files and go.mod --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 + frontend/src/components/EditWorkflow.jsx | 96 +++++++++- frontend/src/components/ParsedAction.jsx | 132 ++++++------- frontend/src/components/ShuffleCodeEditor.jsx | 5 +- frontend/src/views/AngularWorkflow.jsx | 181 +++++++++++++----- frontend/src/views/AppCreator.jsx | 2 +- frontend/src/views/Docs.jsx | 6 +- frontend/src/views/RunWorkflow.jsx | 13 ++ 9 files changed, 309 insertions(+), 130 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index d1678703..0895bde5 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.88 + github.com/shuffle/shuffle-shared v0.5.91 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index a86377b2..f99af079 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -461,6 +461,8 @@ github.com/shuffle/shuffle-shared v0.5.81 h1:pt4lT42FrXN/kd/vlYtm7nXShZI0mOamXUM github.com/shuffle/shuffle-shared v0.5.81/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shuffle/shuffle-shared v0.5.88 h1:YNM6xtnKg0BoMmw2pqV/LnNAsMWMrAzYIAy+o+ChdfI= github.com/shuffle/shuffle-shared v0.5.88/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.5.91 h1:CN2K4iDt2zjx7MR9B+u5Yb7tA8IEw5lk8+Ab+Wia11Q= +github.com/shuffle/shuffle-shared v0.5.91/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 081b6f92..07e21578 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -49,12 +49,14 @@ import { } from '@mui/x-date-pickers' import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' +import { useStyles } from '../views/AppCreator.jsx' import { ExpandLess as ExpandLessIcon, ExpandMore as ExpandMoreIcon, Publish as PublishIcon, OpenInNew as OpenInNewIcon, + Add as AddIcon, } from "@mui/icons-material"; const EditWorkflow = (props) => { @@ -74,6 +76,10 @@ const EditWorkflow = (props) => { const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "") const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day')) + const [inputFields, setInputFields] = React.useState([]) + + const classes = useStyles(); + // Gets the generated workflow const getGeneratedWorkflow = (workflow_id) => { fetch(globalUrl + "/api/v1/workflows/" + workflow_id, { @@ -352,6 +358,9 @@ const EditWorkflow = (props) => { {showMoreClicked === true ? + + +
Status @@ -467,9 +476,94 @@ const EditWorkflow = (props) => { margin="dense" fullWidth /> + + {/* + + Input fields + + + Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the workflow run page. + + + + + {inputFields.length === 0 ? + + : null} + + {inputFields.map((data, index) => { + console.log("Inputfield: ", data) + + return ( +
+ { + inputFields[index].name = e.target.value + setInputFields(inputFields) + setUpdate(Math.random()); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + minHeight: 50, + }, + }} + /> + +
+ ) + })} + */} : null} - + + { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index ab2ead31..c13fecd9 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -2234,23 +2234,23 @@ const ParsedAction = (props) => { parsedPaths = GetParsedPaths(innerdata.example, ""); } - const coverColor = "#82ccc3" - //menuPosition.left -= 50 - //menuPosition.top -= 250 - //console.log("POS: ", menuPosition1) - var menuPosition1 = menuPosition - if (menuPosition1 === null) { - menuPosition1 = { - "left": 0, - "top": 0, - } - } else if (menuPosition1.top === null || menuPosition1.top === undefined) { - menuPosition1.top = 0 - } else if (menuPosition1.left === null || menuPosition1.left === undefined) { - menuPosition1.left = 0 - } + const coverColor = "#82ccc3" + //menuPosition.left -= 50 + //menuPosition.top -= 250 + //console.log("POS: ", menuPosition1) + var menuPosition1 = menuPosition + if (menuPosition1 === null) { + menuPosition1 = { + "left": 0, + "top": 0, + } + } else if (menuPosition1.top === null || menuPosition1.top === undefined) { + menuPosition1.top = 0 + } else if (menuPosition1.left === null || menuPosition1.left === undefined) { + menuPosition1.left = 0 + } - //console.log("POS1: ", menuPosition1) + //console.log("POS1: ", menuPosition1) return parsedPaths.length > 0 ? ( { handleItemClick([innerdata]); }} > - + { // const icon = pathdata.type === "value" ? ( - + ) : pathdata.type === "list" ? ( ) : ( - + ); // - const indentation_count = (pathdata.name.match(/\./g) || []).length+1 - const baseIndent =
- //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 - const boxPadding = 0 - const namesplit = pathdata.name.split(".") - const newname = namesplit[namesplit.length-1] - return ( + const indentation_count = (pathdata.name.match(/\./g) || []).length+1 + const baseIndent =
+ //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 + const boxPadding = 0 + const namesplit = pathdata.name.split(".") + const newname = namesplit[namesplit.length-1] + return ( { @@ -2341,35 +2339,31 @@ const ParsedAction = (props) => { placement="left" >
- {Array(indentation_count).fill().map((subdata, subindex) => { - return ( - baseIndent - ) - })} - {icon} {newname} - {pathdata.type === "list" ? { - e.preventDefault() - e.stopPropagation() + {Array(indentation_count).fill().map((subdata, subindex) => { + return ( + baseIndent + ) + })} + {icon} {newname} + {pathdata.type === "list" ? { + e.preventDefault() + e.stopPropagation() - console.log("INNER: ", innerdata, pathdata) - - // Removing .list from autocomplete - var newname = pathdata.name - if (newname.length > 5) { - newname = newname.slice(0, newname.length-5) - } - selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` - selectedAction.parameters[count].value = selectedActionParameters[count].value; - setSelectedAction(selectedAction); - setUpdate(Math.random()); - setShowDropdown(false); - setMenuPosition(null); - - // innerdata.name - // pathdata.name - //handleItemClick([innerdata, newpathdata]) - //console.log("CLICK LENGTH!") - }} /> : null} + console.log("INNER: ", innerdata, pathdata) + + // Removing .list from autocomplete + var newname = pathdata.name + if (newname.length > 5) { + newname = newname.slice(0, newname.length-5) + } + + selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` + selectedAction.parameters[count].value = selectedActionParameters[count].value; + setSelectedAction(selectedAction); + setUpdate(Math.random()); + setShowDropdown(false); + setMenuPosition(null); + }} /> : null}
@@ -2747,7 +2741,6 @@ const ParsedAction = (props) => { paddingRight: 0, }} onClick={() => { - console.log("FIND EXAMPLE RESULTS FOR ", selectedAction); if (workflowExecutions.length > 0) { // Look for the ID const found = false; @@ -2756,11 +2749,6 @@ const ParsedAction = (props) => { continue; } - // Enforces it to show at least one - //if (workflowExecutions[key].execution_argument.includes("too large") && key !== workflowExecutions.length - 1) { - // continue - //} - var foundResult = workflowExecutions[key].results.find( (result) => result.action.id === selectedAction.id ) @@ -2769,14 +2757,14 @@ const ParsedAction = (props) => { continue; } - const oldstartnode = cy.getElementById(selectedAction.id); - console.log("FOUND NODe: ", oldstartnode) - if (oldstartnode !== undefined && oldstartnode !== null) { - const foundname = oldstartnode.data("label") - if (foundname !== undefined && foundname !== null) { - foundResult.action.label = foundname - } - } + const oldstartnode = cy.getElementById(selectedAction.id); + console.log("FOUND NODe: ", oldstartnode) + if (oldstartnode !== undefined && oldstartnode !== null) { + const foundname = oldstartnode.data("label") + if (foundname !== undefined && foundname !== null) { + foundResult.action.label = foundname + } + } setSelectedResult(foundResult); if (setCodeModalOpen !== undefined) { @@ -3354,6 +3342,8 @@ const ParsedAction = (props) => { style={{ backgroundColor: theme.palette.inputColor, color: "white", + maxWidth: 500, + overflowX: "auto", }} value={data} > diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 3b6e02a2..9015f485 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -61,10 +61,11 @@ import { tags as t } from '@lezer/highlight'; const liquidFilters = [ - {"name": "Size", "value": "size", "example": ""}, - {"name": "Date", "value": `date: "%Y%m%d"`, "example": `{{ "now" | date: "%s" }}`}, + {"name": "Default", "value": `default: []`, "example": `{{ "" | default: "no input" }}`}, {"name": "Split", "value": `split: ","`, "example": `{{ "this,can,become,a,list" | split: "," }}`}, {"name": "Join", "value": `join: ","`, "example": `{{ ["this","can","become","a","string"] | join: "," }}`}, + {"name": "Size", "value": "size", "example": ""}, + {"name": "Date", "value": `date: "%Y%m%d"`, "example": `{{ "now" | date: "%s" }}`}, {"name": "Escape String", "value": `{{ \"\"\"'string with weird'" quotes\"\"\" | escape_string }}`, "example": ``}, {"name": "Flatten", "value": `flatten`, "example": `{{ [1, [1, 2], [2, 3, 4]] | flatten }}`}, {"name": "URL encode", "value": `url_encode`, "example": `{{ "https://www.google.com/search?q=hello world" | url_encode }}`}, diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 0468a12a..fb007b0b 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -80,6 +80,7 @@ import { Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, + DragIndicator as DragIndicatorIcon, Error as ErrorIcon, Warning as WarningIcon, ArrowLeft as ArrowLeftIcon, @@ -250,6 +251,7 @@ function useWindowSize() { function updateSize() { setSize([window.innerWidth, window.innerHeight]); } + window.addEventListener("resize", updateSize); updateSize(); return () => window.removeEventListener("resize", updateSize); @@ -598,9 +600,29 @@ const AngularWorkflow = (defaultprops) => { const unloadText = "Are you sure you want to leave without saving (CTRL+S)?"; const classes = useStyles(); - const [bodyWidth, bodyHeight] = useWindowSize() - //console.log("Mobile: ", isMobile, bodyWidth, bodyHeight) + var [bodyWidth, bodyHeight] = useWindowSize() const cytoscapeWidth = isMobile ? bodyWidth - leftBarSize : bodyWidth - leftBarSize - 25 + + /* + // Zoom testing to try autofixing for small screens + if (document !== undefined && document !== null && !isMobile) { + const currentZoom = document.body.style.zoom; + + if (bodyWidth < 1367 || bodyHeight < 769) { + console.log("LOWER ZOOM") + document.body.style.zoom = "80%" + bodyWidth = bodyWidth*0.8 + bodyHeight = bodyHeight*0.8 + } else { + console.log("RESET ZOOM") + document.body.style.zoom = "100%" + } + } + + console.log("Width, height: ", bodyWidth, bodyHeight) + */ + + //console.log("Mobile: ", isMobile, bodyWidth, bodyHeight) const [elements, setElements] = useState([]); const [loopRunning, setLoopRunning] = useState(false) @@ -7691,7 +7713,7 @@ const AngularWorkflow = (defaultprops) => { marginTop: 1, overflowY: "auto", overflowX: "hidden", - }; + } const handleAppDrag = (e, app) => { const cycontainer = cy.container(); @@ -8426,6 +8448,7 @@ const AngularWorkflow = (defaultprops) => {
) })} + {visibleApps.length <= 4 ? (
{
- ) : null} + ) : +
}
) : apps.length > 0 ? (
{ const AppConditionHandler = (props) => { const { tmpdata, type } = props; - const [data] = useState({...tmpdata}); + const [data] = useState(tmpdata); const [multiline, setMultiline] = useState(false); const [showAutocomplete, setShowAutocomplete] = React.useState(false); const [actionlist, setActionlist] = React.useState([]); @@ -9595,9 +9621,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "equals"; - setConditionValue(newConditionValue); + conditionValue.value = "equals"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"equals"} @@ -9607,9 +9632,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "does not equal"; - setConditionValue(newConditionValue); + conditionValue.value = "does not equal"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"does not equal"} @@ -9619,9 +9643,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "startswith"; - setConditionValue(newConditionValue); + conditionValue.value = "startswith"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"starts with"} @@ -9631,9 +9654,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "endswith"; - setConditionValue(newConditionValue); + conditionValue.value = "endswith"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"ends with"} @@ -9643,9 +9665,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "contains"; - setConditionValue(newConditionValue); + conditionValue.value = "contains"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"contains"} @@ -9655,9 +9676,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "contains_any_of"; - setConditionValue(newConditionValue); + conditionValue.value = "contains_any_of"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"contains_any_of"} @@ -9667,9 +9687,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "matches regex"; - setConditionValue(newConditionValue); + conditionValue.value = "matches regex"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"matches regex"} @@ -9679,9 +9698,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "larger than"; - setConditionValue(newConditionValue); + conditionValue.value = "larger than"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"larger than"} @@ -9691,9 +9709,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "less than"; - setConditionValue(newConditionValue); + conditionValue.value = "less than"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"less than"} @@ -9703,9 +9720,8 @@ const AngularWorkflow = (defaultprops) => { { - const newConditionValue = { ...conditionValue }; - newConditionValue.value = "is empty"; - setConditionValue(newConditionValue); + conditionValue.value = "is empty"; + setConditionValue(conditionValue); setVariableAnchorEl(null); }} key={"is empty"} @@ -9873,8 +9889,6 @@ const AngularWorkflow = (defaultprops) => { marginTop: "15px", marginLeft: "10px", overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", maxWidth: 72, }} > @@ -9894,7 +9908,7 @@ const AngularWorkflow = (defaultprops) => { flex: 1, textAlign: "center", marginTop: "15px", - overflow: "hidden", + overflow: "hidden", maxWidth: 72, }} onClick={() => { }} @@ -9918,8 +9932,6 @@ const AngularWorkflow = (defaultprops) => { marginBottom: "auto", marginLeft: "10px", overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", maxWidth: 72, }} > @@ -13582,16 +13594,14 @@ const AngularWorkflow = (defaultprops) => { }, }} disabled={ - workflow.triggers[selectedTriggerIndex].status === "running" + workflow.triggers[selectedTriggerIndex] === null || workflow.triggers[selectedTriggerIndex] === undefined ? false : workflow.triggers[selectedTriggerIndex].status === "running" } fullWidth rows="6" multiline color="primary" defaultValue={ - workflow.triggers[selectedTriggerIndex] !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== null && workflow.triggers[selectedTriggerIndex].parameters.length > 1 ? - workflow.triggers[selectedTriggerIndex].parameters[1].value - : "" + workflow.triggers[selectedTriggerIndex] !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters !== null && workflow.triggers[selectedTriggerIndex].parameters.length > 1 ? workflow.triggers[selectedTriggerIndex].parameters[1].value : "" } placeholder='{"example": {"json": "is cool"}}' onBlur={(e) => { @@ -13661,11 +13671,16 @@ const AngularWorkflow = (defaultprops) => { top: isMobile ? 30 : appBarSize + 20, }; + + + const TopCytoscapeBar = (props) => { if (workflow.public === true) { return null } + const isCorrectOrg = workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id + return (
@@ -13691,7 +13706,7 @@ const AngularWorkflow = (defaultprops) => {

{workflow.name}

- {workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id ? null : + {isCorrectOrg ? null : Warning: Change { ) } - const showErrors = !isMobile && !workflow.public && workflow.errors !== undefined && workflow.errors !== null && workflow.errors.length > 0 ?
{ > {/**/} + + Workflow Issues: {workflow.errors.length} { } var draggingDisabled = false; + + // Should probably put this on the backend instead when notifications are made :)) + const getErrorSuggestion = (result) => { + if (result === undefined || result === null) { + return "" + } + + if (result.success !== false) { + return "" + } + + var stringjson = result + try { + stringjson = JSON.stringify(result) + } catch (e) { + } + + console.log("JSON: ", stringjson) + stringjson = stringjson.toLowerCase() + if (stringjson.includes("localhost")) { + return "You can't use localhost in apps. Use the external ip or url of the server instead" + } + + if (stringjson.includes("connectionerror")) { + return "Your URL is most likely incorrect." + } + + if (stringjson.includes("result too large to handle")) { + return "Execution loading failed. Reload the execution by closing it and clicking it again" + } + + return "" + } + + const currentSuggestion = getErrorSuggestion(validate.result) const codePopoutModal = !codeModalOpen ? null : ( { + + { + }} + > + + + { e.preventDefault(); setCodeModalOpen(false); @@ -16836,12 +16907,20 @@ const AngularWorkflow = (defaultprops) => { > {selectedResult.action.label.replaceAll("_", " ")}
-
{selectedResult.action.name}
+
{selectedResult.action.name}
-
- Status {selectedResult.status} -
+ + + {currentSuggestion.length > 0 ? +
+ Debug Info: {currentSuggestion} +
+ : +
+ Status {selectedResult.status} +
+ } {validate.valid ? ( { margin: "auto", marginTop: 50, }}> - + Documentation
@@ -938,7 +938,7 @@ const Docs = (defaultprops) => { @@ -957,7 +957,7 @@ const Docs = (defaultprops) => { // Padding and zIndex etc set because of footer in cloud. const loadedCheck = ( -
+
{postDataBrowser} {postDataMobile}
diff --git a/frontend/src/views/RunWorkflow.jsx b/frontend/src/views/RunWorkflow.jsx index a8046fc2..bd5d1bc3 100644 --- a/frontend/src/views/RunWorkflow.jsx +++ b/frontend/src/views/RunWorkflow.jsx @@ -333,6 +333,19 @@ const RunWorkflow = (defaultprops) => { "execution_argument": executionArgument } + if (workflow.start !== undefined && workflow.start !== null && workflow.start.length > 0) { + data.start = workflow.start + } else { + if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { + for (let actionkey in workflow.actions) { + if (workflow.actions[actionkey].isStartNode) { + data.start = workflow.actions[actionkey].id + break + } + } + } + } + var url = `${globalUrl}/api/v1/workflows/${props.match.params.key}/execute` var fetchBody = { headers: { From b5d04bef134f522d4d6a1faa13845a4a8ffcaccb Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 14 Mar 2024 09:54:59 +0100 Subject: [PATCH 047/142] Fixed major sync issues with cloud <-> onprem codebase --- frontend/package.json | 1 + frontend/src/codeeditor-index.css | 60 + .../src/components/ShuffleCodeEditor1.jsx | 1760 +++++++++++++++++ frontend/src/views/HandlePaymentNew.jsx | 19 + 4 files changed, 1840 insertions(+) create mode 100644 frontend/src/codeeditor-index.css create mode 100644 frontend/src/components/ShuffleCodeEditor1.jsx diff --git a/frontend/package.json b/frontend/package.json index 186c50c7..c45a9a9f 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -59,6 +59,7 @@ "mui-chips-input": "^2.1.3", "mui-nested-menu": "^3.2.1", "react": "^18.2.0", + "react-ace": "^10.1.0", "react-alice-carousel": "^2.6.4", "react-avatar-editor": "^11.1.0", "react-beforeunload": "^2.2.1", diff --git a/frontend/src/codeeditor-index.css b/frontend/src/codeeditor-index.css new file mode 100644 index 00000000..3691b90d --- /dev/null +++ b/frontend/src/codeeditor-index.css @@ -0,0 +1,60 @@ +@import url('https://fonts.googleapis.com/css?family=Nunito+Sans'); + +body { + margin: 0; + padding: 0; + font-family: "Nunito Sans", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} + +.partner_container { + height: 200px; + position: relative; +} + +.imggrow { + padding: 60px auto 50px auto; + height: 100px; + width: auto !important; + margin-right: 1rem; + width: 100%; + transition: transform .2s; + border-radius: 10px; + float: center !important; + text-align: center !important; + justify-content: "center" !important; + background-color: #ffffff; + color: black; + position: relative; + overflow: hidden; + z-index: 1; +} + +.imggrow:hover{ + height: 230px; + transform: transform(1.5); + cursor: pointer; + border-color: #3585f9; + display: block; + box-shadow: 0 4px 8px #FC7305; +} + +.bad-marker { + position: absolute; + background-color: #7b322e; + color: white !important; + opacity: 1; +} + +.good-marker { + position: absolute; + background-color: #5ea36a; + color: white !important; + opacity: 0.6; +} diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx new file mode 100644 index 00000000..9bec37ad --- /dev/null +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -0,0 +1,1760 @@ +import React, { useRef, useState, useEffect, useLayoutEffect, } from 'react'; +import { toast } from 'react-toastify'; +import '../codeeditor-index.css'; +import { + CircularProgress, + IconButton, + Dialog, + Modal, + Tooltip, + DialogTitle, + DialogContent, + Typography, + Paper, + Menu, + MenuItem, + Button, +} from '@mui/material'; + +import theme from '../theme.jsx'; +import Checkbox from '@mui/material/Checkbox'; +import { isMobile } from "react-device-detect" +import { NestedMenuItem } from "mui-nested-menu" +import { GetParsedPaths, FindJsonPath } from "../views/Apps.jsx"; +import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx"; + +import { + FullscreenExit as FullscreenExitIcon, + Extension as ExtensionIcon, + Apps as AppsIcon, + FavoriteBorder as FavoriteBorderIcon, + Schedule as ScheduleIcon, + FormatListNumbered as FormatListNumberedIcon, + SquareFoot as SquareFootIcon, + Circle as CircleIcon, + Add as AddIcon, + PlayArrow as PlayArrowIcon, + AutoFixHigh as AutoFixHighIcon, + CompressOutlined, + QrCodeScannerOutlined, + + Close as CloseIcon, + DragIndicator as DragIndicatorIcon, +} from '@mui/icons-material'; + + +import { validateJson } from "../views/Workflows.jsx"; +import ReactJson from "react-json-view"; +import PaperComponent from "../components/PaperComponent.jsx"; + +import { padding, textAlign } from '@mui/system'; +import data from '../frameworkStyle.jsx'; +import { useNavigate, Link, useParams } from "react-router-dom"; +import { tags as t } from '@lezer/highlight'; + + +import AceEditor from "react-ace"; +import 'ace-builds/src-noconflict/mode-python'; +import 'ace-builds/src-noconflict/theme-twilight'; +import "ace-builds/src-noconflict/ext-language_tools"; +import ace from "ace-builds"; + +const liquidFilters = [ + {"name": "Default", "value": `default: []`, "example": `{{ "" | default: "no input" }}`}, + {"name": "Split", "value": `split: ","`, "example": `{{ "this,can,become,a,list" | split: "," }}`}, + {"name": "Join", "value": `join: ","`, "example": `{{ ["this","can","become","a","string"] | join: "," }}`}, + {"name": "Size", "value": "size", "example": ""}, + {"name": "Date", "value": `date: "%Y%m%d"`, "example": `{{ "now" | date: "%s" }}`}, + {"name": "Escape String", "value": `{{ \"\"\"'string with weird'" quotes\"\"\" | escape_string }}`, "example": ``}, + {"name": "Flatten", "value": `flatten`, "example": `{{ [1, [1, 2], [2, 3, 4]] | flatten }}`}, + {"name": "URL encode", "value": `url_encode`, "example": `{{ "https://www.google.com/search?q=hello world" | url_encode }}`}, + {"name": "URL decode ", "value": `url_decode`, "example": `{{ "https://www.google.com/search?q=hello%20world" | url_decode }}`}, + {"name": "base64_encode", "value": `base64_encode`, "example": `{{ "https://www.google.com/search?q=hello%20world" | base64_encode }}`}, + {"name": "base64_decode", "value": `base64_decode`, "example": `{{ "aGVsbG8K" | base64_encode }}`}, +] + +const mathFilters = [ + {"name": "Plus", "value": "plus: 1", "example": `{{ "1" | plus: 1 }}`}, + {"name": "Minus", "value": "minus: 1", "example": `{{ "1" | minus: 1 }}`}, +] + +const pythonFilters = [ + {"name": "Hello World", "value": `{% python %}\nprint("hello world")\n{% endpython %}`, "example": ``}, + {"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``}, +] + +const extensions = [] +const CodeEditor = (props) => { + const { + globalUrl, + fieldCount, + actionlist, + changeActionParameterCodeMirror, + expansionModalOpen, + setExpansionModalOpen, + codedata, + setcodedata, + isFileEditor, + runUpdateText, + toolsAppId, + parameterName, + selectedAction , + workflowExecutions, + getParents, + + fieldname, + } = props + + + + const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); + + //const { setContainer } = useCodeMirror({ + // container: editorRef.current, + // extensions, + // value: localcodedata, + //}) + // const {codelang, setcodelang} = props + + const [validation, setValidation] = React.useState(false); + const [expOutput, setExpOutput] = React.useState(" "); + const [linewrap, setlinewrap] = React.useState(true); + //const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); + const [editorPopupOpen, setEditorPopupOpen] = React.useState(false); + + const [currentCharacter, setCurrentCharacter] = React.useState(-1); + const [currentLine, setCurrentLine] = React.useState(-1); + + const [variableOccurences, setVariableOccurences] = React.useState([]); + const [currentLocation, setCurrentLocation] = React.useState([]); + const [currentVariable, setCurrentVariable] = React.useState(""); + const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorEl2, setAnchorEl2] = React.useState(null); + const [anchorEl3, setAnchorEl3] = React.useState(null); + const [mainVariables, setMainVariables] = React.useState([]); + const [availableVariables, setAvailableVariables] = React.useState([]); + + const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); + + const [menuPosition, setMenuPosition] = useState(null); + const [showAutocomplete, setShowAutocomplete] = React.useState(false); + const [markers, setMarkers] = useState([]); + + const [isAiLoading, setIsAiLoading] = React.useState(false); + // let markers = []; + const baseResult = "" + const [executionResult, setExecutionResult] = useState({ + "valid": false, + "result": baseResult, + }) + const [executing, setExecuting] = useState(false) + + const liquidOpen = Boolean(anchorEl); + const mathOpen = Boolean(anchorEl2); + const pythonOpen = Boolean(anchorEl3); + + const handleMenuClose = () => { + setShowAutocomplete(false); + + setMenuPosition(null); + } + + let navigate = useNavigate(); + + useEffect(() => { + var allVariables = [] + var tmpVariables = [] + + if (actionlist === undefined || actionlist === null) { + return + } + + for(var i=0; i < actionlist.length; i++){ + allVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) + tmpVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) + + var parsedPaths = [] + if (typeof actionlist[i].example === "object") { + parsedPaths = GetParsedPaths(actionlist[i].example, ""); + } + + for (var key in parsedPaths) { + const fullpath = "$"+actionlist[i].autocomplete.toLowerCase()+parsedPaths[key].autocomplete + if (!allVariables.includes(fullpath)) { + allVariables.push(fullpath) + } + } + } + + setAvailableVariables(allVariables) + setMainVariables(tmpVariables) + + //console.log("Checking local codedata: ", localcodedata) + expectedOutput(localcodedata) + }, []) + + useEffect(() => { + expectedOutput(localcodedata) + }, [availableVariables]) + + var to_be_copied = ""; + const HandleJsonCopy = (base, copy, base_node_name) => { + if (typeof copy.name === "string") { + copy.name = copy.name.replaceAll(" ", "_"); + } + + //lol + if (typeof base === 'object' || typeof base === 'dict') { + base = JSON.stringify(base) + } + + if (base_node_name === "execution_argument" || base_node_name === "Execution Argument") { + base_node_name = "exec" + } + + console.log("COPY: ", base_node_name, copy); + + //var newitem = JSON.parse(base); + var newitem = validateJson(base).result + to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); + for (let copykey in copy.namespace) { + if (copy.namespace[copykey].includes("Results for")) { + continue; + } + + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.namespace[copykey]]; + if (!isNaN(copy.namespace[copykey])) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.namespace[copykey]; + } + } + } + + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.name]; + if (!isNaN(copy.name)) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.name; + } + } + + to_be_copied.replaceAll(" ", "_"); + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(to_be_copied); + copyText.select(); + copyText.setSelectionRange(0, 99999); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + console.log("COPYING!"); + toast("Copied JSON path to clipboard.") + } else { + console.log("Couldn't find element ", elementName); + } + } + + const aiSubmit = (value, inputAction) => { + if (value === undefined || value === "") { + console.log("No value input!") + return + } + + setIsAiLoading(true) + + // Time to construct this huh... Hmm + var AppContext = [] + if (inputAction !== undefined && inputAction !== null && getParents !== undefined && getParents !== null && workflowExecutions !== undefined && workflowExecutions !== null) { + const parents = getParents(inputAction) + + console.log("Parents: ", parents) + var actionlist = [] + if (parents.length > 1) { + for (let [key,keyval] in Object.entries(parents)) { + const item = parents[key]; + if (item.label === "Execution Argument") { + continue; + } + + var exampledata = item.example === undefined || item.example === null ? "" : item.example; + // Find previous execution and their variables + //exampledata === "" && + if (workflowExecutions.length > 0) { + // Look for the ID + const found = false; + for (let [key,keyval] in Object.entries(workflowExecutions)) { + if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { + continue; + } + + var foundResult = workflowExecutions[key].results.find((result) => result.action.id === item.id); + if (foundResult === undefined || foundResult === null) { + continue; + } + + if (foundResult.result !== undefined && foundResult.result !== null) { + foundResult = foundResult.result + } + + const valid = validateJson(foundResult, true) + if (valid.valid) { + if (valid.result.success === false) { + //console.log("Skipping success false autocomplete") + } else { + exampledata = valid.result; + break; + } + } else { + exampledata = foundResult; + } + } + } + + // 1. Take + const itemlabelComplete = item.label === null || item.label === undefined ? "" : item.label.split(" ").join("_"); + + const actionvalue = { + app_name: item.app_name, + action_name: item.name, + label: item.label, + + type: "action", + id: item.id, + name: item.label, + autocomplete: itemlabelComplete, + example: exampledata, + }; + + actionlist.push(actionvalue); + } + } + + var fixedResults = [] + for (var i = 0; i < actionlist.length; i++) { + const item = actionlist[i]; + const responseFix = SetJsonDotnotation(item.example, "") + + // Check if json + const validated = validateJson(responseFix) + var exampledata = responseFix; + if (validated.valid) { + exampledata = JSON.stringify(validated.result) + } + + AppContext.push({ + "app_name": item.app_name, + "action_name": item.action_name, + "label": item.label, + "example": exampledata, + }) + } + } + + var conversationData = { + "query": value, + "output_format": "action", + "app_context": AppContext, + } + + if (inputAction !== undefined) { + console.log("Add app context! This should them get parameters directly") + conversationData.output_format = "action_parameters" + + conversationData.app_id = inputAction.app_id + conversationData.app_name = inputAction.app_name + conversationData.action_name = inputAction.name + conversationData.parameters = inputAction.parameters + } + + fetch(`${globalUrl}/api/v1/conversation`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(conversationData), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + console.log("Conversation response: ", responseJson) + setIsAiLoading(false) + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + } + + return + } + + if (inputAction !== undefined) { + console.log("In input action! Should check params if they match, and add suggestions") + + if (responseJson.parameters === undefined || responseJson.parameters.length === 0) { + return + } + + for (let respParam of responseJson.parameters) { + if (respParam.name !== parameterName) { + continue + } + + if (respParam.value === "") { + break + } + + setlocalcodedata(respParam.value) + break + } + + return + } + }) + .catch((error) => { + setIsAiLoading(false) + console.log("Conv response error: ", error); + }); + } + + const autoFormat = (input) => { + // Check if it's default too + if (validation !== true) { + + // Should try to automatically fix this input + console.log("Running AI input fixer") + if (aiSubmit !== undefined && parameterName !== undefined && selectedAction !== undefined) { + + // Should remove params from selectedAction that aren't parameterName + var tmpAction = JSON.parse(JSON.stringify(selectedAction)) + var tmpParams = selectedAction.parameters.filter((param) => param.name === parameterName) + + var aiMsg = `Make it valid for action ${tmpAction.label} with parameter ${parameterName}: ` + if (tmpParams.length > 0) { + aiMsg += tmpParams[0].value + } + + + if (localcodedata.startsWith("//")) { + aiMsg = localcodedata + } + + tmpAction.parameters = tmpParams + console.log("Parameters: ", tmpParams.length) + + aiSubmit(aiMsg, tmpAction) + } + + return + } + + try { + input = JSON.stringify(JSON.parse(input), null, 4) + } catch (e) { + console.log("Failed magic JSON stringification: ", e) + } + + if (input !== localcodedata) { + setlocalcodedata(input) + } + } + + const findIndex = (line, loc) => { + var code_line = localcodedata.split('\n')[line] + if (code_line === undefined) { + return + } + + var dollar_occurences = [] + var dollar_occurences_len = [] + var variable_ranges = [] + var popup = false + + for(var ch=0; ch < code_line.length; ch++){ + if(code_line[ch] === '$'){ + dollar_occurences.push(ch) + } + } + + var variable_occurences = code_line.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) + + try{ + for(var occ = 0; occ < variable_occurences.length; occ++){ + dollar_occurences_len.push(variable_occurences[occ].length) + } + } catch (e) {} + + for(var occ = 0; occ < dollar_occurences.length; occ++){ + // var temp_arr = [] + // for(var occ_len = 0; occ_len { + if (inputvariable === undefined || inputvariable === null) { + return inputvariable + } + + if (!inputvariable.includes(".")) { + return inputvariable + } + + const itemsplit = inputvariable.split(".") + var newitem = [] + var removedIndexes = 0 + for (var key in itemsplit) { + var tmpitem = itemsplit[key] + + // Makes sure #0 and # are same, as we only visualize first one anyway + if (tmpitem.startsWith("#")) { + removedIndexes += tmpitem.length-1 + tmpitem = "#" + } + + newitem.push(tmpitem) + } + + return newitem.join(".") + //return inputvariable + } + + const highlight_variables = (value) => { + // var session = localcodedata.getSession(); + var code_lines = localcodedata.split('\n'); + + const newMarkers = []; + for (var i = 0; i < code_lines.length; i++) { + var current_code_line = code_lines[i]; + var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g); + + if (!variable_occurence) { + continue; + } + + var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]); + variable_occurence = new_occurences; + + var dollar_occurence = []; + for (let ch = 0; ch < current_code_line.length; ch++) { + if (current_code_line[ch] === '$' && (ch === 0)) { + dollar_occurence.push(ch); + } + } + + var dollar_occurence_len = [] + try{ + for(let occ = 0; occ < variable_occurence.length; occ++){ + dollar_occurence_len.push(variable_occurence[occ].length) + } + } catch (e) {} + + try { + if (variable_occurence.length === 0) { + //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #282828; border-radius: 0px; color: #b8bb26"}) + //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #; border-radius: 0px; color: inherit"}) + } + for (let occ = 0; occ < variable_occurence.length; occ++) { + const fixedVariable = fixVariable(variable_occurence[occ]) + var correctVariable = availableVariables.includes(fixedVariable) + var startCh = dollar_occurence[occ] + var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] + newMarkers.push({ + startRow: i, + startCol: startCh, + endRow: i, + endCol: endCh, + className: correctVariable ? "good-marker" : "bad-marker", + type: "text", + }) + } + + setMarkers(newMarkers) + } catch (e) { + console.log("Error in color highlighting: ", e); + } + } + }; + + const replaceVariables = (swapVariable) => { + // var updatedCode = localcodedata.slice(0,index) + "$" + str + localcodedata.slice(index+currentVariable.length+1,) + // setlocalcodedata(updatedCode) + // setEditorPopupOpen(false) + // setCurrentLocation(0) + // console.log(index) + // console.log(currentLocation) + + var code_lines = localcodedata.split('\n') + var parsedVariable = currentVariable + if (currentVariable === undefined || currentVariable === null) { + console.log("Location: ", currentLocation) + parsedVariable= "$" + } + + code_lines[currentLine] = code_lines[currentLine].slice(0,currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1]+parsedVariable.length,) + // console.log(code_lines) + var updatedCode = code_lines.join('\n') + // console.log(updatedCode) + setlocalcodedata(updatedCode) + } + + const fixStringInput = (new_input) => { + // Newline fixes + new_input = new_input.replace(/\r\n/g, "\\n") + new_input = new_input.replace(/\n/g, "\\n") + + // Quote fixes + new_input = new_input.replace(/\\"/g, '"') + new_input = new_input.replace(/"/g, '\\"') + + new_input = new_input.replace(/\\'/g, "'") + new_input = new_input.replace(/'/g, "\\'") + + + return new_input + } + + + const expectedOutput = (input) => { + //const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) + const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) + + // Whelp this is inefficient af. Single loop pls + // When the found array is empty. + if (found !== null && found !== undefined) { + try { + for (var i = 0; i < found.length; i++) { + try { + const fixedVariable = fixVariable(found[i]) + var valuefound = false + for (var j = 0; j < actionlist.length; j++) { + if(fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()){ + continue + } + + valuefound = true + try { + if (typeof actionlist[j].example === "object") { + + input = input.replace(found[i], JSON.stringify(actionlist[j].example), -1); + + } else if (actionlist[j].example.trim().startsWith("{") || actionlist[j].example.trim().startsWith("[")) { + input = input.replace(found[i], JSON.stringify(actionlist[j].example), -1); + } else { + const newExample = fixStringInput(actionlist[j].example) + input = input.replace(found[i], newExample, -1) + } + } catch (e) { + input = input.replace(found[i], actionlist[j].example, -1) + } + } + + + //if (!valuefound) { + // console.log("Couldn't find value "+fixedVariable) + //} + + if (!valuefound && availableVariables.includes(fixedVariable)) { + var shouldbreak = false + for (var k=0; k < actionlist.length; k++){ + var parsedPaths = [] + if (typeof actionlist[k].example === "object") { + parsedPaths = GetParsedPaths(actionlist[k].example, ""); + } + + for (var key in parsedPaths) { + const fullpath = "$"+actionlist[k].autocomplete.toLowerCase()+parsedPaths[key].autocomplete + if (fullpath !== fixedVariable) { + continue + } + + //if (actionlist[k].example === undefined) { + // actionlist[k].example = "TMP" + //} + + var new_input = "" + try { + new_input = FindJsonPath(fullpath, actionlist[k].example) + } catch (e) { + console.log("ERR IN INPUT: ", e) + } + + //console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) + + if (typeof new_input === "object") { + new_input = JSON.stringify(new_input) + } else { + if (typeof new_input === "string") { + // Check if it contains any newlines, and replace them with raw newlines + new_input = fixStringInput(new_input) + + // Replace quotes with nothing + } else { + console.log("NO TYPE? ", typeof new_input) + try { + new_input = new_input.toString() + } catch (e) { + new_input = "" + } + } + } + + input = input.replace(fixedVariable, new_input, -1) + input = input.replace(found[i], new_input, -1) + + //} catch (e) { + // input = input.replace(found[i], actionlist[k].example) + //} + + shouldbreak = true + break + } + + if (shouldbreak) { + break + } + } + } + } catch (e) { + console.log("Replace error: ", e) + } + } + } catch (e) { + console.log("Outer replace error: ", e) + } + } + + const tmpValidation = validateJson(input.valueOf()) + //setValidation(true) + if (tmpValidation.valid === true) { + setValidation(true) + setExpOutput(tmpValidation.result) + } else { + setExpOutput(input.valueOf()) + setValidation(false) + } + } + + const handleItemClick = (values) => { + if ( + values === undefined || + values === null || + values.length === 0 + ) { + return; + } + + var toComplete = localcodedata.trim().endsWith("$") ? values[0].autocomplete : "$" + values[0].autocomplete; + + toComplete = toComplete.toLowerCase().replaceAll(" ", "_"); + for (var key in values) { + if (key == 0 || values[key].autocomplete.length === 0) { + continue; + } + + toComplete += values[key].autocomplete; + } + + setlocalcodedata(localcodedata+toComplete) + setMenuPosition(null) + } + + const handleClick = (item) => { + if (item === undefined || item.value === undefined || item.value === null) { + return + } + + if (!item.value.includes("{%") && !item.value.includes("{{")) { + setlocalcodedata(localcodedata+" | "+item.value+" }}") + } else { + setlocalcodedata(localcodedata+item.value) + } + + setAnchorEl(null) + setAnchorEl2(null) + setAnchorEl3(null) + } + + const executeSingleAction = (inputdata) => { + if (validation === true) { + inputdata = JSON.stringify(inputdata) + } + + // Shuffle Tools 1.2.0 (in most cases?) + const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a" + + const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me" + const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"name":"call", "value": inputdata}] + + const actiondata = {"description":"Repeats the call parameter","id":"","name":actionname,"label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters": params, "execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}} + + setExecutionResult({ + "valid": false, + "result": baseResult, + "errors": [], + }) + + setExecuting(true) + + fetch(`${globalUrl}/api/v1/apps/${appid}/execute`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify(actiondata), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!") + } + + return response.json() + }) + .then((responseJson) => { + //console.log("RESPONSE: ", responseJson) + var newResult = {} + if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) { + const result = responseJson.result.slice(0, 50)+"..." + //toast("SUCCESS: "+result) + + const validate = validateJson(responseJson.result) + newResult = validate + } else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) { + toast(responseJson.reason) + newResult = {"valid": false, "result": responseJson.reason} + } else if (responseJson.success === true) { + newResult = {"valid": false, "result": "Couldn't finish execution. Please fill all the required fields, and retry the execution."} + } else { + newResult = {"valid": false, "result": "Couldn't finish execution (2). Please fill all the required fields, and validate the execution."} + } + + if (responseJson.errors !== undefined && responseJson.errors !== null && responseJson.errors.length > 0) { + newResult.errors = responseJson.errors + } + + setExecutionResult(newResult) + setExecuting(false) + }) + .catch(error => { + //toast("Execution error: "+error.toString()) + console.log("error: ", error) + setExecuting(false) + }) + } + + const adjustPosition = (menuPosition) => { + const { top, left, width, height } = menuPosition; + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + + let adjustedTop = top; + let adjustedLeft = left; + + // Adjust top position if menu overflows the bottom of the window + if (top + height > windowHeight) { + adjustedTop = windowHeight - height; + } + + // Adjust left position if menu overflows the right side of the window + if (left + width > windowWidth) { + adjustedLeft = windowWidth - width; + } + + return { + "top": adjustedTop, + "left": adjustedLeft + } + }; + + + + return ( + { + console.log("In closer") + + if (changeActionParameterCodeMirror !== undefined) { + changeActionParameterCodeMirror({target: {value: ""}}, fieldCount, localcodedata) + } else { + console.log("No action called changeActionParameterCodeMirror in code editor") + } + //setExpansionModalOpen(false) + }} + PaperComponent={PaperComponent} + PaperProps={{ + style: { + zIndex: 12501, + color: "white", + minWidth: isMobile ? "100%" : isFileEditor ? 650 : "80%", + maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100, + minHeight: isMobile ? "100%" : "auto", + maxHeight: isMobile ? "100%" : 700, + border: theme.palette.defaultBorder, + padding: isMobile ? "25px 10px 25px 10px" : 25, + }, + }} + > + + { + }} + > + + + + + { + setExpansionModalOpen(false) + }} + > + + + +
+
+ { isFileEditor ? +
+
+ + File Editor + +
+
+ : +
+
+ {/* + + Code Editor + + */} + { isFileEditor ? null : +
+ {selectedAction.name === "execute_python" ? + + Run Python Code + + : +
+ + { + setAnchorEl(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {liquidFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl2(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {mathFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl3(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {pythonFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + +
+ } + + { + handleMenuClose(); + }} + open={!!menuPosition} + style={{ + color: "white", + marginTop: 2, + maxHeight: 650, + }} + > + {actionlist.map((innerdata) => { + const icon = + innerdata.type === "action" ? ( + + ) : innerdata.type === "workflow_variable" || + innerdata.type === "execution_variable" ? ( + + ) : ( + + ); + + const handleExecArgumentHover = (inside) => { + var exec_text_field = document.getElementById( + "execution_argument_input_field" + ); + if (exec_text_field !== null) { + if (inside) { + exec_text_field.style.border = "2px solid #f85a3e"; + } else { + exec_text_field.style.border = ""; + } + } + }; + + const handleActionHover = (inside, actionId) => { + }; + + const handleMouseover = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(true); + } else if (innerdata.type === "action") { + handleActionHover(true, innerdata.id); + } + }; + + const handleMouseOut = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(false); + } else if (innerdata.type === "action") { + handleActionHover(false, innerdata.id); + } + }; + + var parsedPaths = []; + if (typeof innerdata.example === "object") { + parsedPaths = GetParsedPaths(innerdata.example, ""); + } + + const coverColor = "#82ccc3" + //menuPosition.left -= 50 + //menuPosition.top -= 250 + //console.log("POS: ", menuPosition1) + var menuPosition1 = menuPosition + if (menuPosition1 === null) { + menuPosition1 = { + "left": 0, + "top": 0, + } + } else if (menuPosition1.top === null || menuPosition1.top === undefined) { + menuPosition1.top = 0 + } else if (menuPosition1.left === null || menuPosition1.left === undefined) { + menuPosition1.left = 0 + } + + //console.log("POS1: ", menuPosition1) + + return parsedPaths.length > 0 ? ( + + {icon} {innerdata.name} +
+ } + parentMenuOpen={!!menuPosition} + style={{ + color: "white", + minWidth: 250, + maxWidth: 250, + maxHeight: 50, + overflow: "hidden", + }} + onClick={() => { + console.log("CLICKED: ", innerdata); + console.log(innerdata.example) + handleItemClick([innerdata]); + }} + > + + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + + {innerdata.name} + + + {parsedPaths.map((pathdata, index) => { + // FIXME: Should be recursive in here + // + const icon = + pathdata.type === "value" ? ( + + ) : pathdata.type === "list" ? ( + + ) : ( + + ); + // + + const indentation_count = (pathdata.name.match(/\./g) || []).length+1 + //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 + const boxPadding = 0 + const namesplit = pathdata.name.split(".") + const newname = namesplit[namesplit.length-1] + return ( + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata, pathdata]); + }} + > + +
+ {Array(indentation_count).fill().map((subdata, subindex) => { + return ( +
+ ) + })} + {icon} {newname} + {pathdata.type === "list" ? { + e.preventDefault() + e.stopPropagation() + + console.log("INNER: ", innerdata, pathdata) + + // Removing .list from autocomplete + var newname = pathdata.name + if (newname.length > 5) { + newname = newname.slice(0, newname.length-5) + } + + //selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` + //selectedAction.parameters[count].value = selectedActionParameters[count].value; + //setSelectedAction(selectedAction); + //setShowDropdown(false); + setMenuPosition(null); + + // innerdata.name + // pathdata.name + //handleItemClick([innerdata, newpathdata]) + //console.log("CLICK LENGTH!") + }} /> : null} +
+ + + ); + })} + + + ) : ( + handleMouseover()} + onMouseOut={() => { + handleMouseOut(); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + +
+ {icon} {innerdata.name} +
+
+
+ ); + })} + +
+ } + { + + }} + > + + + + + + + { + autoFormat(localcodedata) + }} + > + + {isAiLoading ? + + : + + } + + +
+
+ } + + {/* +
+ */} + +
+ { + console.log("LOAD: ", editor) + highlight_variables(localcodedata) + }} + onCursorChange={(cursorPosition, editor, value) => { + setCurrentCharacter(cursorPosition.column) + setCurrentLine(cursorPosition.row) + findIndex(cursorPosition.row, cursorPosition.column) + highlight_variables(value) + }} + onChange={(value, editor) => { + // setlocalcodedata(value) + // expectedOutput(value) + // highlight_variables(value,editor) + setlocalcodedata(value) + expectedOutput(value) + highlight_variables(value) + }} + setOptions={{ + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + useWorker: false + }} + // options={options} + /> +
+ +
+
+
+ + {isFileEditor ? null : +
+
+ {isMobile ? null : + +
+ + Expected Output + +
+ +
+ } + +
+ + + + + {isMobile ? null : + validation === true ? + { + //handleReactJsonClipboard(copy); + }} + displayDataTypes={false} + onSelect={(select) => { + var basename = "exec" + if (selectedAction !== undefined && selectedAction !== null && Object.keys(selectedAction).length !== 0) { + basename = selectedAction.label.toLowerCase().replaceAll(" ", "_") + } + + HandleJsonCopy(expOutput, select, basename) + }} + name={"JSON autocompletion"} + /> + : +

+ {expOutput} +

+ } +
+ + {executionResult.valid === true ? + { + //handleReactJsonClipboard(copy); + }} + displayDataTypes={false} + onSelect={(select) => { + //HandleJsonCopy(executionResult.result, select, "exec"); + }} + name={"Test result"} + /> + : + + {executionResult.result.length > 0 ? + + + Test output + + + {executionResult.result} + + + : + +
+ + Output is based on the last VALID run of the node(s) you are referencing. Only updates when you refresh the Workflow Window. + + + No test output yet. + +
+ } + {executionResult.errors !== undefined && executionResult.errors !== null && executionResult.errors.length > 0 ? + + Errors ({executionResult.errors.length}): {executionResult.errors.join("\n")} + + : null} +
+ } +
+ +
+ } +
+ + +
+ + +
+
) +} + +export default CodeEditor; diff --git a/frontend/src/views/HandlePaymentNew.jsx b/frontend/src/views/HandlePaymentNew.jsx index 89546b9b..4be0714c 100644 --- a/frontend/src/views/HandlePaymentNew.jsx +++ b/frontend/src/views/HandlePaymentNew.jsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from 'react'; import ReactGA from 'react-ga4'; import { useNavigate, Link } from "react-router-dom"; import {isMobile} from "react-device-detect"; +import { toast } from "react-toastify" import { Done as DoneIcon, @@ -36,6 +37,24 @@ import Services from "./Services.jsx"; export const typecost = 0.0018 export const typecost_single = (typecost * 1.33).toFixed(4) +export const handlePayasyougo = (userdata) => { + var billingurl = "https://billing.stripe.com/p/login/bIY5lo5bMbWs9Py5kk" + + if (userdata !== undefined && userdata !== null) { + if (userdata.org_email !== undefined && userdata.org_email !== null && userdata.org_email !== "") { + billingurl += `?prefilled_email=${userdata.org_email}` + + } else if (userdata.username !== undefined && userdata.username !== null && userdata.username !== "") { + billingurl += `?prefilled_email=${userdata.username}` + } + } + + toast("Redirecting in 2 seconds. Use the organization owner email.") + setTimeout(() => { + window.location = billingurl + }, 2500) +} + // 1. Create 2-3 payment tiers (slider?) // 2. Create a way to show them anywhere // From e5e4acdf03d768883363f7fdebbd9be38a5049dd Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Fri, 15 Mar 2024 06:55:26 +0000 Subject: [PATCH 048/142] modifed the endopoint --- backend/go-app/main.go | 4 ++-- frontend/src/views/Admin.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 97aede9d..ece4b594 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4923,8 +4923,8 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") - r.HandleFunc("/api/v1/sub_orgs/{orgId}", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS") - + r.HandleFunc("/api/v1/orgs/{orgId}/suborgs", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS") + // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. r.HandleFunc("/api/v1/environments/{key}/stop", shuffle.HandleStopExecutions).Methods("GET", "POST", "OPTIONS") diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 15351d38..159465af 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1120,7 +1120,7 @@ If you're interested, please let me know a time that works for you, or set up a return; } - fetch(`${globalUrl}/api/v1/sub_orgs/${orgId}`, { + fetch(`${globalUrl}/api/v1/orgs/${orgId}/suborgs`, { method: "GET", credentials: "include", headers: { From b8e9599053f17adaed6cbf79d10aebe594a95914 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 15 Mar 2024 14:39:50 +0100 Subject: [PATCH 049/142] Fixed code editor and small ui changes in workflow ui --- frontend/src/components/ParsedAction.jsx | 82 +- .../src/components/ShuffleCodeEditor1.jsx | 22 +- frontend/src/defaultCytoscapeStyle.jsx | 16 +- frontend/src/views/AngularWorkflow.jsx | 809 ++++++++++++------ frontend/src/views/Dashboard.jsx | 2 - 5 files changed, 644 insertions(+), 287 deletions(-) diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index c13fecd9..476fab54 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -117,7 +117,7 @@ const openApiFieldDesc = "Generated by OpenAPI body example"; const ParsedAction = (props) => { const { workflow, - files, + files, setWorkflow, setAction, setSelectedAction, @@ -456,14 +456,33 @@ const ParsedAction = (props) => { if (workflow.execution_variables !== null && workflow.execution_variables !== undefined && workflow.execution_variables.length > 0) { for (let [key,keyval] in Object.entries(workflow.execution_variables)) { - const item = workflow.execution_variables[key]; + const item = workflow.execution_variables[key] + + var exampleoutput = "" + for (let execkey in workflowExecutions) { + const exec = workflowExecutions[execkey] + if (exec["execution_variables"] === undefined || exec["execution_variables"] === null) { + continue + } + + const foundExec = exec.execution_variables.find((exvar) => exvar.name === item.name) + if (!foundExec) { + continue + } + + if (foundExec.value !== undefined && foundExec.value !== null && foundExec.value.length > 0) { + exampleoutput = foundExec.value + break + } + } + actionlist.push({ type: "execution_variable", name: item.name, value: item.value, id: item.id, autocomplete: `${item.name.split(" ").join("_")}`, - example: "", + example: exampleoutput, }); } } @@ -2711,11 +2730,39 @@ const ParsedAction = (props) => { ) } - //const CustomPopper = function (props) { - // const classes = useStyles() - // return - //} - //console.log("env: ", selectedActionEnvironment) + const sortByCategoryLabel = (a, b) => { + const aHasCategoryLabel = a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0 + const bHasCategoryLabel = b.category_label !== undefined && b.category_label !== null && b.category_label.length > 0 + + // Sort by existence and length of "category_label" + if (aHasCategoryLabel && !bHasCategoryLabel) { + return -1 + } else if (!aHasCategoryLabel && bHasCategoryLabel) { + return 1 + } else { + return 0 + } + } + + // Function to deduplicate based on the "name" field + const deduplicateByName = (array) => { + const uniqueNames = {}; + return array.filter(item => { + if (!item.hasOwnProperty('name') || !item.name.length) { + return true + } + if (!uniqueNames[item.name]) { + uniqueNames[item.name] = true + return true + } + return false + }) + } + + // Gets the most important actions first + const renderedActionOptions = deduplicateByName((selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))).sort(sortByCategoryLabel)) + + var baselabel = selectedAction.label; return ( @@ -2743,7 +2790,7 @@ const ParsedAction = (props) => { onClick={() => { if (workflowExecutions.length > 0) { // Look for the ID - const found = false; + var found = false; for (let [key,keyval] in Object.entries(workflowExecutions)) { if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { continue; @@ -2758,7 +2805,6 @@ const ParsedAction = (props) => { } const oldstartnode = cy.getElementById(selectedAction.id); - console.log("FOUND NODe: ", oldstartnode) if (oldstartnode !== undefined && oldstartnode !== null) { const foundname = oldstartnode.data("label") if (foundname !== undefined && foundname !== null) { @@ -2769,10 +2815,16 @@ const ParsedAction = (props) => { setSelectedResult(foundResult); if (setCodeModalOpen !== undefined) { setCodeModalOpen(true); + + found = true } break; } + + if (!found) { + toast("No result for this action yet. Please run the workflow first.") + } } }} > @@ -2804,6 +2856,7 @@ const ParsedAction = (props) => { + {/* { + */} {/* { */} + {/* { + */} { > {autoCompleting ? @@ -3560,6 +3616,8 @@ const ParsedAction = (props) => { return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions"; }} renderGroup={(params) => { + //return null + return (
  • {params.group} @@ -3567,7 +3625,7 @@ const ParsedAction = (props) => {
  • ) }} - options={selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))} + options={renderedActionOptions} ListboxProps={{ style: { backgroundColor: theme.palette.surfaceColor, diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 9bec37ad..b08d0252 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -676,6 +676,8 @@ const CodeEditor = (props) => { for (var i = 0; i < found.length; i++) { try { const fixedVariable = fixVariable(found[i]) + + // Finding if the value is in the list at all, and does initial replacement var valuefound = false for (var j = 0; j < actionlist.length; j++) { if(fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()){ @@ -697,12 +699,13 @@ const CodeEditor = (props) => { } catch (e) { input = input.replace(found[i], actionlist[j].example, -1) } + + } + + //console.log("INPUT: ", fixedVariable, valuefound, input) + if (!valuefound) { + //console.log("Couldn't find value "+fixedVariable) } - - - //if (!valuefound) { - // console.log("Couldn't find value "+fixedVariable) - //} if (!valuefound && availableVariables.includes(fixedVariable)) { var shouldbreak = false @@ -729,8 +732,6 @@ const CodeEditor = (props) => { console.log("ERR IN INPUT: ", e) } - //console.log("Got output for: ", fullpath, new_input, actionlist[k].example, typeof new_input) - if (typeof new_input === "object") { new_input = JSON.stringify(new_input) } else { @@ -752,10 +753,6 @@ const CodeEditor = (props) => { input = input.replace(fixedVariable, new_input, -1) input = input.replace(found[i], new_input, -1) - //} catch (e) { - // input = input.replace(found[i], actionlist[k].example) - //} - shouldbreak = true break } @@ -1498,7 +1495,6 @@ const CodeEditor = (props) => { backgroundColor: "rgba(40,40,40,1)", }} onLoad={(editor) => { - console.log("LOAD: ", editor) highlight_variables(localcodedata) }} onCursorChange={(cursorPosition, editor, value) => { @@ -1591,7 +1587,7 @@ const CodeEditor = (props) => { minheight: 450, overflow: "auto", minWidth: 450, - maxWidth: 450, + maxWidth: "100%", }} collapsed={false} enableClipboard={(copy) => { diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index 7e7132fd..ca66ef3c 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -34,6 +34,20 @@ const data = [ "z-index": 5001, }, }, + { + selector: `node[buttonType="ACTIONSUGGESTION"]`, + css: { + label: "data(label)", + shape: "roundrectangle", + "height": "16px", + "width": "120px", + "background-color": "#212121", + "border-color": "#81c784", + "z-index": 10000, + "border-radius": "10px", + "text-margin-x": "0px", + }, + }, { selector: `node[type="ACTION"]`, css: { @@ -120,7 +134,7 @@ const data = [ { selector: `node[type="TRIGGER"]`, css: { - shape: "octagon", + shape: "round-octagon", "border-radius": "5px", "border-color": "orange", "background-color": "#213243", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index fb007b0b..1b06ed53 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -6,6 +6,7 @@ import theme from "../theme.jsx"; import { useInterval } from "react-powerhooks"; 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"; @@ -25,6 +26,7 @@ import algoliasearch from 'algoliasearch/lite'; import { Zoom, Fade, + Slide, Avatar, Popover, @@ -535,6 +537,8 @@ const AngularWorkflow = (defaultprops) => { const [workflowExecutionCount, setWorkflowExecutionCount] = React.useState(0); const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0); const [workflowRecommendations, setWorkflowRecommendations] = React.useState(undefined); + const [showErrors, setShowErrors] = React.useState(true); + const [highlightedApp, setHighlightedApp] = React.useState("") const [listCache, setListCache] = React.useState([]); const [suggestionBox, setSuggestionBox] = React.useState({ @@ -557,6 +561,59 @@ const AngularWorkflow = (defaultprops) => { "field_id": "", }) + // Event for making sure app is correct + useEffect(() => { + if (selectedApp === undefined || selectedApp === null && selectedApp.app_name === undefined) { + return + } + + if (apps === undefined || apps === null || apps.length === 0) { + return + } + + // Handle the activation case, as they are NOT in the event management system yet + if (selectedApp.actions === undefined || selectedApp.actions === null || selectedApp.actions.length > 1) { + return + } + + console.log("Checking to update app with useeffect.") + + for (let appkey in apps) { + const curapp = apps[appkey] + if (curapp.name !== selectedApp.name) { + 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) { + const curaction = curapp.actions[actionkey] + + // First action with a label, as they are most used (typically) + if (curaction.category_label !== undefined && curaction.category_label !== null && curaction.category_label.length > 0) { + foundActionIndex = actionkey + break + } + } + + if (foundActionIndex >= 0) { + var newaction = curapp.actions[foundActionIndex] + + setNewSelectedAction({ + "target": { + "value": newaction.name + }, + }) + } + + setSelectedApp(curapp) + } + break + } + + }, [selectedApp]) + const [executionArgumentModalOpen, setExecutionArgumentModalOpen] = React.useState(false); // This should all be set once, not on every iteration @@ -569,32 +626,6 @@ const AngularWorkflow = (defaultprops) => { : false; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - useEffect(() => { - return () => { - console.log("UNMOUNTING USER!") - sendStreamRequest({ - "item": "workflow", - "type": "leave", - "id": workflow.id, - }) - } - }, []) - /* - useEffect(() => { - console.log("In useeffect for workflow: ", workflow) - if (cy === undefined || cy === null) { - return - } - - if (workflow === undefined || workflow === null || workflow.actions === undefined || workflow.actions === null) { - return - } - - // Check if actions of original vs new are changed - fetchRecommendations(workflow) - }, [workflow]) - */ - const appBarSize = isCloud ? 75 : 72; const triggerEnvironments = isCloud ? ["cloud"] : ["onprem", "cloud"]; const unloadText = "Are you sure you want to leave without saving (CTRL+S)?"; @@ -2084,7 +2115,7 @@ const AngularWorkflow = (defaultprops) => { "large_image": "", }] - setAppsLoaded(true) + setAppsLoaded(true) setApps(pretend_apps) setFilteredApps(pretend_apps) setPrioritizedApps(pretend_apps); @@ -2096,10 +2127,10 @@ const AngularWorkflow = (defaultprops) => { } // Used for e.g. Liquid testing - const foundTools = responseJson.find((app) => app.name === "Shuffle Tools") - if (foundTools !== undefined && foundTools !== null) { - setToolsApp(foundTools) - } + const foundTools = responseJson.find((app) => app.name === "Shuffle Tools") + if (foundTools !== undefined && foundTools !== null) { + setToolsApp(foundTools) + } setApps(responseJson); @@ -2108,23 +2139,24 @@ const AngularWorkflow = (defaultprops) => { setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name.toLowerCase()))); } else { - //setFilteredApps( - // responseJson.filter( - // (app) => - // !internalIds.includes(app.name) && - // !(!app.activated && app.generated) - // ) - //); - var tmpFiltered = responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase())) - //tmpFiltered = sortByKey(tmpFiltered, "activated") setFilteredApps(tmpFiltered) - - //!(!app.activated && app.generated) setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name.toLowerCase()))); } - setAppsLoaded(true) + setAppsLoaded(true) + + // Remove all cytoscape triggers first? + if (cy !== undefined && cy !== null) { + cy.removeListener("select"); + } + + // Re-adding cytoscape triggers + if (cy !== undefined && cy !== null) { + cy.on("select", "node", (e) => { + onNodeSelect(e, appAuthentication); + }); + } }) .catch((error) => { console.log("App loading error: " + error.toString()); @@ -3257,28 +3289,28 @@ const AngularWorkflow = (defaultprops) => { return; } - const connected = event.target.connectedEdges().jsons() + const connected = event.target.connectedEdges().jsons() if (connected.length > 0 && connected !== undefined) { - for (let connectkey in connected) { - const edge = connected[connectkey] - //console.log("EDGE:", edge) + for (let connectkey in connected) { + const edge = connected[connectkey] + //console.log("EDGE:", edge) - //const edge = edgeBase.json() + //const edge = edgeBase.json() - const sourcenode = cy.getElementById(edge.data.source) - const destinationnode = cy.getElementById(edge.data.target) - if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) { - continue - } + const sourcenode = cy.getElementById(edge.data.source) + const destinationnode = cy.getElementById(edge.data.target) + if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) { + continue + } - const edgeCurve = calculateEdgeCurve(sourcenode.position(), destinationnode.position()) - const currentedge = cy.getElementById(edge.data.id) - if (currentedge !== undefined && currentedge !== null) { - currentedge.style('control-point-distance', edgeCurve.distance) - currentedge.style('control-point-weight', edgeCurve.weight) - } + const edgeCurve = calculateEdgeCurve(sourcenode.position(), destinationnode.position()) + const currentedge = cy.getElementById(edge.data.id) + if (currentedge !== undefined && currentedge !== null) { + currentedge.style('control-point-distance', edgeCurve.distance) + currentedge.style('control-point-weight', edgeCurve.weight) } } + } if (styledElements.length === 1) { console.log( @@ -3366,7 +3398,7 @@ const AngularWorkflow = (defaultprops) => { if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") { console.log("Found triggers. Add!") - if (!found) { + if (!found) { console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions) // Find how many executions it has var executions = 0 @@ -3586,7 +3618,70 @@ const AngularWorkflow = (defaultprops) => { //const data = JSON.parse(JSON.stringify(event.target.data())) const data = event.target.data() - if (data.isSuggestion === true) { + + if (data.buttonType == "ACTIONSUGGESTION") { + const attachedToId = data.attachedTo + + const parentitem = cy.getElementById(data.attachedTo).data() + if (parentitem !== null && parentitem !== undefined) { + + const findaction = data.label + console.log("CLICKED: ", findaction, apps.length) + + for (let appkey in apps) { + const curapp = apps[appkey] + if (curapp.name !== parentitem.app_name) { + continue + } + + if (curapp.actions === undefined || curapp.actions === null) { + continue + } + + for (let actionkey in curapp.actions) { + const curaction = curapp.actions[actionkey] + + if (curaction.category_label !== undefined && curaction.category_label !== null && curaction.category_label.length > 0) { + if (curaction.category_label[0].toLowerCase() === findaction.toLowerCase()) { + console.log("FOUND: ", curaction) + + // Update the action itself + // Find the action index, and update: + // - label + // - description + // - parameters + // - name + + var foundindex = -1 + for (let wfactionkey in workflow.actions) { + const wfaction = workflow.actions[wfactionkey] + if (wfaction.id === data.attachedTo) { + foundindex = wfactionkey + break + } + } + + console.log("Updating action: ", foundindex, findaction) + if (foundindex >= 0) { + workflow.actions[foundindex].label = findaction + workflow.actions[foundindex].description = curaction.description + workflow.actions[foundindex].parameters = curaction.parameters + workflow.actions[foundindex].name = curaction.name + + setWorkflow(workflow) + } + break + } + } + } + + break + } + + return + } + + } else if (data.isSuggestion === true) { console.log("Suggestion! Replace with a real action.") const attachedToId = data.attachedTo @@ -4115,7 +4210,6 @@ const AngularWorkflow = (defaultprops) => { //} //curaction.parameters = newparams - console.log("ACTION CLICK: ", curaction) setSelectedApp(curapp); setSelectedAction(curaction); @@ -4268,8 +4362,10 @@ const AngularWorkflow = (defaultprops) => { toast("Failed to auto-activate the app. Go to /apps and activate it.") } else { if (refresh === true) { + setHighlightedApp(appid) //toast("App activated for your organization! Refresh the page to use the app.") getApps() + } } }) @@ -5392,7 +5488,6 @@ const AngularWorkflow = (defaultprops) => { } if (nodedata.finished === false) { - console.log("NODE UNFINISHED HOVEROUT: ", nodedata) // Should just be 1, so this should be fast enough :3 const incomingEdges = event.target.incomers("edge").jsons() @@ -5567,57 +5662,136 @@ const AngularWorkflow = (defaultprops) => { }); }; + const addActionSuggestions = (nodedata, event) => { + console.log("App Action suggestions disabled for now") + return + + if (nodedata.type !== "ACTION") { + 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; + + const parentlabel = parentNode.data("label").toLowerCase().replace(" ", "_") + const parentname = parentNode.data("app_name").toLowerCase().replace(" ", "_") + if (!parentlabel.startsWith(parentname)) { + console.log("Bad startname to start with: ", parentname, parentlabel) + return + } + + const iconInfo = { + icon: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z", + iconColor: buttonColor, + iconBackgroundColor: buttonBackgroundColor, + }; + + const svg_pin = ``; + const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); + + // 1. Find the app + // 2. Loop the apps' actions + // 3. Find actions based on category label IF it exists + var added = 0 + for (let appKey in apps) { + const curapp = apps[appKey] + if (curapp.name.toLowerCase().replace(" ", "_") !== parentname) { + continue + } + + if (curapp.actions === undefined || curapp.actions === null || curapp.actions.length === 0) { + continue + } + + for (let actionKey in curapp.actions) { + const curaction = curapp.actions[actionKey] + + // Check if this is the current action already + if (parentNode.data("name") == curaction.name) { + continue + } + + if (curaction.category_label !== undefined && curaction.category_label !== null && curaction.category_label.length > 0) { + + cy.add({ + group: "nodes", + data: { + weight: 30, + id: uuidv4(), + label: curaction.category_label[0], + attachedTo: event.target.data("id"), + is_valid: true, + buttonType: "ACTIONSUGGESTION", + }, + position: { + x: px, + y: py + (added * 50), + }, + }) + + added += 1 + if (added >= 2) { + break + } + } + } + + break + } + } + const addSuggestionButtons = (nodedata, event) => { //console.log("Skipping Adding suggestion buttons") //return - // Skipping add for now. Should Re-enable + // Skipping add for now. Should Re-enable - // Add a button for autocompletion based on input - if (nodedata.type === "ACTION") { - /* - const color = "#34a853" + // Add a button for autocompletion based on input + if (nodedata.type === "ACTION") { + /* + const color = "#34a853" - // Fix icon - const iconInfo = { - icon: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.9959.9959 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41l-2.33-2.35zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12-2.44 2.44z", - iconColor: buttonColor, - iconBackgroundColor: buttonBackgroundColor, - }; + // Fix icon + const iconInfo = { + icon: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.9959.9959 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41l-2.33-2.35zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12-2.44 2.44z", + iconColor: buttonColor, + iconBackgroundColor: buttonBackgroundColor, + }; - const svg_pin = ``; - const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); + const svg_pin = ``; + const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); - const decoratorNode = { - position: { - x: event.target.position().x + 0, - y: event.target.position().y + 65, - }, - locked: true, - data: { - isButton: true, - isValid: true, - is_valid: true, - //label: "+", - attachedTo: nodedata.id, - imageColor: color, - buttonType: "suggestion", - icon: svgpin_Url, - iconBackground: iconInfo.iconBackgroundColor, - }, - }; + const decoratorNode = { + position: { + x: event.target.position().x + 0, + y: event.target.position().y + 65, + }, + locked: true, + data: { + isButton: true, + isValid: true, + is_valid: true, + //label: "+", + attachedTo: nodedata.id, + imageColor: color, + buttonType: "suggestion", + icon: svgpin_Url, + iconBackground: iconInfo.iconBackgroundColor, + }, + }; - cy.add(decoratorNode); - */ - } + cy.add(decoratorNode); + */ + } - console.log("RECS: ", workflowRecommendations) + if (workflowRecommendations === undefined || workflowRecommendations === null || workflowRecommendations.length === 0) { + return + } - if (workflowRecommendations === undefined || workflowRecommendations === null || workflowRecommendations.length === 0) { - 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") + 200; @@ -5816,6 +5990,7 @@ const AngularWorkflow = (defaultprops) => { cytoscapeElement.style.cursor = "pointer" } + sendStreamRequest({ "item": "node", "type": "hover", @@ -5936,6 +6111,8 @@ const AngularWorkflow = (defaultprops) => { // autocomplete // right click // suggestions + addActionSuggestions(nodedata, event); + if (workflow.actions.length < 4) { addSuggestionButtons(nodedata, event); } else { @@ -5944,6 +6121,7 @@ const AngularWorkflow = (defaultprops) => { } } + var parsedStyle = { "border-width": "7px", "border-opacity": ".7", @@ -6007,7 +6185,7 @@ const AngularWorkflow = (defaultprops) => { if (incomingEdges.length > 0) { outgoingEdges.addClass("hover-highlight"); } - }; + } const onEdgeHoverOut = (event) => { if (event === null || event === undefined || event.target === null || event.target === undefined) { @@ -6650,8 +6828,6 @@ const AngularWorkflow = (defaultprops) => { } const getRevisionHistory = (workflow_id) => { - console.log("Loading revisions for workflow ID ", workflow_id) - fetch(`${globalUrl}/api/v1/workflows/${workflow_id}/revisions`, { method: "GET", headers: { @@ -6816,7 +6992,6 @@ const AngularWorkflow = (defaultprops) => { cy.on("boxstart", (e) => { console.log("START"); - //cy.removeListener("select"); }); cy.on("boxend", (e) => { @@ -7940,10 +8115,17 @@ const AngularWorkflow = (defaultprops) => { ? `${pixelSize} solid ${green}` : `${pixelSize} solid ${yellow}`; + if (app.id == highlightedApp) { + console.log("Found correct appid to highlight: ", app.id) + + newAppStyle.border = "3px solid " + green + } + if (!app.activated && app.generated) { newAppStyle.borderLeft = `${pixelSize} solid ${yellow}`; } + return ( { @@ -8235,146 +8417,162 @@ const AngularWorkflow = (defaultprops) => { hits = hits.slice(0, 4) } + + const clickedApp = (hit) => { + toast(`Activating App. Please wait a moment.`) + + const queryID = hit.__queryID + + + if (queryID !== undefined && queryID !== null) { + aa('init', { + appId: "JNSS5CFDZZ", + apiKey: "db08e40265e2941b9a7d8f644b6e5240", + }) + + const timestamp = new Date().getTime() + aa('sendEvents', [ + { + eventType: 'conversion', + eventName: 'Public App Activated', + index: 'appsearch', + objectIDs: [hit.objectID], + timestamp: timestamp, + queryID: queryID, + userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id, + } + ]) + } else { + console.log("No query to handle when activating") + } + + activateApp(hit.objectID, true) + } + var type = "app" const baseImage = - return ( -
    - - {hits.length === 0 ? - - console.log(hits)}> - - - - - - - : - hits.map((hit, index) => { - const innerlistitemStyle = { - width: positionInfo.width + 35, - overflowX: "hidden", - overflowY: "hidden", - borderBottom: "1px solid rgba(255,255,255,0.4)", - backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit", - cursor: "pointer", - marginLeft: 0, - marginRight: 0, - maxHeight: 75, - minHeight: 75, - maxWidth: 420, - minWidth: "100%", - } +
    + + {hits.length === 0 ? + + console.log(hits)}> + + + + + + + : + hits.map((hit, index) => { + const innerlistitemStyle = { + width: positionInfo.width + 35, + overflowX: "hidden", + overflowY: "hidden", + borderBottom: "1px solid rgba(255,255,255,0.4)", + backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit", + cursor: "pointer", + marginLeft: 0, + marginRight: 0, + maxHeight: 75, + minHeight: 75, + maxWidth: 420, + minWidth: "100%", + } - const name = hit.name === undefined ? - hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title : - (hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ") + const name = hit.name === undefined ? + hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title : + (hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ") - var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : "" - const avatar = hit.image_url === undefined ? - baseImage - : - + var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : "" + const avatar = hit.image_url === undefined ? + baseImage + : + - //console.log(hit) - if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) { - secondaryText = hit.categories.slice(0, 3).map((data, index) => { - if (index === 0) { - return data - } + //console.log(hit) + if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) { + secondaryText = hit.categories.slice(0, 3).map((data, index) => { + if (index === 0) { + return data + } - return ", " + data + return ", " + data - /* - { - //handleChipClick - }} - variant="outlined" - color="primary" - /> - */ - }) - } + /* + { + //handleChipClick + }} + variant="outlined" + color="primary" + /> + */ + }) + } - var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}` - parsedUrl += `?queryID=${hit.__queryID}` + var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}` + parsedUrl += `?queryID=${hit.__queryID}` - return ( -
    { - //if (!isCloud) { - // toast("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.") - // setTimeout(() => { - // event.preventDefault() - // window.open(parsedUrl, '_blank') - // }, 2000) - //} else { - toast(`Activating ${name}`) - //} + var appdragged = false + return ( + { + e.preventDefault() + e.stopPropagation() - console.log("CLICK: ", hit) + if (!appdragged) { + clickedApp(hit) + } + + appdragged = true + }} + onStop={(e) => { + }} + dragging={false} + position={{ + x: 0, + y: 0, + }} + > +
    { + clickedApp(hit) - const queryID = hit.__queryID - console.log("QUERY: ", queryID) - - if (queryID !== undefined && queryID !== null) { - aa('init', { - appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", - }) - - const timestamp = new Date().getTime() - aa('sendEvents', [ - { - eventType: 'conversion', - eventName: 'Public App Activated', - index: 'appsearch', - objectIDs: [hit.objectID], - timestamp: timestamp, - queryID: queryID, - userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id, - } - ]) - } else { - console.log("No query to handle when activating") - } - - activateApp(hit.objectID, true) - }}> - { - setMouseHoverIndex(index) - }}> - - {avatar} - - - {/* - - - - - - */} - -
    - ) - }) - } - -
    + }}> + { + setMouseHoverIndex(index) + }}> + + {avatar} + + + {/* + + + + + + */} + +
    + + ) + }) + } +
    +
    ) } @@ -8449,7 +8647,7 @@ const AngularWorkflow = (defaultprops) => { ) })} - {visibleApps.length <= 4 ? ( + {visibleApps.length <= 2 ?
    { @@ -8467,7 +8665,7 @@ const AngularWorkflow = (defaultprops) => {
    - ) : + :
    } @@ -8542,6 +8740,8 @@ const AngularWorkflow = (defaultprops) => { return } + console.log("action input: ", e) + const newaction = selectedApp.actions.find( (a) => a.name === e.target.value ); @@ -8570,8 +8770,6 @@ const AngularWorkflow = (defaultprops) => { newSelectedAction.is_valid = true; newSelectedAction.required_body_fields = newaction.required_body_fields - console.log("New selected action: ", newSelectedAction) - // Simple action swap autocompleter if (oldaction.parameters !== undefined && oldaction.parameters !== null && newSelectedAction.parameters !== undefined && oldaction.id === newSelectedAction.id) { var fileid_found = false @@ -8661,7 +8859,11 @@ const AngularWorkflow = (defaultprops) => { // Further checks if those fields are already set in a previously used action newSelectedAction = RunAutocompleter(newSelectedAction); + console.log("newaction: ", newaction) + if ( + newaction.return !== undefined && + newaction.return !== null && newaction.returns.example !== undefined && newaction.returns.example !== null && newaction.returns.example.length > 0 @@ -12161,12 +12363,6 @@ const AngularWorkflow = (defaultprops) => { selectedTrigger.app_association = parsedvalue setUpdate(Math.random()); } - // setNewSelectedAction({ - // target: { - // value: newValue.name - // } - // }); - //} }} renderOption={(props, app, state) => { var appname = app.name.replaceAll("_", " ") @@ -14068,7 +14264,7 @@ const AngularWorkflow = (defaultprops) => { ) } - const showErrors = !isMobile && !workflow.public && workflow.errors !== undefined && workflow.errors !== null && workflow.errors.length > 0 ? + const shownErrors = !isMobile && !workflow.public && workflow.errors !== undefined && workflow.errors !== null && workflow.errors.length > 0 && showErrors ?
    { borderRadius: theme.palette.borderRadius, }} > + + + { + e.preventDefault(); + + // A temporary hider thing + setShowErrors(false) + }} + > + + + + {/**/} @@ -16653,30 +16867,48 @@ const AngularWorkflow = (defaultprops) => { return "" } + // Validate and check for newlines if (result.success !== false) { + + var stringjson = result + const valid = validateJson(stringjson, true) + if (valid.valid === false) { + if (stringjson.startsWith("{") && stringjson.endsWith("}")) { + // Look for newline + if (stringjson.includes("\n") && !stringjson.includes("\\n")) { + return "Looks like you have a newline problem. Consider using the | replace: '\\n', '\\\\n' }} filter in Liquid." + } else { + return "The result looks like it should be JSON, but is invalid. Look for potential" + } + } + } + return "" } - var stringjson = result try { stringjson = JSON.stringify(result) } catch (e) { } - console.log("JSON: ", stringjson) stringjson = stringjson.toLowerCase() if (stringjson.includes("localhost")) { return "You can't use localhost in apps. Use the external ip or url of the server instead" } if (stringjson.includes("connectionerror")) { - return "Your URL is most likely incorrect." + if (stringjson.includes("kms")) { + return "KMS authentication failed. Check your notifications for more details." + } + + return "Your URL is incorrect." } if (stringjson.includes("result too large to handle")) { return "Execution loading failed. Reload the execution by closing it and clicking it again" } + return "" } @@ -16940,21 +17172,21 @@ const AngularWorkflow = (defaultprops) => { ) : (
    Result -
    +
    { - console.log("IN HERE TO CLICK"); to_be_copied = selectedResult.result; var copyText = document.getElementById( "copy_element_shuffle" ); - console.log("PRECOPY: ", to_be_copied); + if (copyText !== null && copyText !== undefined) { - console.log("COPY: ", copyText); - console.log("NAVIGATOR: ", navigator); const clipboard = navigator.clipboard; if (clipboard === undefined) { toast("Can only copy over HTTPS (port 3443)"); @@ -17139,7 +17371,7 @@ const AngularWorkflow = (defaultprops) => { {showWorkflowRevisions ? null : {/**/} - {showErrors} + {shownErrors} @@ -18813,6 +19045,63 @@ const AngularWorkflow = (defaultprops) => { //setUpdate(Math.random()) } + /* + var foundusecase = {} + if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0 && userdata !== undefined && userdata !== null && userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0) { + for (let priokey in userdata.priorities) { + const prio = userdata.priorities[priokey] + if (prio.type !== "usecase") { + continue + } + + const descsplit = prio.description.split("&") + var srcapp = "" + var dstapp = "" + if (descsplit.length > 0) { + srcapp = descsplit[0].toLowerCase().replaceAll(" ", "_") + + if (descsplit.length > 2) { + dstapp = descsplit[2].toLowerCase().replaceAll(" ", "_") + } + } + + if (srcapp.length > 0 && dstapp.length > 0) { + for (let actionkey in workflow.actions) { + const curaction = workflow.actions[actionkey] + const appname = curaction.app_name.toLowerCase().replaceAll(" ", "_") + if (appname === srcapp || appname === dstapp) { + foundusecase = prio + break + } + } + } + + if (foundusecase.name !== undefined && foundusecase.name !== null && foundusecase.name !== "") { + break + } + } + } + + const foundusecase.name === undefined || foundusecase.name === null || foundusecase.name === "" ? null : + +
    + +
    +
    + */ + const loadedCheck = isLoaded && workflowDone ? (
    @@ -18827,6 +19116,8 @@ const AngularWorkflow = (defaultprops) => { {/*editWorkflowModal*/} {executionArgumentModal} {configureWorkflowModal} + {/*usecaseSlidein*/} + {codeEditorModalOpen ? diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index e9a5c201..b79fbf06 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -903,7 +903,6 @@ const UsecaseListComponent = (props) => { isLoggedIn={isLoggedIn} appFramework={frameworkData} userdata={userdata} - globalUrl={globalUrl} img1={inputUsecase.srcimg} srcapp={inputUsecase.srcapp} @@ -911,7 +910,6 @@ const UsecaseListComponent = (props) => { dstapp={inputUsecase.dstapp} title={inputUsecase.name} description={inputUsecase.description} - apps={apps} getAppFramework={getFramework} //appSetupDone={appSetupDone} From c36ed18a0486b769065e43dbcfb8e462e6ac8537 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Mar 2024 00:50:35 +0100 Subject: [PATCH 050/142] Added images for environment page --- frontend/public/icons/docker.svg | 5 ++ frontend/public/icons/k8s.svg | 84 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 frontend/public/icons/docker.svg create mode 100644 frontend/public/icons/k8s.svg diff --git a/frontend/public/icons/docker.svg b/frontend/public/icons/docker.svg new file mode 100644 index 00000000..297bb83f --- /dev/null +++ b/frontend/public/icons/docker.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/public/icons/k8s.svg b/frontend/public/icons/k8s.svg new file mode 100644 index 00000000..bedd3b88 --- /dev/null +++ b/frontend/public/icons/k8s.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + From 833a8d53e06ca1063e7b6b453b06735ce22f1496 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Mar 2024 02:48:53 +0100 Subject: [PATCH 051/142] Fixed relevant stats & license trackers to make it easier to use --- backend/go-app/main.go | 2 + backend/go-app/walkoff.go | 29 ++- frontend/src/components/Billing.jsx | 26 ++- frontend/src/components/BillingStats.jsx | 6 +- frontend/src/components/EditWorkflow.jsx | 255 +++++++++++++--------- frontend/src/components/NewHeader.jsx | 37 +++- frontend/src/components/ParsedAction.jsx | 1 - frontend/src/views/Admin.jsx | 263 ++++++++++++++--------- frontend/src/views/AngularWorkflow.jsx | 10 + frontend/src/views/RunWorkflow.jsx | 126 ++++++++--- functions/onprem/orborus/go.mod | 2 +- functions/onprem/orborus/go.sum | 2 + functions/onprem/orborus/orborus.go | 27 +-- 13 files changed, 513 insertions(+), 273 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index efc61caf..ccba422d 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -969,6 +969,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { tutorialsFinished = append(tutorialsFinished, tutorial) } + licensed := shuffle.IsLicensed(ctx, *currentOrg) returnValue := shuffle.HandleInfo{ Success: true, @@ -989,6 +990,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { Tutorials: tutorialsFinished, Priorities: orgPriorities, + Licensed: licensed, } returnData, err := json.Marshal(returnValue) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 40f9ba3a..634e3acf 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -293,12 +293,35 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { env, err := shuffle.GetEnvironment(ctx, orgId, "") timeNow := time.Now().Unix() if err == nil && len(env.Id) > 0 && len(env.Name) > 0 { + // Updates every 60 seconds~ if time.Now().Unix() > env.Edited+60 { - env.RunningIp = request.RemoteAddr + env.RunningIp = shuffle.GetRequestIp(request) + if len(orborusLabel) > 0 { + env.RunningIp = orborusLabel + } + + if request.Method == "POST" { + body, err := ioutil.ReadAll(request.Body) + if err == nil { + var envData shuffle.OrborusStats + err = json.Unmarshal(body, &envData) + if err == nil { + if envData.Swarm { + env.Licensed = true + env.RunType = "docker" + } + + if envData.Kubernetes { + env.RunType = "k8s" + } + } + } + } + env.Checkin = timeNow - err = shuffle.SetEnvironment(ctx, env) + err = shuffle.SetEnvironment(ctx, &env) if err != nil { - log.Printf("[WARNING] Failed updating environment: %s", err) + log.Printf("[ERROR] Failed updating environment: %s", err) } } } diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 817e6295..c3687489 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -120,10 +120,11 @@ const Billing = (props) => { minHeight: 280, maxWidth: 400, width: "100%", - backgroundColor: theme.palette.surfaceColor, - borderRadius: theme.palette.borderRadius, + backgroundColor: theme.palette.platformColor, + borderRadius: theme.palette.borderRadius*2, border: "1px solid rgba(255,255,255,0.3)", marginRight: 10, + marginTop: 15, } const isCloud = @@ -273,6 +274,7 @@ const Billing = (props) => { const [signatureOpen, setSignatureOpen] = React.useState(false); const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed) + const [hovered, setHovered] = React.useState(false) var top_text = "Base Cloud Access" if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) { @@ -321,8 +323,16 @@ const Billing = (props) => { newPaperstyle.border = "1px solid #f85a3e" } + if (hovered) { + newPaperstyle.backgroundColor = theme.palette.surfaceColor + } + return ( - + setHovered(true)} + onMouseLeave={() => setHovered(false)} + > { const isChildOrg = userdata.active_org.creator_org !== "" && userdata.active_org.creator_org !== undefined && userdata.active_org.creator_org !== null return ( -
    +
    {addDealModal} - Billing + Billing & Licensing {isCloud ? - "We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below." + "Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below." : - "Shuffle is an Open Source automation platform, and no license is required to use it. You may however activate Cloud Sync, get our Scale license, get help with Kubernetes, or talk to Shuffle's Support team to get automation help." + "Shuffle is an Open Source automation platform, and no license is required. We do however offer a Scale license with HA guarantees, along with support hours. By buying a license on https://shuffler.io, you can get access to the license immediately, and if Cloud Syncronisation is enabled, the UI in your local instance will also update." } @@ -1315,7 +1325,7 @@ const Billing = (props) => {
    ) : null*/} -
    +
    { const data = (
    - + All shown statistics are gathered from Your Organization Statistics - This is a feature to help give you more insight into Shuffle, and to understand your utilization of the Shuffle platform. The billing tracker is in Beta, and is always calculated manually before being invoiced. + >Your Organisation Statistics. + It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. The billing tracker is in Beta, and is always calculated manually before being invoiced.
    diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 07e21578..298c0511 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -57,6 +57,7 @@ import { Publish as PublishIcon, OpenInNew as OpenInNewIcon, Add as AddIcon, + Remove as RemoveIcon, } from "@mui/icons-material"; const EditWorkflow = (props) => { @@ -76,7 +77,8 @@ const EditWorkflow = (props) => { const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "") const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day')) - const [inputFields, setInputFields] = React.useState([]) + console.log("WORKFLOW: ", workflow) + const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : []) const classes = useStyles(); @@ -232,7 +234,91 @@ const EditWorkflow = (props) => {
    - +
    + {/* + + */} + +
    + +
    { @@ -477,41 +563,21 @@ const EditWorkflow = (props) => { fullWidth /> - {/* - + Input fields - + Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the workflow run page. - - - {inputFields.length === 0 ? - - : null} - {inputFields.map((data, index) => { + {inputQuestions.map((data, index) => { console.log("Inputfield: ", data) return (
    { marginRight: 5, }} fullWidth={true} - placeholder="Name" + placeholder="Question" + id="standard-required" + margin="normal" + variant="outlined" + defaultValue={data.name} + onChange={(e) => { + inputQuestions[index].name = e.target.value + setInputQuestions(inputQuestions) + setUpdate(Math.random()); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + minHeight: 50, + }, + }} + /> + { - inputFields[index].name = e.target.value - setInputFields(inputFields) + inputQuestions[index].value = e.target.value + setInputQuestions(inputQuestions) setUpdate(Math.random()); }} InputProps={{ @@ -544,28 +641,44 @@ const EditWorkflow = (props) => {
    ) })} - */} + + : null} { setShowMoreClicked(!showMoreClicked); }} @@ -577,71 +690,7 @@ const EditWorkflow = (props) => { - - - - + {newWorkflow === true ? diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index c0cf173d..4f6ecbdd 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -70,6 +70,7 @@ const Header = (props) => { const [anchorEl, setAnchorEl] = React.useState(null); const [anchorElAvatar, setAnchorElAvatar] = React.useState(null); const [subAnchorEl, setSubAnchorEl] = React.useState(null); + const [upgradeHovered, setUpgradeHovered] = React.useState(false); let navigate = useNavigate(); const handleClick = (event) => { @@ -1097,10 +1098,10 @@ const Header = (props) => { )} {/* Show on cloud, if not suborg and if not customer/pov/internal */} - {isCloud && - (userdata.org_status === undefined || - userdata.org_status === null || - userdata.org_status.length === 0) ? ( + { + userdata.licensed !== undefined && + userdata.licensed !== null && + userdata.licensed === false ? { marginTop: 0, }} > - + - ) : null} + : null} {userdata === undefined || userdata.app_execution_limit === undefined || @@ -1145,7 +1165,6 @@ const Header = (props) => { textAlign: "center", cursor: "pointer", borderRadius: theme.palette.borderRadius, - marginRight: 10, marginTop: 5, backgroundColor: theme.palette.surfaceColor, minWidth: 60, @@ -1154,7 +1173,7 @@ const Header = (props) => { userdata.app_execution_usage / userdata.app_execution_limit >= 0.9 - ? "#f86a3e" + ? "2px solid #f86a3e" : null, }} onClick={() => { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 476fab54..016670c2 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -3258,7 +3258,6 @@ const ParsedAction = (props) => { } } - console.log("DID NAME REPLACE ACTUALLY WORK? - may be missing it in certain triggers"); setWorkflow(workflow); setUpdate(Math.random()); baselabel = name diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index e4559e69..4890918f 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -70,6 +70,9 @@ import { Business as BusinessIcon, Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon, + Cancel as CancelIcon, + Dns as DnsIcon, + Help as HelpIcon, Flag as FlagIcon, FmdGood as FmdGoodIcon, @@ -193,9 +196,26 @@ const Admin = (props) => { const [, forceUpdate] = React.useState(); useEffect(() => { - getUsers() + getUsers() + + setTimeout(() => { + if (adminTab === 3) { + window.scroll({ + top: 450, + left: 0, + behavior: 'smooth' + }) + } + }, 1500) }, []); + useEffect(() => { + window.scroll({ + top: 450, + left: 0, + behavior: 'smooth' + }) + }, [adminTab]); useEffect(() => { if (isDropzone) { @@ -970,6 +990,8 @@ If you're interested, please let me know a time that works for you, or set up a .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { toast("Failed to deactivate user: " + responseJson.reason); + } else if (responseJson.success === false) { + toast("Failed to deactivate user. Please contact support@shuffler.io if this persists.") } else { toast("Changed activation for user " + data.id); } @@ -1146,55 +1168,54 @@ If you're interested, please let me know a time that works for you, or set up a }); }; -const handleClickChangeOrg = (orgId) => { - // Don't really care about the logout - //name: org.name, - //orgId = "asd" - const data = { - org_id: orgId, - } - - localStorage.setItem("globalUrl", "") - localStorage.setItem("getting_started_sidebar", "open"); - - fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { - mode: 'cors', - credentials: 'include', - crossDomain: true, - method: 'POST', - body: JSON.stringify(data), - withCredentials: true, - headers: { - 'Content-Type': 'application/json; charset=utf-8', - }, - }) - .then(function(response) { - if (response.status !== 200) { - console.log("Error in response") - } - - return response.json(); - }).then(function(responseJson) { - if (responseJson.success === true) { - if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { - console.log("Region Change: ", responseJson.region_url) - localStorage.setItem("globalUrl", responseJson.region_url) - //globalUrl = responseJson.region_url - } - - setTimeout(() => { - window.location.reload() - }, 2000) - toast("Successfully changed active organization - refreshing!") - } else { - toast("Failed changing org: ", responseJson.reason) - } - }) - .catch(error => { - console.log("error changing: ", error) - //removeCookie("session_token", {path: "/"}) - }) -} + const handleClickChangeOrg = (orgId) => { + // Don't really care about the logout + //name: org.name, + //orgId = "asd" + const data = { + org_id: orgId, + } + + localStorage.setItem("globalUrl", "") + localStorage.setItem("getting_started_sidebar", "open"); + + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: 'cors', + credentials: 'include', + crossDomain: true, + method: 'POST', + body: JSON.stringify(data), + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then(function(response) { + if (response.status !== 200) { + console.log("Error in response") + } + + return response.json(); + }).then(function(responseJson) { + if (responseJson.success === true) { + if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { + localStorage.setItem("globalUrl", responseJson.region_url) + //globalUrl = responseJson.region_url + } + + setTimeout(() => { + window.location.reload() + }, 2000) + toast("Successfully changed active organization - refreshing!") + } else { + toast("Failed changing org: "+responseJson.reason) + } + }) + .catch(error => { + console.log("error changing: ", error) + //removeCookie("session_token", {path: "/"}) + }) + } const inviteUser = (data) => { @@ -1850,6 +1871,8 @@ const handleClickChangeOrg = (orgId) => { .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { toast("Failed setting user: " + responseJson.reason); + } else if (responseJson.success === false) { + toast("Failed to update user") } else { //toast("Set the user field " + field + " to " + value); toast("Successfully updated user field " + field) @@ -2912,6 +2935,7 @@ const handleClickChangeOrg = (orgId) => { )} { }} /> - {adminTab === 0 ? ( - - ) - : adminTab === 1 ? ( -
    + {adminTab === 0 ? ( + + ) + : adminTab === 1 ? ( +
    {

    Environments

    - Decides what Orborus environment to execute an action in a workflow - in.{" "} + Decides what Orborus environment to run your workflow actions. If you have scale problems, talk to our team: support@shuffler.io.  { }} /> - + + + { const queueSize = environment.queue !== undefined && environment.queue !== null ? environment.queue < 0 ? 0 : environment.queue > 1000 ? ">1000" : environment.queue : 0 return ( - - + + + + + + : environment.run_type === "docker" ? + + + + : environment.run_type === "k8s" ? + + + + : + + + + } + style={{ + minWidth: 50, + maxWidth: 50, + overflow: "hidden", + }} + /> + + + + : + + + + + + } + style={{ + minWidth: 85, + maxWidth: 85, + overflow: "hidden", + }} + /> { environment.running_ip === null || environment.running_ip.length === 0 ? -
    - Not running -
    +
    + Not running +
    : environment.running_ip.split(":")[0] : "N/A" } style={{ - minWidth: 200, - maxWidth: 200, + minWidth: 150, + maxWidth: 150, overflow: "hidden", }} /> @@ -4849,11 +4926,7 @@ const handleClickChangeOrg = (orgId) => { /> - { /> - { @@ -4920,7 +4990,7 @@ const handleClickChangeOrg = (orgId) => { letterSpacing: "1px", }} > - Sub Organizations of the Current Organization + Sub Organizations of the Current Organization ({subOrgs.length})
    @@ -4940,11 +5010,7 @@ const handleClickChangeOrg = (orgId) => { /> - @@ -4974,11 +5040,7 @@ const handleClickChangeOrg = (orgId) => { /> - { />
    ) - })} + })*/}
    ) } @@ -321,8 +340,6 @@ const RunWorkflow = (defaultprops) => { event.preventDefault() } - console.log("In submit!") - stop() setMessage("") setExecutionLoading(true) @@ -330,12 +347,22 @@ const RunWorkflow = (defaultprops) => { setExecutionInfo("") var data = { - "execution_argument": executionArgument + "execution_argument": executionArgument, + "execution_source": "questions", + } + + if (workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0) { + try { + data["execution_argument"] = JSON.stringify(executionArgument) + } catch (e) { + console.log("Error parsing execution argument: ", e) + } } if (workflow.start !== undefined && workflow.start !== null && workflow.start.length > 0) { - data.start = workflow.start + //data.start = workflow.start } else { + /* if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { for (let actionkey in workflow.actions) { if (workflow.actions[actionkey].isStartNode) { @@ -344,6 +371,7 @@ const RunWorkflow = (defaultprops) => { } } } + */ } var url = `${globalUrl}/api/v1/workflows/${props.match.params.key}/execute` @@ -369,7 +397,6 @@ const RunWorkflow = (defaultprops) => { console.log("Pre request: ", url, fetchBody) fetch(url, fetchBody) .then((response) => { - console.log("Got answer 1") if (response.status !== 200 && response.status !== 201) { if (answer !== undefined && execution_id !== undefined && authorization !== undefined) { @@ -385,11 +412,9 @@ const RunWorkflow = (defaultprops) => { } } - console.log("Got answer 2") return response.json(); }) .then(responseJson => { - console.log("Got answer 3") setExecutionLoading(false) if (responseJson["success"] === false) { console.log("Failed sending execution request") @@ -404,7 +429,6 @@ const RunWorkflow = (defaultprops) => { start(); } } - console.log("Got answer 4") }) .catch(error => { //setExecutionInfo("Error in workflow startup: " + error) @@ -446,8 +470,18 @@ const RunWorkflow = (defaultprops) => { responseJson.triggers = []; } - handleGetOrg(responseJson.org_id) - setWorkflow(responseJson); + if (responseJson.input_questions !== undefined && responseJson.input_questions !== null && responseJson.input_questions.length > 0) { + var newexec = {} + for (let questionkey in responseJson.input_questions) { + const question = responseJson.input_questions[questionkey] + newexec[question.value] = "" + } + + setExecutionArgument(newexec) + } + + handleGetOrg(responseJson.org_id) + setWorkflow(responseJson); }) .catch((error) => { console.log("Get workflow error: ", error.toString()); @@ -475,6 +509,18 @@ const RunWorkflow = (defaultprops) => { // Doesn't work because this is some async garbage if (executionData.execution_id === undefined || (responseJson.execution_id === executionData.execution_id && responseJson.results !== undefined && responseJson.results !== null)) { if (executionData.status !== responseJson.status || executionData.result !== responseJson.result || (executionData.results !== undefined && responseJson.results !== null && executionData.results.length !== responseJson.results.length)) { + + if (responseJson.result !== undefined && responseJson.result !== null && responseJson.result.length > 0) { + if (responseJson.result.startsWith("[") && responseJson.result.endsWith("]")) { + try { + responseJson.result = JSON.parse(responseJson.result).length + console.log("Set length to: ", responseJson.result) + } catch (e) { + console.log("Error parsing length: ", e) + } + } + } + //console.log("Updating data!") setExecutionData(responseJson) @@ -685,27 +731,50 @@ const RunWorkflow = (defaultprops) => { {message} {answer !== undefined && answer !== null ? null : - Workflow: {workflow.name} + {workflow.name} } - {executionData !== undefined && executionData !== null && executionData !== {} && executionData.status !== undefined && (answer === undefined || answer === null) ? -
    - - Status  - - - {executionData.status} - -
    - : null} - {workflowQuestion.length > 0 ? {workflowQuestion} : null} - {answer !== undefined && answer !== null ? null : + {workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ? +
    + {workflow.input_questions.map((question, index) => { + + return ( +
    + {question.name} + { + //setExecutionArgument(e.target.value) + executionArgument[question.value] = e.target.value + }} + /> +
    + ) + })} +
    + : + answer !== undefined && answer !== null ? null : Runtime Argument
    @@ -733,6 +802,7 @@ const RunWorkflow = (defaultprops) => {
    } + {executionRunning ? diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 72b0a217..7f236c89 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( github.com/docker/docker v23.0.3+incompatible github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.68 + github.com/shuffle/shuffle-shared v0.5.93 k8s.io/api v0.28.1 k8s.io/apimachinery v0.28.1 k8s.io/client-go v0.28.1 diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 82ba3db3..713c5ad1 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -361,6 +361,8 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shuffle/shuffle-shared v0.5.68 h1:2ok4SsLojHvFIQMfe2upm3CLEvSbUPMJPIBaBZO1bL8= github.com/shuffle/shuffle-shared v0.5.68/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0= +github.com/shuffle/shuffle-shared v0.5.93 h1:fZf9s2cEgDoyYXXvPYVeNh8UysuSlywQkc54IXkNL0k= +github.com/shuffle/shuffle-shared v0.5.93/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 2d761857..25412d05 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1065,26 +1065,10 @@ func getOrborusStats(ctx context.Context) shuffle.OrborusStats { Timestamp: time.Now().Unix(), } - // FIXME: Returning for now due to this causing network congestion - // and database fillup. The backend api also has it disabled. - return newStats - - // Disable orborus stats - if os.Getenv("SHUFFLE_STATS_DISABLED") == "true" { - return newStats - } - - - if swarmConfig == "run" || swarmConfig == "swarm" { + if (swarmConfig == "run" || swarmConfig == "swarm") && strings.Contains(newWorkerImage, "scale") { newStats.Swarm = true } - - // Run this 1/10 times - //if rand.Intn(10) != 1 { - // return newStats - //} - newStats.PollTime = sleepTime newStats.MaxQueue = maxConcurrency newStats.Queue = executionCount @@ -1094,6 +1078,15 @@ func getOrborusStats(ctx context.Context) shuffle.OrborusStats { return newStats } + // Disable orborus stats + if os.Getenv("SHUFFLE_STATS_DISABLED") == "true" { + return newStats + } + + // FIXME: Returning for now due to this causing network congestion + // and database fillup. The backend api also has it disabled. + return newStats + // Use the docker API to get the CPU usage of the docker engine machine pers, err := dockercli.Info(ctx) if err != nil { From e3271b2438e2f9b9e05dd6396c439f0d1b1fe2f3 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Mar 2024 02:50:07 +0100 Subject: [PATCH 052/142] Fixed UI tracker backend --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 ++ backend/go-app/main.go | 2 +- backend/go-app/walkoff.go | 2 +- functions/onprem/orborus/go.mod | 6 +++-- functions/onprem/orborus/go.sum | 42 +++++++-------------------------- 6 files changed, 18 insertions(+), 38 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 0895bde5..d35c542e 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.91 + github.com/shuffle/shuffle-shared v0.5.93 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index f99af079..c0c09158 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -463,6 +463,8 @@ github.com/shuffle/shuffle-shared v0.5.88 h1:YNM6xtnKg0BoMmw2pqV/LnNAsMWMrAzYIAy github.com/shuffle/shuffle-shared v0.5.88/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shuffle/shuffle-shared v0.5.91 h1:CN2K4iDt2zjx7MR9B+u5Yb7tA8IEw5lk8+Ab+Wia11Q= github.com/shuffle/shuffle-shared v0.5.91/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.5.93 h1:fZf9s2cEgDoyYXXvPYVeNh8UysuSlywQkc54IXkNL0k= +github.com/shuffle/shuffle-shared v0.5.93/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ccba422d..52d8bc78 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -969,7 +969,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { tutorialsFinished = append(tutorialsFinished, tutorial) } - licensed := shuffle.IsLicensed(ctx, *currentOrg) + licensed := shuffle.IsLicensed(ctx, *org) returnValue := shuffle.HandleInfo{ Success: true, diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 634e3acf..10df25b2 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -319,7 +319,7 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { } env.Checkin = timeNow - err = shuffle.SetEnvironment(ctx, &env) + err = shuffle.SetEnvironment(ctx, env) if err != nil { log.Printf("[ERROR] Failed updating environment: %s", err) } diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 7f236c89..6d4e457c 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -23,7 +23,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/adrg/strutil v0.2.3 // indirect github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect - github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 // indirect + github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect @@ -34,6 +34,7 @@ require ( github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/frikky/kin-openapi v0.41.0 // indirect + github.com/frikky/schemaless v0.0.6 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect @@ -71,6 +72,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/sashabaranov/go-openai v1.19.2 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/skeema/knownhosts v1.2.1 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect @@ -89,7 +91,7 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect google.golang.org/api v0.36.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595 // indirect google.golang.org/grpc v1.34.1 // indirect google.golang.org/protobuf v1.30.0 // indirect diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 713c5ad1..9e8931bc 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -53,10 +53,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCv github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -75,8 +73,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3w github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw= -github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -89,7 +87,6 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -109,7 +106,6 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/El github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -117,14 +113,14 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= +github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= +github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -230,7 +226,6 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -246,7 +241,6 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -256,7 +250,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -266,16 +259,13 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/libgit2/git2go/v34 v34.0.0/go.mod h1:blVco2jDAw6YTXkErMMqzHLcAjKkwF0aWIRHBqiJkZ0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= @@ -338,11 +328,9 @@ github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsr github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -354,13 +342,12 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= +github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.5.68 h1:2ok4SsLojHvFIQMfe2upm3CLEvSbUPMJPIBaBZO1bL8= -github.com/shuffle/shuffle-shared v0.5.68/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0= github.com/shuffle/shuffle-shared v0.5.93 h1:fZf9s2cEgDoyYXXvPYVeNh8UysuSlywQkc54IXkNL0k= github.com/shuffle/shuffle-shared v0.5.93/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -371,9 +358,7 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -388,7 +373,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -407,14 +391,11 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -550,7 +531,6 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -583,7 +563,6 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -611,7 +590,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -634,6 +612,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -661,7 +640,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -743,8 +721,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -825,9 +804,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= From 06494a0093a5f27fdb14da8d8ea63843d63ccc74 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Mar 2024 16:00:37 +0100 Subject: [PATCH 053/142] Fixed SDK bug in loops with tmp being undefined --- backend/app_sdk/app_base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index c64939ce..c3162303 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1164,6 +1164,7 @@ class AppBase: for subparams in param_multiplier: #self.logger.info(f"SUBPARAMS IN MULTI: {subparams}") + tmp = "" try: while True: From 54b3687eefd9dff4f7753c8daccb857b6e78f620 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 19 Mar 2024 23:56:16 +0100 Subject: [PATCH 054/142] Fixed further user input issues --- backend/app_sdk/app_base.py | 11 +++- frontend/src/components/CacheView.jsx | 4 +- frontend/src/components/Files.jsx | 2 +- frontend/src/views/Admin.jsx | 23 ++++--- frontend/src/views/AngularWorkflow.jsx | 85 +++++++++++++++++++++++++- frontend/src/views/Workflows.jsx | 48 +++++++++------ functions/onprem/orborus/go.mod | 2 +- functions/onprem/orborus/go.sum | 2 + functions/onprem/orborus/orborus.go | 9 ++- 9 files changed, 148 insertions(+), 38 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index c3162303..be5bee26 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1371,9 +1371,16 @@ class AppBase: returns = [] for item in value: - self.logger.info("VALUE: %s" % item) + self.logger.info("FILE VALUE: %s" % item) + # Check if item is a dict, and if it is, check if it has the key "id" + if isinstance(item, dict): + if "file_id" in item: + item = item["file_id"] + elif "id" in item: + item = item["id"] + if len(item) != 36 and not item.startswith("file_"): - self.logger.info("Bad length for file value %s" % item) + self.logger.info("Bad length for file value: '%s'" % item) continue #return { # "filename": "", diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 607e0030..8810029a 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -457,7 +457,7 @@ const CacheView = (props) => { const validate = validateJson(data.value); return ( - + { style={{ minWidth: 400, maxWidth: 400, - overflowX: "auto", - overflowY: "hidden", }} primary={validate.valid ? { const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); //const alert = useAlert(); - const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"] + const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1"] var upload = ""; const handleKeyDown = (event) => { diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 4890918f..099cbeba 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1155,7 +1155,7 @@ If you're interested, please let me know a time that works for you, or set up a }) .then((responseJson) => { if (responseJson.success === false) { - toast("Failed getting your org. If this persists, please contact support."); + //toast("Failed getting your org. If this persists, please contact support."); } else { const { subOrgs, parentOrg } = responseJson; setSubOrgs(subOrgs); @@ -1164,7 +1164,7 @@ If you're interested, please let me know a time that works for you, or set up a }) .catch((error) => { console.log("Error getting sub orgs: ", error); - toast("Error getting sub organizations"); + //toast("Error getting sub organizations"); }); }; @@ -2846,13 +2846,20 @@ If you're interested, please let me know a time that works for you, or set up a : null} {isCloud ? { - toast("Region change is not implemented yet for users. Please contact support.") + toast("Region change is not directly implemented yet, and requires support help.") + + if (window.drift !== undefined) { + window.drift.api.startInteraction({ + interactionId: 386411, + }) + } }} > {regiontag} @@ -3924,7 +3931,7 @@ If you're interested, please let me know a time that works for you, or set up a Schedules used in Workflows. Makes locating and control easier.{" "} @@ -4528,7 +4535,7 @@ If you're interested, please let me know a time that works for you, or set up a style={{ minWidth: 150, maxWidth: 150 }} /> { }); }; + // POST to /api/v1/workflows + const createWorkflow = (workflow, trigger_index) => { + fetch(globalUrl + "/api/v1/workflows", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(workflow), + credentials: "include", + }) + .then((response) => { + if (response.status === 200) { + getAvailableWorkflows(trigger_index) + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id.length > 0) { + toast("Successfully created workflow"); + + handleWorkflowSelectionUpdate({ target: { value: responseJson } }, true) + } + }) + .catch((error) => { + console.log("Create workflow error: ", error.toString()) + }) + } + const UserinputSidebar = () => { if (Object.getOwnPropertyNames(selectedTrigger).length > 0 && workflow.triggers[selectedTriggerIndex] !== undefined) { if ( @@ -13229,7 +13259,7 @@ const AngularWorkflow = (defaultprops) => { /> {workflow.triggers[selectedTriggerIndex].parameters[2] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[2].value.includes("subflow") ? ( -
    +
    {workflows === undefined || workflows === null || workflows.length === 0 ? null : ( @@ -13316,7 +13346,29 @@ const AngularWorkflow = (defaultprops) => { }} /> )} -
    + + {/* Button for making a new workflow to attach */} + + +
    ) : null} {workflow.triggers[selectedTriggerIndex].parameters[2] !== undefined && @@ -16202,11 +16254,13 @@ const AngularWorkflow = (defaultprops) => { Env      + { window.open("/admin?tab=environments", "_blank") }}> {executionData.workflow.actions[0].environment} +
    : null} {executionData.status !== undefined && @@ -16877,6 +16931,30 @@ const AngularWorkflow = (defaultprops) => { return "" } + // Check if array with json inside to handle one item at a time~ + if (typeof result === "object" && result.length !== undefined) { + if (result.length > 0) { + // Check type inside + if (typeof result[0] === "object") { + result = result[0] + } + } + } + + if (result.success === true && result.status === 200) { + if (result.body !== undefined && result.body !== null) { + const stringbody = result.body.toString() + if ((stringbody.startsWith("{") && stringbody.endsWith("}")) || (stringbody.startsWith("[") && stringbody.endsWith("]"))) { + return "" + } + + 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 { + } + } + // Validate and check for newlines if (result.success !== false) { @@ -16894,7 +16972,8 @@ const AngularWorkflow = (defaultprops) => { } return "" - } + } + try { stringjson = JSON.stringify(result) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 03fab69d..bf21e96b 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1018,9 +1018,9 @@ const Workflows = (props) => { data.default_return_value, data, false, - [], - "", - data.status + [], + "", + data.status ).then((response) => { if (response !== undefined) { toast(`Successfully imported ${data.name}`); @@ -1483,17 +1483,9 @@ const Workflows = (props) => { const sanitizeWorkflow = (data) => { data = JSON.parse(JSON.stringify(data)); - data["owner"] = ""; console.log("Sanitize start: ", data); data = deduplicateIds(data); - data["org"] = []; - data["org_id"] = ""; - data["execution_org"] = {}; - - // These are backwards.. True = saved before. Very confuse. - data["previously_saved"] = false; - data["first_save"] = false; console.log("Sanitize end: ", data); return data; @@ -1508,14 +1500,24 @@ const Workflows = (props) => { let exportFileDefaultName = data.name + ".json"; + data["owner"] = ""; + data["org"] = []; + data["org_id"] = ""; + data["execution_org"] = {}; + + // These are backwards.. True = saved before. Very confuse. + data["previously_saved"] = false; + data["first_save"] = false; + if (sanitize === true) { data = sanitizeWorkflow(data); if (data.subflows !== null && data.subflows !== undefined) { toast( "Not exporting with subflows when sanitizing. Please manually export them." - ); - data.subflows = []; + ) + + data.subflows = [] } // for (var key in data.subflows) { @@ -1527,7 +1529,7 @@ const Workflows = (props) => { // Add correct ID's for triggers // Add mag - data.status = "test" + data.status = "test" let dataStr = JSON.stringify(data); let dataUri = "data:application/json;charset=utf-8," + encodeURIComponent(dataStr); @@ -2220,6 +2222,7 @@ const Workflows = (props) => { inputblogpost, inputstatus, ) => { + var method = "POST"; var extraData = ""; var workflowdata = {}; @@ -2232,6 +2235,10 @@ const Workflows = (props) => { console.log("REMOVING OWNER"); workflowdata["owner"] = ""; + workflowdata["org"] = []; + workflowdata["org_id"] = ""; + workflowdata["execution_org"] = {}; + workflowdata["previously_saved"] = false; // FIXME: Loop triggers and turn them off? } @@ -2240,8 +2247,9 @@ const Workflows = (props) => { if (tags !== undefined) { workflowdata["tags"] = tags; } - workflowdata["blogpost"] = inputblogpost - workflowdata["status"] = inputstatus + + workflowdata["blogpost"] = inputblogpost + workflowdata["status"] = inputstatus if (defaultReturnValue !== undefined) { workflowdata["default_return_value"] = defaultReturnValue; @@ -2320,7 +2328,7 @@ const Workflows = (props) => { if (file.type !== "application/json") { if (file.type !== undefined) { toast("File has to contain valid json"); - setSubmitLoading(false) + setSubmitLoading(false) } continue; @@ -2338,7 +2346,7 @@ const Workflows = (props) => { return; } - console.log("File being loaded: ", data.name); + console.log("File being loaded: ", data.name); // Initialize the workflow itself setNewWorkflow( @@ -2359,6 +2367,10 @@ const Workflows = (props) => { data.first_save = false; data.previously_saved = false; data.is_valid = false; + data.org_id = userdata.active_org.id + data.org = [] + data.execution_org = {} + // Actually create it setNewWorkflow( diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 6d4e457c..888c95f5 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( github.com/docker/docker v23.0.3+incompatible github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.93 + github.com/shuffle/shuffle-shared v0.5.98 k8s.io/api v0.28.1 k8s.io/apimachinery v0.28.1 k8s.io/client-go v0.28.1 diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 9e8931bc..78bb4045 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -350,6 +350,8 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shuffle/shuffle-shared v0.5.93 h1:fZf9s2cEgDoyYXXvPYVeNh8UysuSlywQkc54IXkNL0k= github.com/shuffle/shuffle-shared v0.5.93/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.5.98 h1:0qG/1UZZVmY+wIJBuC9bnFjCITJBr7iKLG5ZibpBkTI= +github.com/shuffle/shuffle-shared v0.5.98/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 25412d05..7e01b46c 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -829,7 +829,7 @@ func deployWorker(image string, identifier string, env []string, executionReques } if err != nil { - log.Printf("[ERROR] Failed to start worker container in environment %s: %s", environment, err) + log.Printf("[ERROR] Failed to start worker container in environment '%s': %s", environment, err) return err } else { log.Printf("[INFO][%s] Worker Container created (2). Environment %s: docker logs %s", executionRequest.ExecutionId, environment, cont.ID) @@ -1672,6 +1672,11 @@ func main() { continue } + if len(execution.ExecutionId) == 0 { + log.Printf("[WARNING] Execution ID is empty: %#v", execution) + continue + } + if execution.Status == "ABORT" || execution.Status == "FAILED" { log.Printf("[INFO] Executionstatus issue: ", execution.Status) } @@ -1767,7 +1772,7 @@ func main() { toBeRemoved.Data = append(toBeRemoved.Data, execution) executionIds = append(executionIds, execution.ExecutionId) } else { - log.Printf("[WARNING] Execution ID %s failed to deploy: %s", execution.ExecutionId, err) + log.Printf("[WARNING] Execution ID '%s' failed to deploy: %s", execution.ExecutionId, err) } } From 2b3675a329dbff3a3a7cac881c926f2405af70ca Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 19 Mar 2024 23:57:19 +0100 Subject: [PATCH 055/142] Last part worker update for user update with shuffle-shared --- functions/onprem/worker/go.mod | 2 +- functions/onprem/worker/worker.go | 35 +++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 9bc53ad4..b51d219d 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/docker v23.0.3+incompatible github.com/gorilla/mux v1.8.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.86 + github.com/shuffle/shuffle-shared v0.5.98 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 548e6b92..58cb4de9 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -1601,12 +1601,17 @@ func handleSubflowPoller(ctx context.Context, workflowExecution shuffle.Workflow } } + hasUserinput := false for _, result := range workflowExecution.Results { if result.Action.ID != subflowId { continue } - log.Printf("[DEBUG][%s] Found subflow to handle: %s (%s)", workflowExecution.ExecutionId, result.Action.Label, result.Status) + if result.Action.AppName == "User Input" { + hasUserinput = true + } + + log.Printf("[DEBUG][%s] Found subflow to handle: %s (%s)", workflowExecution.ExecutionId, result.Action.AppName, result.Status) if result.Status == "SUCCESS" || result.Status == "FINISHED" || result.Status == "FAILURE" || result.Status == "ABORTED" { // Check for results @@ -1615,7 +1620,14 @@ func handleSubflowPoller(ctx context.Context, workflowExecution shuffle.Workflow } } - log.Printf("[INFO][%s] Status: %s, Results: %d, actions: %d", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra) + + if workflowExecution.Status == "WAITING" && workflowExecution.ExecutionSource != "default" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { + log.Printf("[INFO][%s] Workflow execution is waiting. Exiting worker, as backend will restart it.", workflowExecution.ExecutionId) + shutdown(workflowExecution, "", "", true) + } + + + log.Printf("[INFO][%s] (2) Status: %s, Results: %d, actions: %d. Userinput: %#v", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra, hasUserinput) return errors.New("Subflow status not found yet") } @@ -1685,7 +1697,7 @@ func handleDefaultExecutionWrapper(ctx context.Context, workflowExecution shuffl } } - log.Printf("[INFO][%s] Status: %s, Results: %d, actions: %d", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra) + log.Printf("[INFO][%s] (3) Status: %s, Results: %d, actions: %d", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra) if workflowExecution.Status != "EXECUTING" { log.Printf("[WARNING][%s] Exiting as worker execution has status %s!", workflowExecution.ExecutionId, workflowExecution.Status) log.Printf("[DEBUG] Shutting down (21)") @@ -2687,12 +2699,23 @@ func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, newerr = strings.ReplaceAll(strings.ReplaceAll(newerr, "\"", "\\\""), "\n", "\\n") } + if strings.Contains(fmt.Sprintf("%s", err), "no such host") { + log.Printf("[DEBUG] SHOULD be Removing references to location for app %s as to be rediscovered", action.AppName) + + //for k, v := range portMappings { + // if strings.Contains(strings.ToLower(strings.ReplaceAll(action.AppName, " ", "_"))) { + // } + //} + + //var portMappings map[string]int + } + log.Printf("[ERROR][%s] Error running app run request: %s", workflowExecution.ExecutionId, err) actionResult := shuffle.ActionResult{ Action: *action, ExecutionId: workflowExecution.ExecutionId, Authorization: workflowExecution.Authorization, - Result: fmt.Sprintf(`{"success": false, "reason": "Failed to connect to app %s in swarm. Restart Orborus if this is recurring, or contact support@shuffler.io.", "details": "%s"}`, streamUrl, newerr), + Result: fmt.Sprintf(`{"success": false, "reason": "Failed to connect to app %s in swarm. Try the action again, restart Orborus if this is recurring, or contact support@shuffler.io.", "details": "%s"}`, streamUrl, newerr), StartedAt: int64(time.Now().Unix()), CompletedAt: int64(time.Now().Unix()), Status: "FAILURE", @@ -3187,11 +3210,11 @@ func handleRunExecution(resp http.ResponseWriter, request *http.Request) { } } - log.Printf("[INFO][%s] Status: %s, Results: %d, actions: %d", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra) + log.Printf("[INFO][%s] (1) Status: %s, Results: %d, actions: %d", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra) if workflowExecution.Status != "EXECUTING" { log.Printf("[WARNING] Exiting as worker execution has status %s!", workflowExecution.Status) - log.Printf("[DEBUG] Shutting down (21)") + log.Printf("[DEBUG] Shutting down (38)") resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad status %s for the workflow execution %s"}`, workflowExecution.Status, workflowExecution.ExecutionId))) return From 05b80d2f55ea1c20fdef64fd95460391dcca2e53 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 20 Mar 2024 03:42:50 +0100 Subject: [PATCH 056/142] Fixed oauth2-app mapping --- backend/go-app/go.mod | 2 +- frontend/src/components/Oauth2Auth.jsx | 19 ++++++++------- frontend/src/components/ParsedAction.jsx | 12 --------- frontend/src/views/AngularWorkflow.jsx | 27 +++++++++------------ frontend/src/views/AppCreator.jsx | 10 +++++--- frontend/src/views/SetAuthentication.jsx | 6 ++--- frontend/src/views/UpdateAuthentication.jsx | 2 +- 7 files changed, 33 insertions(+), 45 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index d35c542e..520d8919 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.93 + github.com/shuffle/shuffle-shared v0.6.0 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 94b57b2a..8b9d58e6 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -767,7 +767,7 @@ const AuthenticationOauth2 = (props) => { } - + OR @@ -805,18 +805,19 @@ const AuthenticationOauth2 = (props) => { setOauthUrl(data.value); } - const defaultValue = data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value - const fieldname = data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 ? "Token URL" : data.name + const defaultValue = data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value + + const fieldname = data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name return ( -
    +
    {fieldname} {data.schema !== undefined && - data.schema !== null && - data.schema.type === "bool" ? ( + data.schema !== null && + data.schema.type === "bool" ? - ) : ( + : { //const [oauthUrl, setOauthUrl] = React.useState("") }} /> - )} + }
    - ); + ) })} { return helperText } - //console.log("AUTH: ", authenticationType) - if (authenticationType !== undefined && authenticationType !== null && authenticationType.type === "oauth2") { - /* - return ( - - You must authenticate before using oauth2 apps. - - ) - */ - } - - //console.log("APP: ", selectedApp) // FIXME: Issue #40 - selectedActionParameters not reset if ( diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 4549f8ad..ccf7bee9 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -4049,7 +4049,6 @@ const AngularWorkflow = (defaultprops) => { if (!curapp || curapp === undefined) { console.log("APPS - couldn't find it: ", newapps) - //toast(`App ${curaction.app_name}:${curaction.app_version} not found. Is it activated?`); const tmpapp = { name: curaction.app_name, @@ -4057,20 +4056,16 @@ const AngularWorkflow = (defaultprops) => { app_version: curaction.app_version, id: curaction.app_id, actions: [curaction], - }; + } - setSelectedApp(tmpapp); - setSelectedAction(curaction); + setSelectedApp(tmpapp) + setSelectedAction(curaction) } else { - //if (curapp.id !== curaction.id) { - // curaction.app_id = curapp.id - // //.valueOf() - //} curaction.app_id = curapp.id setAuthenticationType( - curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null ? { - type: "oauth2", + 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, @@ -8117,7 +8112,7 @@ const AngularWorkflow = (defaultprops) => { : `${pixelSize} solid ${yellow}`; if (app.id == highlightedApp) { - console.log("Found correct appid to highlight: ", app.id) + //console.log("Found correct appid to highlight: ", app.id) newAppStyle.border = "3px solid " + green } @@ -18258,13 +18253,13 @@ const AngularWorkflow = (defaultprops) => { style={{ flex: 2, padding: 0, - minHeight: isMobile ? "90%" : 650, - maxHeight: isMobile ? "90%" : 650, + minHeight: isMobile ? "90%" : 700, + maxHeight: isMobile ? "90%" : 700, overflowY: "auto", overflowX: isMobile ? "auto" : "hidden", }} > - {authenticationType.type === "oauth2" ? ( + {authenticationType.type === "oauth2" || authenticationType.type === "oauth2-app" ? { setAuthenticationModalOpen={setAuthenticationModalOpen} isCloud={isCloud} /> - ) : ( + : - )} + }
    { optionset = true } else if (value.scheme === "oauth2") { - setAuthenticationOption("Oauth2"); - setAuthenticationRequired(true); - optionset = true + setAuthenticationOption("Oauth2"); + setAuthenticationRequired(true); + optionset = true } else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) { //toast("Can't handle Oauth2 auth yet.") @@ -6054,6 +6054,10 @@ const AppCreator = (defaultprops) => { if (e.target.value === "application" && oauth2GrantType === "") { setOauth2GrantType("client_credentials") + } + + if (e.target.value === "delegated") { + setOauth2GrantType("") } }} value={oauth2Type} diff --git a/frontend/src/views/SetAuthentication.jsx b/frontend/src/views/SetAuthentication.jsx index 258056d0..0015deda 100755 --- a/frontend/src/views/SetAuthentication.jsx +++ b/frontend/src/views/SetAuthentication.jsx @@ -305,15 +305,15 @@ const SetAuthentication = (props) => { variant="h4" style={{ marginLeft: "auto", marginRight: "auto", marginTop: 50}} > - Oauth2 setup + Oauth2 setup {!finished ? ( - failed ? - null : + failed ? + null : ) : ( "Done - this window should close within 3 seconds." diff --git a/frontend/src/views/UpdateAuthentication.jsx b/frontend/src/views/UpdateAuthentication.jsx index 054244c3..92b17e80 100644 --- a/frontend/src/views/UpdateAuthentication.jsx +++ b/frontend/src/views/UpdateAuthentication.jsx @@ -141,7 +141,7 @@ const SetAuthentication = (props) => { {app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ? null : - app.authentication.type === "oauth2" ? + app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ? Date: Wed, 20 Mar 2024 15:33:14 +0100 Subject: [PATCH 057/142] Fixed code editor colorization and highlights --- .../src/components/ShuffleCodeEditor1.jsx | 50 ++++++++++++------- frontend/src/views/AngularWorkflow.jsx | 4 ++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index b08d0252..37c2749e 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -55,7 +55,9 @@ import { tags as t } from '@lezer/highlight'; import AceEditor from "react-ace"; import 'ace-builds/src-noconflict/mode-python'; -import 'ace-builds/src-noconflict/theme-twilight'; +//import 'ace-builds/src-noconflict/theme-twilight'; +//import 'ace-builds/src-noconflict/theme-solarized_dark'; +import 'ace-builds/src-noconflict/theme-gruvbox'; import "ace-builds/src-noconflict/ext-language_tools"; import ace from "ace-builds"; @@ -182,14 +184,13 @@ const CodeEditor = (props) => { const fullpath = "$"+actionlist[i].autocomplete.toLowerCase()+parsedPaths[key].autocomplete if (!allVariables.includes(fullpath)) { allVariables.push(fullpath) + allVariables.push(fullpath.toLowerCase()) } } } setAvailableVariables(allVariables) setMainVariables(tmpVariables) - - //console.log("Checking local codedata: ", localcodedata) expectedOutput(localcodedata) }, []) @@ -570,10 +571,14 @@ const CodeEditor = (props) => { } const highlight_variables = (value) => { + if (value === undefined || value === null || value.length === 0) { + return + } // var session = localcodedata.getSession(); - var code_lines = localcodedata.split('\n'); + //var code_lines = localcodedata.split('\n') + var code_lines = value.split('\n') - const newMarkers = []; + var newMarkers = [] for (var i = 0; i < code_lines.length; i++) { var current_code_line = code_lines[i]; var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g); @@ -583,7 +588,8 @@ const CodeEditor = (props) => { } var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]); - variable_occurence = new_occurences; + variable_occurence = new_occurences + var dollar_occurence = []; for (let ch = 0; ch < current_code_line.length; ch++) { @@ -597,16 +603,22 @@ const CodeEditor = (props) => { for(let occ = 0; occ < variable_occurence.length; occ++){ dollar_occurence_len.push(variable_occurence[occ].length) } - } catch (e) {} + } catch (e) { + console.log("Error in color highlighting list: ", e); + } + + //console.log("Variable occurences: ", variable_occurence) + //console.log("Dollar occurences: ", dollar_occurence) try { if (variable_occurence.length === 0) { //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #282828; border-radius: 0px; color: #b8bb26"}) //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #; border-radius: 0px; color: inherit"}) } + for (let occ = 0; occ < variable_occurence.length; occ++) { const fixedVariable = fixVariable(variable_occurence[occ]) - var correctVariable = availableVariables.includes(fixedVariable) + var correctVariable = availableVariables.includes(fixedVariable.toLowerCase()) var startCh = dollar_occurence[occ] var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] newMarkers.push({ @@ -675,12 +687,12 @@ const CodeEditor = (props) => { try { for (var i = 0; i < found.length; i++) { try { + // Finding if the value is in the list at all, and does initial replacement const fixedVariable = fixVariable(found[i]) - // Finding if the value is in the list at all, and does initial replacement var valuefound = false for (var j = 0; j < actionlist.length; j++) { - if(fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()){ + if(fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()) { continue } @@ -702,12 +714,10 @@ const CodeEditor = (props) => { } - //console.log("INPUT: ", fixedVariable, valuefound, input) if (!valuefound) { - //console.log("Couldn't find value "+fixedVariable) } - if (!valuefound && availableVariables.includes(fixedVariable)) { + if (!valuefound && availableVariables.includes(fixedVariable.toLowerCase())) { var shouldbreak = false for (var k=0; k < actionlist.length; k++){ var parsedPaths = [] @@ -716,8 +726,8 @@ const CodeEditor = (props) => { } for (var key in parsedPaths) { - const fullpath = "$"+actionlist[k].autocomplete.toLowerCase()+parsedPaths[key].autocomplete - if (fullpath !== fixedVariable) { + const fullpath = "$"+actionlist[k].autocomplete.toLowerCase()+parsedPaths[key].autocomplete.toLowerCase() + if (fullpath !== fixedVariable.toLowerCase()) { continue } @@ -1478,10 +1488,11 @@ const CodeEditor = (props) => { // minHeight: 548, // overflow: "hidden", }}> + {availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0 && { setCurrentCharacter(cursorPosition.column) setCurrentLine(cursorPosition.row) findIndex(cursorPosition.row, cursorPosition.column) - highlight_variables(value) + + //highlight_variables(value) + //console.log("VALUE CURSOR: ", value) }} onChange={(value, editor) => { // setlocalcodedata(value) @@ -1519,6 +1532,7 @@ const CodeEditor = (props) => { }} // options={options} /> + }
    { } } + if (useworkflow.id === undefined || useworkflow.id === null || useworkflow.id.length === 0) { + useworkflow.id = props.match.params.key + } + setLastSaved(true); fetch(`${globalUrl}/api/v1/workflows/${useworkflow.id}`, { method: "PUT", From 16ef8082d22b4f917d13fccec128f9cde5a7b580 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 21 Mar 2024 18:30:51 +0100 Subject: [PATCH 058/142] Made it work with Opensearch >=2.12.0 with improved password etc --- .env | 4 ++-- backend/go-app/go.mod | 14 +++++++------- backend/go-app/go.sum | 33 +++++++++++++-------------------- docker-compose.yml | 4 +++- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/.env b/.env index f574d03d..833a00d2 100755 --- a/.env +++ b/.env @@ -87,8 +87,8 @@ SHUFFLE_MAX_EXECUTION_DEPTH= # Max recursion depth for subflows DATASTORE_EMULATOR_HOST=shuffle-database:8000 #SHUFFLE_OPENSEARCH_URL=http://shuffle-opensearch:9200 SHUFFLE_OPENSEARCH_URL=https://shuffle-opensearch:9200 -SHUFFLE_OPENSEARCH_USERNAME=admin -SHUFFLE_OPENSEARCH_PASSWORD=admin +SHUFFLE_OPENSEARCH_USERNAME="admin" +SHUFFLE_OPENSEARCH_PASSWORD="StrongShufflePassword321!" SHUFFLE_OPENSEARCH_CERTIFICATE_FILE= SHUFFLE_OPENSEARCH_APIKEY= SHUFFLE_OPENSEARCH_CLOUDID= diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 520d8919..c425a292 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.0 + github.com/shuffle/shuffle-shared v0.6.2 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 @@ -30,10 +30,10 @@ require ( ) require ( - cloud.google.com/go v0.110.2 // indirect + cloud.google.com/go v0.110.0 // indirect cloud.google.com/go/compute v1.19.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.0.1 // indirect + cloud.google.com/go/iam v0.13.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Masterminds/semver v1.5.0 // indirect @@ -41,14 +41,15 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/adrg/strutil v0.2.3 // indirect github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect - github.com/bitly/go-simplejson v0.5.0 // indirect + github.com/bitly/go-simplejson v0.5.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/containerd v1.6.18 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect @@ -103,7 +104,7 @@ require ( golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.8 // indirect @@ -118,5 +119,4 @@ require ( k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect sigs.k8s.io/yaml v1.2.0 // indirect - ) diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index c0c09158..81f02ce3 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -16,8 +16,8 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -33,8 +33,8 @@ cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1 cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/iam v1.0.1 h1:lyeCAU6jpnVNrE9zGQkTl3WgNgK/X+uWwaw0kynZJMU= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -102,8 +102,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jely github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw= github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= +github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q= github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= @@ -143,8 +143,10 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg= github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -455,16 +457,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE= -github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= -github.com/shuffle/shuffle-shared v0.5.81 h1:pt4lT42FrXN/kd/vlYtm7nXShZI0mOamXUMjpWLH7qI= -github.com/shuffle/shuffle-shared v0.5.81/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= -github.com/shuffle/shuffle-shared v0.5.88 h1:YNM6xtnKg0BoMmw2pqV/LnNAsMWMrAzYIAy+o+ChdfI= -github.com/shuffle/shuffle-shared v0.5.88/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= -github.com/shuffle/shuffle-shared v0.5.91 h1:CN2K4iDt2zjx7MR9B+u5Yb7tA8IEw5lk8+Ab+Wia11Q= -github.com/shuffle/shuffle-shared v0.5.91/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= -github.com/shuffle/shuffle-shared v0.5.93 h1:fZf9s2cEgDoyYXXvPYVeNh8UysuSlywQkc54IXkNL0k= -github.com/shuffle/shuffle-shared v0.5.93/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.6.2 h1:wcy7rc8QnF18uTsicG7L+9Ce1Ah/ol7Jc8t5nu9p9/Y= +github.com/shuffle/shuffle-shared v0.6.2/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -774,9 +768,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/docker-compose.yml b/docker-compose.yml index 63124856..f146a31a 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,9 +60,10 @@ services: security_opt: - seccomp:unconfined opensearch: - image: opensearchproject/opensearch:2.11.0 + image: opensearchproject/opensearch:2.12.0 hostname: shuffle-opensearch container_name: shuffle-opensearch + env_file: .env environment: - "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM - bootstrap.memory_lock=true @@ -73,6 +74,7 @@ services: - node.name=shuffle-opensearch - node.store.allow_mmap=false - discovery.seed_hosts=shuffle-opensearch + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${SHUFFLE_OPENSEARCH_PASSWORD} ulimits: memlock: soft: -1 From c50b84036e386fbc531cf3f40792c8bfb9c2988c Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Mar 2024 15:17:05 +0100 Subject: [PATCH 059/142] Moved over cache view and admin tracker features --- .env | 18 +++-- frontend/src/components/CacheView.jsx | 36 +++++----- frontend/src/components/Header.jsx | 12 ++++ frontend/src/components/NewHeader.jsx | 2 +- frontend/src/components/ParsedAction.jsx | 30 ++++++++- .../src/components/ShuffleCodeEditor1.jsx | 45 ++++++++++--- frontend/src/views/Admin.jsx | 6 +- frontend/src/views/AngularWorkflow.jsx | 65 ++++++++++++++----- 8 files changed, 157 insertions(+), 57 deletions(-) diff --git a/.env b/.env index 833a00d2..2b7da284 100755 --- a/.env +++ b/.env @@ -49,15 +49,19 @@ DOCKER_API_VERSION=1.40 # Orborus/Proxy configurations HTTP_PROXY= HTTPS_PROXY= -SHUFFLE_PASS_WORKER_PROXY=TRUE # Decides if proxy configurations should be passed to workers or not -SHUFFLE_PASS_APP_PROXY=TRUE # Decides if proxy configurations should be passed to apps or not. Requires SHUFFLE_PASS_WORKER_PROXY=true -SHUFFLE_INTERNAL_HTTP_PROXY= # Used to differentiate proxies for shuffle internal vs external traffic +SHUFFLE_PASS_WORKER_PROXY=TRUE +SHUFFLE_PASS_APP_PROXY=TRUE +SHUFFLE_INTERNAL_HTTP_PROXY= SHUFFLE_INTERNAL_HTTPS_PROXY= -TZ=Europe/Amsterdam # Timezone-handler in Orborus, Worker and Apps -ORBORUS_CONTAINER_NAME= # Used to FIND the containername. cgroup v2: issue 501 -SHUFFLE_ORBORUS_STARTUP_DELAY= # Used for setting up a startup delay for Orborus +# Timezone-handler in Orborus, Worker and Apps +TZ=Europe/Amsterdam +# Used to FIND the containername. cgroup v2: issue 501 +ORBORUS_CONTAINER_NAME= +# Used for setting up a startup delay for Orborus +SHUFFLE_ORBORUS_STARTUP_DELAY= SHUFFLE_SKIPSSL_VERIFY=true -IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not +# Used for controlling if the environment should run in kubernetes or not +IS_KUBERNETES=false #SHUFFLE_BASE_IMAGE_NAME=shuffle #SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 8810029a..4d4d02fe 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -93,26 +93,26 @@ const CacheView = (props) => { }, credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for apps :O!"); - return; - } + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for list cache :O!"); + return; + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === true) { - setListCache(responseJson.keys); - } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === true) { + setListCache(responseJson.keys); + } - if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") { - setCacheCursor(responseJson.cursor); - } - }) - .catch((error) => { - toast(error.toString()); - }); + if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") { + setCacheCursor(responseJson.cursor); + } + }) + .catch((error) => { + toast(error.toString()); + }); }; // const getCacheList = (orgId) => { diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 4f1d47ef..e0188656 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -155,9 +155,21 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho .then(() => { // Log out anyway removeCookie("session_token", {path: "/"}) + removeCookie("__session", {path: "/"}) + removeCookie("_session", {path: "/"}) + removeCookie("session_token", {path: "/"}) + removeCookie("__session", {path: "/"}) + removeCookie("_session", {path: "/"}) + removeCookie("session_token", {path: "/"}) + removeCookie("__session", {path: "/"}) + removeCookie("_session", {path: "/"}) + removeCookie("session_token", {path: "/"}) + removeCookie("__session", {path: "/"}) + removeCookie("_session", {path: "/"}) + window.location.pathname = "/" }) .catch(error => { diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 4f6ecbdd..c2332016 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -1155,7 +1155,7 @@ const Header = (props) => { userdata.app_execution_usage === undefined || userdata.app_execution_usage < 1000 ? null : (
    { } var helperText = "" - //console.log("DATA: ", name, value) if (name.includes("url")) { if (value.includes("localhost") || value.includes("127.0.0.1")) { helperText = "Can't use localhost. Please change to your external IP." @@ -1364,6 +1364,13 @@ const ParsedAction = (props) => { } } + + var showCacheConfig = false + if (data.name === "key" && selectedAction.name.includes("cache") && selectedAction.app_name === "Shuffle Tools") { + // Show a key popout button + showCacheConfig = true + } + var disabled = false; var rows = "3"; var openApiHelperText = "This is an OpenAPI specific field"; @@ -1614,10 +1621,14 @@ const ParsedAction = (props) => { setExpansionModalOpen(true) //setcodedata(data.value) + var parsedvalue = data.value + if (parsedvalue === undefined || parsedvalue === null) { + parsedvalue = "" + } setEditorData({ "name": data.name, - "value": data.value, + "value": parsedvalue, "field_number": count, "actionlist": actionlist, "field_id": clickedFieldId, @@ -2484,6 +2495,21 @@ const ParsedAction = (props) => { : null} + {showCacheConfig === true ? + + + + + + : null} +
    { const highlight_variables = (value) => { if (value === undefined || value === null || value.length === 0) { + setMarkers([]) return } + // var session = localcodedata.getSession(); //var code_lines = localcodedata.split('\n') var code_lines = value.split('\n') @@ -589,11 +591,11 @@ const CodeEditor = (props) => { var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]); variable_occurence = new_occurences - var dollar_occurence = []; for (let ch = 0; ch < current_code_line.length; ch++) { - if (current_code_line[ch] === '$' && (ch === 0)) { + //if (current_code_line[ch] === '$' && (ch === 0)) { + if (current_code_line[ch] === '$') { dollar_occurence.push(ch); } } @@ -607,9 +609,6 @@ const CodeEditor = (props) => { console.log("Error in color highlighting list: ", e); } - //console.log("Variable occurences: ", variable_occurence) - //console.log("Dollar occurences: ", dollar_occurence) - try { if (variable_occurence.length === 0) { //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #282828; border-radius: 0px; color: #b8bb26"}) @@ -619,8 +618,10 @@ const CodeEditor = (props) => { for (let occ = 0; occ < variable_occurence.length; occ++) { const fixedVariable = fixVariable(variable_occurence[occ]) var correctVariable = availableVariables.includes(fixedVariable.toLowerCase()) + var startCh = dollar_occurence[occ] var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] + newMarkers.push({ startRow: i, startCol: startCh, @@ -629,13 +630,17 @@ const CodeEditor = (props) => { className: correctVariable ? "good-marker" : "bad-marker", type: "text", }) + + setMarkers(newMarkers) } - setMarkers(newMarkers) + } catch (e) { console.log("Error in color highlighting: ", e); } } + + setMarkers(newMarkers) }; const replaceVariables = (swapVariable) => { @@ -927,6 +932,18 @@ const CodeEditor = (props) => { + // Define a custom completer for the Ace Editor + const customVariables = availableVariables + const customCompleter = { + getCompletions: function(editor, session, pos, prefix, callback) { + callback(null, customVariables.map((variable) => ({ + caption: variable, + value: variable, + meta: 'custom', + }))); + } + } + return ( { theme="gruvbox" height={isFileEditor ? 450 : 550} width={isFileEditor ? 650 : "100%"} + markers={markers} + highlightActiveLine={false} + + enableBasicAutocompletion={true} + completers={[customCompleter]} + style={{ wordBreak: "break-word", marginTop: 0, @@ -1513,7 +1536,7 @@ const CodeEditor = (props) => { setCurrentLine(cursorPosition.row) findIndex(cursorPosition.row, cursorPosition.column) - //highlight_variables(value) + highlight_variables(localcodedata) //console.log("VALUE CURSOR: ", value) }} onChange={(value, editor) => { @@ -1528,7 +1551,12 @@ const CodeEditor = (props) => { enableBasicAutocompletion: true, enableLiveAutocompletion: true, enableSnippets: true, - useWorker: false + showLineNumbers: true, + tabSize: 2, + wrap: true, + + useWorker: false, + enableBasicAutocompletion: [customCompleter], }} // options={options} /> @@ -1688,6 +1716,7 @@ const CodeEditor = (props) => {
    } + {executionResult.errors !== undefined && executionResult.errors !== null && executionResult.errors.length > 0 ? Errors ({executionResult.errors.length}): {executionResult.errors.join("\n")} diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 099cbeba..a68a9831 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -4586,19 +4586,17 @@ If you're interested, please let me know a time that works for you, or set up a foundIndex = userdata.priorities.findIndex(prio => prio.name.includes("CPU") && prio.active === true) if (foundIndex >= 0 && userdata.priorities[foundIndex].name.endsWith(environment.Name)) { - showCPUAlert = true + showCPUAlert = true } } - //console.log("Show CPU alert: ", showCPUAlert) - const queueSize = environment.queue !== undefined && environment.queue !== null ? environment.queue < 0 ? 0 : environment.queue > 1000 ? ">1000" : environment.queue : 0 return ( diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 6c0c2e69..4fbb5bb5 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -3212,9 +3212,7 @@ const AngularWorkflow = (defaultprops) => { ) { toast("This edge can't be edited."); } else { - //console.log("DATA: ", event.target.data()) const destinationId = event.target.data("target"); - //console.log("DATA: ", event.target.data()) const curaction = workflow.actions.find((a) => a.id === destinationId); //console.log("ACTION: ", curaction) if (curaction !== undefined && curaction !== null) { @@ -7608,6 +7606,11 @@ const AngularWorkflow = (defaultprops) => { } } + // Hiding since March 2024 + if (trigger.trigger_type === "EMAIL") { + return null + } + const imagesize = isMobile ? 40 : trigger.large_image.includes("svg") ? 80 : 80 var imageline = trigger.large_image.length === 0 ? : @@ -7999,7 +8002,6 @@ const AngularWorkflow = (defaultprops) => { for (let nodekey in foundnodes) { const curnode = foundnodes[nodekey] if (curnode.data.environment !== undefined && curnode.data.environment !== null && curnode.data.environment.length > 0) { - console.log("Found environment: ", curnode.data.environment) parsedEnvironments = curnode.data.environment break } @@ -8007,7 +8009,6 @@ const AngularWorkflow = (defaultprops) => { } } - console.log("Discovered environment: ", parsedEnvironments) const newAppData = { name: app.actions[actionIndex].name, label: actionLabel, @@ -9537,7 +9538,7 @@ const AngularWorkflow = (defaultprops) => { var availableArguments = [] if (executionArgumentModalOpen && workflowExecutions.length > 0) { for (let executionKey in workflowExecutions) { - if (availableArguments.length > 5) { + if (availableArguments.length > 2) { break } @@ -16184,7 +16185,6 @@ const AngularWorkflow = (defaultprops) => { color="primary" style={{ float: "right", marginTop: 20, marginLeft: 10 }} onClick={() => { - //console.log("DATA: ", executionData); executeWorkflow( executionData.execution_argument, executionData.start, @@ -16443,20 +16443,34 @@ const AngularWorkflow = (defaultprops) => { : yellow; var imgSrc = curapp === undefined ? "" : curapp.large_image; - if ( - imgSrc.length === 0 && - workflow.actions !== undefined && - workflow.actions !== null - ) { + if (imgSrc.length === 0 && workflow.actions !== undefined && workflow.actions !== null) { // Look for the node in the workflow const action = workflow.actions.find( (action) => action.id === data.action.id - ); + ) if (action !== undefined && action !== null) { imgSrc = action.large_image; } } + if (imgSrc.length === 0 && cy !== undefined && cy !== null) { + const foundnode = cy.getElementById(data.action.id) + if (foundnode !== undefined && foundnode !== null && foundnode.length > 0) { + // FIXME: Find image from cytoscape action + } else { + for (let actionkey in workflow.actions) { + if (workflow.actions[actionkey].app_name === data.action.app_name || workflow.actions[actionkey].id === data.action.id || workflow.actions[actionkey].label === data.action.label || workflow.actions[actionkey].name === data.action.name) { + + if (workflow.actions[actionkey].large_image !== undefined && workflow.actions[actionkey].large_image !== null && workflow.actions[actionkey].large_image.length > 0) { + imgSrc = workflow.actions[actionkey].large_image + break + } + } + } + } + } + + var actionimg = curapp === null ? null : ( { } } - if ( - data.action.app_name === "Shuffle Tools" && - data.action.id !== undefined && - cy !== undefined - ) { + if (data.action.app_name === "Shuffle Tools" && data.action.id !== undefined && cy !== undefined) { const nodedata = cy.getElementById(data.action.id).data(); if (nodedata !== undefined && nodedata !== null && nodedata.fillstyle === "linear-gradient") { var imgStyle = { @@ -16954,6 +16964,18 @@ const AngularWorkflow = (defaultprops) => { } } + if (result.status === 401) { + return "Authentication failed (401). The URL or auth key is wrong. Check the body of the result for more information." + } + + if (result.status === 403) { + 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 === 400) { + return "The queries or data sent to the API is most likely wrong (400). Check the body of the result for more information." + } + // Validate and check for newlines if (result.success !== false) { @@ -16984,6 +17006,11 @@ const AngularWorkflow = (defaultprops) => { return "You can't use localhost in apps. Use the external ip or url of the server instead" } + if (result.status !== 200 && stringjson.includes("192.168") || stringjson.includes("172.16") || stringjson.includes("10.0")) { + return "Consider whether your Orborus environment can connect to a local IP or not." + } + + if (stringjson.includes("connectionerror")) { if (stringjson.includes("kms")) { return "KMS authentication failed. Check your notifications for more details." @@ -16996,6 +17023,10 @@ const AngularWorkflow = (defaultprops) => { return "Execution loading failed. Reload the execution by closing it and clicking it again" } + if (isCloud && stringjson.toLowerCase().includes("timeout error")) { + return "Run this workflow in a local environment to increase the timeout. Go to https://shuffler.io/admin?tab=environments to create an environment to connect to" + } + return "" } From bf601e3e1f3013890bcb62d529735df1a9a20506 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 18 Apr 2024 04:10:34 +0200 Subject: [PATCH 060/142] Syncing up with cloud files --- .env | 4 +- frontend/src/codeeditor-index.css | 32 + frontend/src/components/AppFramework.jsx | 8 +- frontend/src/components/ConfigureWorkflow.jsx | 8 +- frontend/src/components/Files.jsx | 34 +- frontend/src/components/Header.jsx | 5 + frontend/src/components/NewHeader.jsx | 33 +- frontend/src/components/OrgHeader.jsx | 1 + frontend/src/components/ParsedAction.jsx | 209 +- frontend/src/components/RuntimeDebugger.jsx | 134 +- frontend/src/components/SearchData.jsx | 64 + .../src/components/ShuffleCodeEditor1.jsx | 244 +- frontend/src/components/UsecaseSearch.jsx | 10 +- frontend/src/theme.jsx | 5 +- frontend/src/views/Admin.jsx | 4517 ++++++++++------- frontend/src/views/AngularWorkflow.jsx | 383 +- frontend/src/views/AppCreator.jsx | 2 +- frontend/src/views/Apps.jsx | 19 +- frontend/src/views/Search.jsx | 123 +- frontend/src/views/SettingsPage.jsx | 336 +- frontend/src/views/Workflows.jsx | 8 + 21 files changed, 3791 insertions(+), 2388 deletions(-) diff --git a/.env b/.env index 2b7da284..63ac9da3 100755 --- a/.env +++ b/.env @@ -51,8 +51,8 @@ HTTP_PROXY= HTTPS_PROXY= SHUFFLE_PASS_WORKER_PROXY=TRUE SHUFFLE_PASS_APP_PROXY=TRUE -SHUFFLE_INTERNAL_HTTP_PROXY= -SHUFFLE_INTERNAL_HTTPS_PROXY= +SHUFFLE_INTERNAL_HTTP_PROXY=NOPROXY +SHUFFLE_INTERNAL_HTTPS_PROXY=NOPROXY # Timezone-handler in Orborus, Worker and Apps TZ=Europe/Amsterdam # Used to FIND the containername. cgroup v2: issue 501 diff --git a/frontend/src/codeeditor-index.css b/frontend/src/codeeditor-index.css index 3691b90d..6a8b757f 100644 --- a/frontend/src/codeeditor-index.css +++ b/frontend/src/codeeditor-index.css @@ -58,3 +58,35 @@ code { color: white !important; opacity: 0.6; } + +/* Style for checkbox */ +.ais-RefinementList-checkbox { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + width: 16px; + height: 16px; + border: 1px solid #595b5e; + border-radius: 4px; + background-color: #27292d; + cursor: pointer; + flex-shrink: 0; +} + +.ais-RefinementList-checkbox:checked { + background-color: rgb(248, 103, 67) !important; + border-color: rgb(248, 103, 67) !important; + width: 16px; + height: 16px; +} + +.ais-RefinementList-checkbox:checked::before { + content: "\2714"; + display: block; + width: 100%; + height: 100%; + text-align: center; + line-height: 18px; + color: #fff; + font-size: 14px; +} diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index fbdf7fa0..a73be239 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -936,7 +936,7 @@ const AppFramework = (props) => { credentials: "include", }) .then((response) => { - if (response.status !== 200) { + if (response.status !== 200 || response.status !== 202) { console.log("Failed to activate") } @@ -944,7 +944,11 @@ const AppFramework = (props) => { }) .then((responseJson) => { if (responseJson.success === false) { - toast("Failed to activate the app") + var msgString = "Failed to activate the app" + if (responseJson.reason !== undefined) { + msgString += ": " + responseJson.reason + } + toast(msgString) } else { //toast("App activated for your organization! Refresh the page to use the app.") } diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index e3938c65..5e9605f2 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -603,10 +603,10 @@ const ConfigureWorkflow = (props) => { .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { - toast("Failed to activate the app: "+responseJson.reason); - } else { - toast("Failed to activate the app"); - } + toast("Failed to activate the app: "+responseJson.reason); + } else { + toast("Failed to activate the app"); + } } else { toast("App activated for your organization!"); } diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index 402519e7..106cd18d 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -59,7 +59,7 @@ const Files = (props) => { const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); //const alert = useAlert(); - const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1"] + const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"] var upload = ""; const handleKeyDown = (event) => { @@ -395,8 +395,6 @@ const Files = (props) => { setTimeout(() => { getFiles(); }, 1500); - - console.log(responseJson); }) .catch((error) => { toast(error.toString()); @@ -759,21 +757,21 @@ const Files = (props) => { {renderTextBox && { - handleKeyDown(event); - }} - InputProps={{ - style: { - color: "white", - }, - }} - color="primary" - placeholder="File category name" - required - margin="dense" - defaultValue={""} - autoFocus - />}
    + onKeyPress={(event)=>{ + handleKeyDown(event); + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + placeholder="File category name" + required + margin="dense" + defaultValue={""} + autoFocus + />}
    { handleClose(); }} > - Admin + Organisation - + + { + handleClose(); + }} + > + Account + + + { @@ -584,7 +593,6 @@ const Header = (props) => { Use Cases - { @@ -594,16 +602,7 @@ const Header = (props) => { Creator page - - { - handleClose(); - }} - > - Settings - - - + { @@ -617,7 +616,7 @@ const Header = (props) => { - Version: 1.3.3 + Version: 1.3.4 @@ -1052,6 +1051,12 @@ const Header = (props) => { const namesplit = regionsplit[0].split("/"); regiontag = namesplit[namesplit.length - 1]; + + if (regiontag === "california") { + regiontag = "us" + } else if (regiontag === "frankfurt") { + regiontag = "fr" + } } } diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 95020bb1..f9eceb64 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -172,6 +172,7 @@ const OrgHeader = (props) => { : theme.palette.inputColor, maxWidth: 174, maxHeight: 174, + borderRadius: theme.shape.borderRadius, }} onClick={() => { upload.click(); diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 26c566a5..4fd1edbf 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -164,6 +164,7 @@ const ParsedAction = (props) => { expansionModalOpen, setExpansionModalOpen, + apps, setEditorData, setcodedata, setAiQueryModalOpen, @@ -178,6 +179,8 @@ const ParsedAction = (props) => { const [autoCompleting, setAutocompleting] = React.useState(false); + const isIntegration = selectedAction.app_id === "integration" + useEffect(() => { if (setLastSaved !== undefined) { setLastSaved(false) @@ -1087,29 +1090,113 @@ const ParsedAction = (props) => { var authWritten = false; return (
    - 0 ? +
    + {apps.map((app, appIndex) => { + if (app.categories === undefined || app.categories === null || app.categories.length === 0) { + return null + } + + var found = false + var actionname = selectedAction.name.toLowerCase() + if (actionname === "email") { + actionname = "communication" + } + + + for (var key in app.categories) { + if (app.categories[key].toLowerCase() !== actionname) { + continue + } + + found = true + break + } + + if (!found) { + return null + } + + var isAppSelected = false + const paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name") + if (paramIndex > -1) { + // Check the actual value and if it's the same + if (selectedAction.parameters[paramIndex].value === app.name) { + isAppSelected = true + } + } + + return ( +
    { + selectedAction.large_image = app.large_image + + /* + if (cy !== undefined) { + const foundnode = cy.getElementById(selectedAction.id) + if (foundnode !== undefined && foundnode !== null) { + foundnode.data("large_image", app.large_image) + } + } + */ + + if (paramIndex === -1) { + console.log("Couldn't find app_name parameter") + selectedAction.parameters.push({ + name: "app_name", + value: app.name, + autocompleted: false, + }) + } else { + selectedAction.parameters[paramIndex].value = app.name + } + + setSelectedAction(selectedAction) + setUpdate(Math.random()) + + }}> + + + +
    + ) + })} +
    + : null + : + + - - {selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ? -
    - - Select an app you want to use - + Parameters + + + } + {selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ? +
    + + Select an app you want to use + {
    ) : null} + {selectedActionParameters.map((data, count) => { if (data.variant === "") { data.variant = "STATIC_VALUE"; } + if (isIntegration && data.name === "app_name") { + return null + } + // selectedAction.selectedAuthentication = e.target.value // selectedAction.authentication_id = e.target.value.id if ( @@ -1288,7 +1380,7 @@ const ParsedAction = (props) => { var staticcolor = "inherit"; var actioncolor = "inherit"; var varcolor = "inherit"; - var multiline; + var multiline if ( data.multiline !== undefined && data.multiline !== null && @@ -1297,6 +1389,11 @@ const ParsedAction = (props) => { multiline = true; } + // make data.value from array to comma separated string if it is an array + if (data.value !== undefined && data.value !== null && Array.isArray(data.value)) { + data.value = data.value.join(",") + } + if ( data.value !== undefined && data.value !== null && @@ -2003,6 +2100,13 @@ const ParsedAction = (props) => { changeActionParameter(e, count, data); } + var multi = false + if (selectedActionParameters[count].multiselect !== undefined && selectedActionParameters[count].multiselect !== null && selectedActionParameters[count].multiselect === true) { + multi = true + + selectedActionParameters[count].value = selectedActionParameters[count].value.split(",") + } + datafield = ( { - console.log("VAL: ", event.target.value) - console.log("App: ", selectedApp) + console.log("VAL: ", event.target.value) + console.log("App: ", selectedApp) const newversion = selectedApp.versions.find( (tmpApp) => tmpApp.version == event.target.value ); @@ -3051,15 +3162,6 @@ const ParsedAction = (props) => { ) : null}
    -
    Name @@ -3308,10 +3410,10 @@ const ParsedAction = (props) => { )} {selectedApp.name !== undefined && - selectedAction.authentication !== null && - selectedAction.authentication !== undefined && - selectedAction.authentication.length === 0 && - requiresAuthentication ? ( + selectedAction.authentication !== null && + selectedAction.authentication !== undefined && + selectedAction.authentication.length === 0 && + requiresAuthentication ? (
    {
    ) : null} - {showEnvironment !== undefined && showEnvironment && environments.length > 1 ? ( + {showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
    Environment { + setSelectedRegion(e.target.value); + setRegionChangeModalOpen(false); + }} + > + {regions.map((region) => { + // Set the default region if selectedOrganization.region is not set + if (selectedOrganization.region.length === 0) { + 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 ( + + {region} + + ); + } else { + // Otherwise, render a regular MenuItem + return {region}; + } + })} + + + + + + +
    + ); + }; const get2faCode = (userId) => { fetch(`${globalUrl}/api/v1/users/${userId}/get2fa`, { @@ -324,190 +437,249 @@ const Admin = (props) => { ] */ - //const alert = useAlert(); - const handleStatusChange = (event) => { - const { value } = event.target; - setSelectedStatus(value); + //const alert = useAlert(); + const handleStatusChange = (event) => { + const { value } = event.target; + setSelectedStatus(value); - - handleEditOrg( - "", - "", - selectedOrganization.id, - "", - {}, - {}, - value.length === 0 ? ["none"] : value, - ) - } + handleEditOrg( + "", + "", + selectedOrganization.id, + "", + {}, + {}, + value.length === 0 ? ["none"] : value, + ); + }; - // Basically just a simple way to get a generated email - // This also may help understand how to communicate with users - // both inside and outside Shuffle - // This could also be generated on the backend - const mailsendingButton = (org) => { - if (org === undefined || org === null) { - return "" - } + // Basically just a simple way to get a generated email + // This also may help understand how to communicate with users + // both inside and outside Shuffle + // This could also be generated on the backend + const mailsendingButton = (org) => { + if (org === undefined || org === null) { + return ""; + } - if (users.length === 0) { - return "" - } + if (users.length === 0) { + return ""; + } - // 1 mail based on users that have only apps - // Another based on those doing workflows - // Another based on those trying usecases(?) or templates - // - // Start based on edr, siem & ticketing - // Talk about enrichment? - // Check suggested usecases - // Check suggested workflows - var your_apps = "- Connecting " + // 1 mail based on users that have only apps + // Another based on those doing workflows + // Another based on those trying usecases(?) or templates + // + // Start based on edr, siem & ticketing + // Talk about enrichment? + // Check suggested usecases + // Check suggested workflows + var your_apps = "- Connecting "; - var subject_add = 0 - var subject = "POC to automate " + var subject_add = 0; + var subject = "POC to automate "; - if (org.security_framework !== undefined && org.security_framework !== null) { - if (org.security_framework.cases.name !== undefined && org.security_framework.cases.name !== null && org.security_framework.cases.name !== "") { - your_apps += org.security_framework.cases.name.replace("_", " ", -1).replace(" API", "", -1) + ", " + if ( + org.security_framework !== undefined && + org.security_framework !== null + ) { + if ( + org.security_framework.cases.name !== undefined && + org.security_framework.cases.name !== null && + org.security_framework.cases.name !== "" + ) { + your_apps += + org.security_framework.cases.name + .replace("_", " ", -1) + .replace(" API", "", -1) + ", "; - if (subject_add < 2) { - if (subject_add === 1) { - subject += " and " - } + if (subject_add < 2) { + if (subject_add === 1) { + subject += " and "; + } - subject_add += 1 - subject += org.security_framework.cases.name.replace("_", " ", -1).replace(" API", "", -1) - } - } + subject_add += 1; + subject += org.security_framework.cases.name + .replace("_", " ", -1) + .replace(" API", "", -1); + } + } - if (org.security_framework.siem.name !== undefined && org.security_framework.siem.name !== null && org.security_framework.siem.name !== "") { - your_apps += org.security_framework.siem.name.replace("_", " ", -1).replace(" API", "", -1) + ", " - if (subject_add < 2) { - if (subject_add === 1) { - subject += " and " - } + if ( + org.security_framework.siem.name !== undefined && + org.security_framework.siem.name !== null && + org.security_framework.siem.name !== "" + ) { + your_apps += + org.security_framework.siem.name + .replace("_", " ", -1) + .replace(" API", "", -1) + ", "; + if (subject_add < 2) { + if (subject_add === 1) { + subject += " and "; + } - subject_add += 1 - subject += org.security_framework.siem.name.replace("_", " ", -1).replace(" API", "", -1) - } - } + subject_add += 1; + subject += org.security_framework.siem.name + .replace("_", " ", -1) + .replace(" API", "", -1); + } + } - if (org.security_framework.communication.name !== undefined && org.security_framework.communication.name !== null && org.security_framework.communication.name !== "") { - your_apps += org.security_framework.communication.name.replace("_", " ", -1).replace(" API", "", -1) + ", " + if ( + org.security_framework.communication.name !== undefined && + org.security_framework.communication.name !== null && + org.security_framework.communication.name !== "" + ) { + your_apps += + org.security_framework.communication.name + .replace("_", " ", -1) + .replace(" API", "", -1) + ", "; - if (subject_add < 2) { - if (subject_add === 1) { - subject += " and " - } + if (subject_add < 2) { + if (subject_add === 1) { + subject += " and "; + } - subject_add += 1 - subject += org.security_framework.communication.name.replace("_", " ", -1).replace(" API", "", -1) - } - } + subject_add += 1; + subject += org.security_framework.communication.name + .replace("_", " ", -1) + .replace(" API", "", -1); + } + } - if (org.security_framework.edr.name !== undefined && org.security_framework.edr.name !== null && org.security_framework.edr.name !== "") { - your_apps += org.security_framework.edr.name.replace("_", " ", -1).replace(" API", "", -1) + ", " + if ( + org.security_framework.edr.name !== undefined && + org.security_framework.edr.name !== null && + org.security_framework.edr.name !== "" + ) { + your_apps += + org.security_framework.edr.name + .replace("_", " ", -1) + .replace(" API", "", -1) + ", "; - if (subject_add < 2) { - if (subject_add === 1) { - subject += " and " - } + if (subject_add < 2) { + if (subject_add === 1) { + subject += " and "; + } - subject_add += 1 - subject += org.security_framework.edr.name.replace("_", " ", -1).replace(" API", "", -1) - } - } + subject_add += 1; + subject += org.security_framework.edr.name + .replace("_", " ", -1) + .replace(" API", "", -1); + } + } - if (org.security_framework.intel.name !== undefined && org.security_framework.intel.name !== null && org.security_framework.intel.name !== "") { - your_apps += org.security_framework.intel.name.replace("_", " ", -1).replace(" API", "", -1) + ", " + if ( + org.security_framework.intel.name !== undefined && + org.security_framework.intel.name !== null && + org.security_framework.intel.name !== "" + ) { + your_apps += + org.security_framework.intel.name + .replace("_", " ", -1) + .replace(" API", "", -1) + ", "; - if (subject_add < 2) { - if (subject_add === 1) { - subject += " and " - } + if (subject_add < 2) { + if (subject_add === 1) { + subject += " and "; + } - subject_add += 1 - subject += org.security_framework.intel.name.replace("_", " ", -1).replace(" API", "", -1) - } - } + subject_add += 1; + subject += org.security_framework.intel.name + .replace("_", " ", -1) + .replace(" API", "", -1); + } + } + // Remove comma + //subject += "?" + your_apps = your_apps.substring(0, your_apps.length - 2); + } - // Remove comma - //subject += "?" - your_apps = your_apps.substring(0, your_apps.length - 2) - } + // Add usecases they may not have tried (from recommendations): org.priorities where item type is usecase + var usecases = "- Building usecases like "; + const active_usecase = org.priorities.filter( + (item) => item.type === "usecase" && item.active === true, + ); + if (active_usecase.length > 0) { + for (var i = 0; i < active_usecase.length; i++) { + if (active_usecase[i].name.includes("Suggested Usecase: ")) { + usecases += + active_usecase[i].name.replace("Suggested Usecase: ", "", -1) + + ", "; + } else { + usecases += active_usecase[i].name + ", "; + } + } + usecases = usecases.substring(0, usecases.length - 2); + } - // Add usecases they may not have tried (from recommendations): org.priorities where item type is usecase - var usecases = "- Building usecases like " - const active_usecase = org.priorities.filter((item) => item.type === "usecase" && item.active === true) - if (active_usecase.length > 0) { - for (var i = 0; i < active_usecase.length; i++) { - if (active_usecase[i].name.includes("Suggested Usecase: ")) { - usecases += active_usecase[i].name.replace("Suggested Usecase: ", "", -1) + ", " - } else { - usecases += active_usecase[i].name + ", " - } - } + if (your_apps.length <= 15) { + your_apps = ""; + } - usecases = usecases.substring(0, usecases.length - 2) - } + if (usecases.length <= 30) { + usecases = ""; + } - if (your_apps.length <= 15) { - your_apps = "" - } + var workflow_amount = "a few"; + var admins = ""; - if (usecases.length <= 30) { - usecases = "" - } + // Loop users + var lastLogin = 0; + for (var i = 0; i < users.length; i++) { + if (users[i].username.includes("shuffler")) { + continue; + } - var workflow_amount = "a few" - var admins = "" + if (users[i].role === "admin") { + admins += users[i].username + ","; + } - // Loop users - var lastLogin = 0 - for (var i = 0; i < users.length; i++) { - if (users[i].username.includes("shuffler")) { - continue - } + const data = users[i]; + for (var i = 0; i < data.login_info.length; i++) { + if (data.login_info[i].timestamp > lastLogin) { + lastLogin = data.login_info[i].timestamp; + } + } + } - if (users[i].role === "admin") { - admins += users[i].username + "," - } + // Remove last comma + admins = admins.substring(0, admins.length - 1); - const data = users[i] - for (var i = 0; i < data.login_info.length; i++) { - if (data.login_info[i].timestamp > lastLogin) { - lastLogin = data.login_info[i].timestamp - } - } - } + if (your_apps.length > 5) { + your_apps += "%0D%0A"; + } + if (usecases.length > 5) { + usecases += "%0D%0A"; + } - // Remove last comma - admins = admins.substring(0, admins.length - 1) + // Get drift username from userdata.username before @ in email + const username = userdata.username.substring( + 0, + userdata.username.indexOf("@"), + ); - if (your_apps.length > 5) { - your_apps += "%0D%0A" - } + // Check if timestamp is more than 2 weeks ago and add "a while back" to the message + const timeComparison = 1209600; + const extra_timestamp_text = + lastLogin === 0 + ? 0 + : Date.now() / 1000 - lastLogin > timeComparison + ? " a while back" + : ""; + console.log("LAST LOGIN: " + lastLogin, extra_timestamp_text); - if (usecases.length > 5) { - usecases += "%0D%0A" - } + // Check if cloud sync is active, and if so, add a message about it + const cloudSyncInfo = + selectedOrganization.cloud_sync === true + ? "- Scale your onprem installation" + : ""; - // Get drift username from userdata.username before @ in email - const username = userdata.username.substring(0, userdata.username.indexOf("@")) - - // Check if timestamp is more than 2 weeks ago and add "a while back" to the message - const timeComparison = 1209600 - const extra_timestamp_text = lastLogin === 0 ? 0 : (Date.now()/1000 - lastLogin) > timeComparison ? " a while back" : "" - console.log("LAST LOGIN: " + lastLogin, extra_timestamp_text) - - // Check if cloud sync is active, and if so, add a message about it - const cloudSyncInfo = selectedOrganization.cloud_sync === true ? "- Scale your onprem installation" : "" - - var body = `Hey,%0D%0A%0D%0AI noticed you tried to use Shuffle${extra_timestamp_text}, and thought you may be interested in a POC. It looks like you have ${workflow_amount} workflows made, but it still doesn't look like you are getting what you wanted out of Shuffle. If you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A + var body = `Hey,%0D%0A%0D%0AI noticed you tried to use Shuffle${extra_timestamp_text}, and thought you may be interested in a POC. It looks like you have ${workflow_amount} workflows made, but it still doesn't look like you are getting what you wanted out of Shuffle. If you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A Some of the things we can help with:%0D%0A ${your_apps} @@ -516,18 +688,17 @@ ${usecases} - Multi-Tenancy and creating special usecases%0D%0A ${cloudSyncInfo}%0D%0A -If you're interested, please let me know a time that works for you, or set up a call here: https://drift.me/${username}` - - return `mailto:${admins}?bcc=frikky@shuffler.io,binu@shuffler.io&subject=${subject}&body=${body}` - } +If you're interested, please let me know a time that works for you, or set up a call here: https://drift.me/${username}`; + return `mailto:${admins}?bcc=frikky@shuffler.io,binu@shuffler.io&subject=${subject}&body=${body}`; + }; const changeDistribution = (data) => { - //changeDistributed(data, !isDistributed) - console.log("Should change distribution to be shared among suborgs") - - editAuthenticationConfig(data.id, "suborg_distribute") - } + //changeDistributed(data, !isDistributed) + console.log("Should change distribution to be shared among suborgs"); + + editAuthenticationConfig(data.id, "suborg_distribute"); + }; const deleteAuthentication = (data) => { toast("Deleting auth " + data.label); @@ -553,7 +724,7 @@ If you're interested, please let me know a time that works for you, or set up a }, 1000); //toast("Successfully deleted authentication!") } - }) + }), ) .catch((error) => { console.log("Error in userdata: ", error); @@ -590,19 +761,24 @@ If you're interested, please let me know a time that works for you, or set up a }, 1500); //toast("Successfully stopped schedule!") } - }) + }), ) .catch((error) => { console.log("Error in userdata: ", error); }); }; - - if (userdata.support === true && selectedOrganization.id !== "" && selectedOrganization.id !== undefined && selectedOrganization.id !== null && selectedOrganization.id !== userdata.active_org.id) { - toast("Refreshing window to fix org support access") - window.location.reload() - return null - } + if ( + userdata.support === true && + selectedOrganization.id !== "" && + selectedOrganization.id !== undefined && + selectedOrganization.id !== null && + selectedOrganization.id !== userdata.active_org.id + ) { + toast("Refreshing window to fix org support access"); + window.location.reload(); + return null; + } const handleVerify2FA = (userId, code) => { const data = { @@ -681,7 +857,7 @@ If you're interested, please let me know a time that works for you, or set up a } else { console.log("Cloud sync fail?"); toast( - "Failed stopping sync. Try again, and contact support if this persists." + "Failed stopping sync. Try again, and contact support if this persists.", ); } @@ -748,7 +924,7 @@ If you're interested, please let me know a time that works for you, or set up a } else { toast("Cloud Syncronization successfully set up!"); setOrgSyncResponse( - "Successfully started syncronization. Cloud features you now have access to can be seen below." + "Successfully started syncronization. Cloud features you now have access to can be seen below.", ); } @@ -783,22 +959,21 @@ If you're interested, please let me know a time that works for you, or set up a .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - // Check if .reason exists - if (responseJson.reason !== undefined) { - toast("Failed changing authentication: " + responseJson.reason); - } else { - toast("Failed changing authentication"); - } + // Check if .reason exists + if (responseJson.reason !== undefined) { + toast("Failed changing authentication: " + responseJson.reason); + } else { + toast("Failed changing authentication"); + } } else { //toast("Successfully password!") setSelectedUserModalOpen(false); getAppAuthentication(); - - setSelectedAuthentication({}); - setSelectedAuthenticationModalOpen(false); + setSelectedAuthentication({}); + setSelectedAuthenticationModalOpen(false); } - }) + }), ) .catch((error) => { toast("Err: " + error.toString()); @@ -812,9 +987,8 @@ If you're interested, please let me know a time that works for you, or set up a image, defaults, sso_config, - lead_info, + lead_info, ) => { - const data = { name: name, description: description, @@ -822,7 +996,7 @@ If you're interested, please let me know a time that works for you, or set up a image: image, defaults: defaults, sso_config: sso_config, - lead_info: lead_info, + lead_info: lead_info, }; const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; @@ -842,11 +1016,15 @@ If you're interested, please let me know a time that works for you, or set up a if (responseJson["success"] === false) { toast("Failed updating org: ", responseJson.reason); } else { - if (lead_info === undefined || lead_info === null || lead_info === []) { - toast("Successfully edited org!"); - } + if ( + lead_info === undefined || + lead_info === null || + lead_info === [] + ) { + toast("Successfully edited org!"); + } } - }) + }), ) .catch((error) => { toast("Err: " + error.toString()); @@ -856,8 +1034,11 @@ If you're interested, please let me know a time that works for you, or set up a const editAuthenticationConfig = (id, parentAction) => { const data = { id: id, - action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere", - } + action: + parentAction !== undefined && parentAction !== null + ? parentAction + : "assign_everywhere", + }; const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config"; @@ -883,7 +1064,7 @@ If you're interested, please let me know a time that works for you, or set up a getAppAuthentication(); }, 1000); } - }) + }), ) .catch((error) => { toast("Err: " + error.toString()); @@ -915,9 +1096,7 @@ If you're interested, please let me know a time that works for you, or set up a toast("Failed creating suborg. Please try again"); } } else { - toast( - "Successfully created suborg. Reloading in 3 seconds!" - ); + toast("Successfully created suborg. Reloading in 3 seconds!"); setSelectedUserModalOpen(false); setTimeout(() => { @@ -927,7 +1106,7 @@ If you're interested, please let me know a time that works for you, or set up a setOrgName(""); setModalOpen(false); - }) + }), ) .catch((error) => { toast("Err: " + error.toString()); @@ -961,7 +1140,7 @@ If you're interested, please let me know a time that works for you, or set up a toast("Successfully updated password!"); setSelectedUserModalOpen(false); } - }) + }), ) .catch((error) => { toast("Err: " + error.toString()); @@ -990,8 +1169,10 @@ If you're interested, please let me know a time that works for you, or set up a .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { toast("Failed to deactivate user: " + responseJson.reason); - } else if (responseJson.success === false) { - toast("Failed to deactivate user. Please contact support@shuffler.io if this persists.") + } else if (responseJson.success === false) { + toast( + "Failed to deactivate user. Please contact support@shuffler.io if this persists.", + ); } else { toast("Changed activation for user " + data.id); } @@ -1002,26 +1183,29 @@ If you're interested, please let me know a time that works for you, or set up a }); }; - - const handleGetOrg = (orgId) => { - - if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { - const urlSearchParams = new URLSearchParams(window.location.search); - const params = Object.fromEntries(urlSearchParams.entries()); - const foundorgid = params["org_id"]; - if (foundorgid !== undefined && foundorgid !== null) { - orgId = foundorgid; - } - } + if ( + serverside !== true && + window.location.search !== undefined && + window.location.search !== null + ) { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundorgid = params["org_id"]; + if (foundorgid !== undefined && foundorgid !== null) { + orgId = foundorgid; + } + } if (orgId.length === 0) { - toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); + toast( + "Organization ID not defined. Please contact us on https://shuffler.io if this persists logout.", + ); return; } // Just use this one? - + fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { method: "GET", credentials: "include", @@ -1037,7 +1221,9 @@ If you're interested, please let me know a time that works for you, or set up a }) .then((responseJson) => { if (responseJson["success"] === false) { - toast("Failed getting your org. If this persists, please contact support."); + toast( + "Failed getting your org. If this persists, please contact support.", + ); } else { if ( responseJson.sync_features === undefined || @@ -1046,64 +1232,67 @@ If you're interested, please let me know a time that works for you, or set up a responseJson.sync_features = {}; } - if (responseJson.lead_info !== undefined && responseJson.lead_info !== null) { - var leads = [] - if (responseJson.lead_info.contacted) { - leads.push("contacted") - } + if ( + responseJson.lead_info !== undefined && + responseJson.lead_info !== null + ) { + var leads = []; + if (responseJson.lead_info.contacted) { + leads.push("contacted"); + } - if (responseJson.lead_info.customer) { - leads.push("customer") - } + if (responseJson.lead_info.customer) { + leads.push("customer"); + } - if (responseJson.lead_info.old_customer) { - leads.push("old customer") - } + if (responseJson.lead_info.old_customer) { + leads.push("old customer"); + } - if (responseJson.lead_info.old_lead) { - leads.push("old lead") - } + if (responseJson.lead_info.old_lead) { + leads.push("old lead"); + } - if (responseJson.lead_info.tech_partner) { - leads.push("tech partner") - } + if (responseJson.lead_info.tech_partner) { + leads.push("tech partner"); + } - if (responseJson.lead_info.creator) { - leads.push("creator") - } + if (responseJson.lead_info.creator) { + leads.push("creator"); + } - if (responseJson.lead_info.opensource) { - leads.push("open source") - } + if (responseJson.lead_info.opensource) { + leads.push("open source"); + } - if (responseJson.lead_info.demo_done) { - leads.push("demo done") - } + if (responseJson.lead_info.demo_done) { + leads.push("demo done"); + } - if (responseJson.lead_info.pov) { - leads.push("pov") - } + if (responseJson.lead_info.pov) { + leads.push("pov"); + } - if (responseJson.lead_info.lead) { - leads.push("lead") - } + if (responseJson.lead_info.lead) { + leads.push("lead"); + } - if (responseJson.lead_info.student) { - leads.push("student") - } + if (responseJson.lead_info.student) { + leads.push("student"); + } - if (responseJson.lead_info.internal) { - leads.push("internal") - } + if (responseJson.lead_info.internal) { + leads.push("internal"); + } - if (responseJson.lead_info.sub_org) { - leads.push("sub_org") - } + if (responseJson.lead_info.sub_org) { + leads.push("sub_org"); + } - setSelectedStatus(leads) - } + setSelectedStatus(leads); + } - setSelectedOrganization(responseJson) + setSelectedOrganization(responseJson); var lists = { active: { triggers: [], @@ -1134,12 +1323,13 @@ If you're interested, please let me know a time that works for you, or set up a }; const handleGetSubOrgs = (orgId) => { - if (orgId.length === 0) { - toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); + toast( + "Organization ID not defined. Please contact us on https://shuffler.io if this persists logout.", + ); return; } - + fetch(`${globalUrl}/api/v1/orgs/${orgId}/suborgs`, { method: "GET", credentials: "include", @@ -1147,76 +1337,80 @@ If you're interested, please let me know a time that works for you, or set up a "Content-Type": "application/json", }, }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to fetch sub organizations'); - } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - //toast("Failed getting your org. If this persists, please contact support."); - } else { - const { subOrgs, parentOrg } = responseJson; - setSubOrgs(subOrgs); - setParentOrg(parentOrg); - } - }) + .then((response) => { + if (!response.ok) { + throw new Error("Failed to fetch sub organizations"); + } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + //toast("Failed getting your org. If this persists, please contact support."); + } else { + const { subOrgs, parentOrg } = responseJson; + setSubOrgs(subOrgs); + setParentOrg(parentOrg); + } + }) .catch((error) => { console.log("Error getting sub orgs: ", error); //toast("Error getting sub organizations"); }); }; - const handleClickChangeOrg = (orgId) => { - // Don't really care about the logout - //name: org.name, - //orgId = "asd" - const data = { - org_id: orgId, - } - - localStorage.setItem("globalUrl", "") - localStorage.setItem("getting_started_sidebar", "open"); - - fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { - mode: 'cors', - credentials: 'include', - crossDomain: true, - method: 'POST', - body: JSON.stringify(data), - withCredentials: true, - headers: { - 'Content-Type': 'application/json; charset=utf-8', - }, - }) - .then(function(response) { - if (response.status !== 200) { - console.log("Error in response") - } - - return response.json(); - }).then(function(responseJson) { - if (responseJson.success === true) { - if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { - localStorage.setItem("globalUrl", responseJson.region_url) - //globalUrl = responseJson.region_url - } - - setTimeout(() => { - window.location.reload() - }, 2000) - toast("Successfully changed active organization - refreshing!") - } else { - toast("Failed changing org: "+responseJson.reason) - } - }) - .catch(error => { - console.log("error changing: ", error) - //removeCookie("session_token", {path: "/"}) - }) - } + const handleClickChangeOrg = (orgId) => { + // Don't really care about the logout + //name: org.name, + //orgId = "asd" + const data = { + org_id: orgId, + }; + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify(data), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + if ( + responseJson.region_url !== undefined && + responseJson.region_url !== null && + responseJson.region_url.length > 0 + ) { + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + setTimeout(() => { + window.location.reload(); + }, 2000); + toast("Successfully changed active organization - refreshing!"); + } else { + toast("Failed changing org: " + responseJson.reason); + } + }) + .catch((error) => { + console.log("error changing: ", error); + //removeCookie("session_token", {path: "/"}) + }); + }; const inviteUser = (data) => { //console.log("INPUT: ", data); @@ -1243,21 +1437,27 @@ If you're interested, please let me know a time that works for you, or set up a response.json().then((responseJson) => { if (responseJson["success"] === false) { setLoginInfo("Error: " + responseJson.reason); - toast("Failed to send email (2). Please try again and contact support if this persists.") + toast( + "Failed to send email (2). Please try again and contact support if this persists.", + ); } else { setLoginInfo(""); setModalOpen(false); setTimeout(() => { getUsers(); }, 1000); - - toast("Invite sent! They will show up in the list when they have accepted the invite.") + + toast( + "Invite sent! They will show up in the list when they have accepted the invite.", + ); } - }) + }), ) .catch((error) => { console.log("Error in userdata: ", error); - toast("Failed to send email. Please try again and contact support if this persists.") + toast( + "Failed to send email. Please try again and contact support if this persists.", + ); }); }; @@ -1289,7 +1489,7 @@ If you're interested, please let me know a time that works for you, or set up a getUsers(); }, 1000); } - }) + }), ) .catch((error) => { console.log("Error in userdata: ", error); @@ -1343,7 +1543,7 @@ If you're interested, please let me know a time that works for you, or set up a getEnvironments(); }, 1500); } - }) + }), ) .catch((error) => { console.log("Error in backend data: ", error); @@ -1370,7 +1570,7 @@ If you're interested, please let me know a time that works for you, or set up a setModalOpen(false); getEnvironments(); } - }) + }), ) .catch((error) => { console.log("Error when deleting: ", error); @@ -1378,14 +1578,11 @@ If you're interested, please let me know a time that works for you, or set up a }; const rerunCloudWorkflows = (environment) => { - toast("Starting execution reruns. This can run in the background.") - fetch( - `${globalUrl}/api/v1/environments/${environment.id}/rerun`, - { - method: "GET", - credentials: "include", - } - ) + toast("Starting execution reruns. This can run in the background."); + fetch(`${globalUrl}/api/v1/environments/${environment.id}/rerun`, { + method: "GET", + credentials: "include", + }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for apps :O!"); @@ -1409,14 +1606,16 @@ If you're interested, please let me know a time that works for you, or set up a const abortEnvironmentWorkflows = (environment) => { //console.log("Aborting all workflows started >10 minutes ago, not finished"); - toast("Clearing the queue - this may take some time. A new will show up when finished.") + toast( + "Clearing the queue - this may take some time. A new will show up when finished.", + ); fetch( `${globalUrl}/api/v1/environments/${environment.id}/stop?deleteall=true`, { method: "GET", credentials: "include", - } + }, ) .then((response) => { if (response.status !== 200) { @@ -1424,9 +1623,9 @@ If you're interested, please let me know a time that works for you, or set up a toast("Failed aborting dangling workflows"); return; } else { - toast("Successfully cleared the queue") + toast("Successfully cleared the queue"); - getEnvironments() + getEnvironments(); } return response.json(); @@ -1506,7 +1705,7 @@ If you're interested, please let me know a time that works for you, or set up a setModalOpen(false); getEnvironments(); } - }) + }), ) .catch((error) => { console.log("Error when deleting: ", error); @@ -1541,7 +1740,7 @@ If you're interested, please let me know a time that works for you, or set up a setModalOpen(false); getEnvironments(); } - }) + }), ) .catch((error) => { console.log("Error in userdata: ", error); @@ -1550,6 +1749,46 @@ If you're interested, please let me know a time that works for you, or set up a var localData = ""; + const handleDeleteAccount = (userID) => { + if (userID === undefined || userID === null || userID === "") { + return; + } + + const url = `${globalUrl}/api/v1/users/${userID}/remove`; + fetch(url, { + mode: "cors", + method: "DELETE", + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => response.json()) + .then((data) => { + if (data.success) { + toast.success( + "Deleted their account. Would reload users in a few seconds.", + ); + + setTimeout(() => { + getUsers(); + }); + } else { + toast.error(`${data.reason}`); + } + }) + .catch((error) => { + console.error( + "There was a problem with deleting the account. Please try again:", + error, + ); + toast.error( + "There was a problem with the delete request. Please try again", + ); + }); + }; const getSchedules = () => { fetch(globalUrl + "/api/v1/workflows/schedules", { @@ -1627,18 +1866,30 @@ If you're interested, please let me know a time that works for you, or set up a .then((responseJson) => { setEnvironments(responseJson); - // Helper info for users in case they have a large queue and don't know about queue flushing - if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) { - for (var i = 0; i < responseJson.length; i++) { - const env = responseJson[i]; + // Helper info for users in case they have a large queue and don't know about queue flushing + if ( + responseJson !== undefined && + responseJson !== null && + responseJson.length > 0 + ) { + for (var i = 0; i < responseJson.length; i++) { + const env = responseJson[i]; - // Check if queuesize is too large - if (env.queue !== undefined && env.queue !== null && env.queue > 100) { - toast("Queue size for " + env.name + " is very large. We recommend you to reduce it by flushing the queue before continuing."); - break - } - } - } + // Check if queuesize is too large + if ( + env.queue !== undefined && + env.queue !== null && + env.queue > 100 + ) { + toast( + "Queue size for " + + env.name + + " is very large. We recommend you to reduce it by flushing the queue before continuing.", + ); + break; + } + } + } }) .catch((error) => { toast(error.toString()); @@ -1737,7 +1988,7 @@ If you're interested, please let me know a time that works for you, or set up a const admin_views = { 0: "organization", 1: "cloud_sync", - 2: "priorities", + 2: "priorities", 3: "billing", 4: "branding", }; @@ -1777,7 +2028,7 @@ If you're interested, please let me know a time that works for you, or set up a navigate(`/admin?tab=${views[newValue]}`); setModalUser({}); - } + }; if (firstRequest) { setFirstRequest(false); @@ -1800,26 +2051,26 @@ If you're interested, please let me know a time that works for you, or set up a const adminTab = params["admin_tab"]; if (adminTab !== null && adminTab !== undefined) { - for (var key in Object.keys(admin_views)) { + for (var key in Object.keys(admin_views)) { const value = admin_views[key]; if (value === adminTab) { - setAdminTab(parseInt(key)); + setAdminTab(parseInt(key)); setConfig("", 0); break; } } - } else { - const foundTab = params["tab"]; - if (foundTab !== null && foundTab !== undefined) { - for (var key in Object.keys(views)) { - const value = views[key]; - if (value === foundTab) { - setConfig("", key); - break; - } - } - } - } + } else { + const foundTab = params["tab"]; + if (foundTab !== null && foundTab !== undefined) { + for (var key in Object.keys(views)) { + const value = views[key]; + if (value === foundTab) { + setConfig("", key); + break; + } + } + } + } } } @@ -1871,11 +2122,11 @@ If you're interested, please let me know a time that works for you, or set up a .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { toast("Failed setting user: " + responseJson.reason); - } else if (responseJson.success === false) { - toast("Failed to update user") + } else if (responseJson.success === false) { + toast("Failed to update user"); } else { //toast("Set the user field " + field + " to " + value); - toast("Successfully updated user field " + field) + toast("Successfully updated user field " + field); if (field !== "suborgs") { setSelectedUserModalOpen(false); @@ -1891,7 +2142,7 @@ If you're interested, please let me know a time that works for you, or set up a const userId = user.id; const data = { user_id: userId }; - toast("Generating new API key") + toast("Generating new API key"); var fetchdata = { method: "POST", @@ -1900,13 +2151,13 @@ If you're interested, please let me know a time that works for you, or set up a Accept: "application/json", }, credentials: "include", - } + }; if (userId === userdata.id) { - fetchdata.method = "GET" - } else { - fetchdata.body = JSON.stringify(data) - } + fetchdata.method = "GET"; + } else { + fetchdata.body = JSON.stringify(data); + } fetch(globalUrl + "/api/v1/generateapikey", fetchdata) .then((response) => { @@ -1948,62 +2199,80 @@ If you're interested, please let me know a time that works for you, or set up a > - Edit authentication for {selectedAuthentication.app.name.replaceAll("_", " ")} ( + Edit authentication for{" "} + {selectedAuthentication.app.name.replaceAll("_", " ")} ( {selectedAuthentication.label}) - - You can not see the previous values for an authentication while editing. This is to keep your data secure. You can overwrite one- or multiple fields at a time. - + + You can not see the previous values for an authentication while + editing. This is to keep your data secure. You can overwrite one- or + multiple fields at a time. + - - Authentication Label - - { - selectedAuthentication.label = e.target.value - }} - /> + + Authentication Label + + { + selectedAuthentication.label = e.target.value; + }} + /> - - {selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ? -
    - - Only the name and url can be modified for Oauth2/OpenID connect. Please remake the authentication if you want to change the other fields like Client ID, Secret, Scopes etc. - -
    - : null } + + {selectedAuthentication.type === "oauth" || + selectedAuthentication.type === "oauth2" || + selectedAuthentication.type === "oauth2-app" ? ( +
    + + Only the name and url can be modified for Oauth2/OpenID connect. + Please remake the authentication if you want to change the other + fields like Client ID, Secret, Scopes etc. + +
    + ) : null} - {selectedAuthentication.fields.map((data, index) => { - var fieldname = data.key.replaceAll("_", " ") - if (fieldname.endsWith(" basic")) { - fieldname = fieldname.substring(0, fieldname.length - 6) - } - - if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") { - if (selectedAuthentication.fields[index].key !== "url") { - return null - } - } + {selectedAuthentication.fields.map((data, index) => { + var fieldname = data.key.replaceAll("_", " "); + if (fieldname.endsWith(" basic")) { + fieldname = fieldname.substring(0, fieldname.length - 6); + } + if ( + selectedAuthentication.type === "oauth" || + selectedAuthentication.type === "oauth2" || + selectedAuthentication.type === "oauth2-app" + ) { + if (selectedAuthentication.fields[index].key !== "url") { + return null; + } + } //console.log("DATA: ", data, selectedAuthentication) return ( @@ -2052,15 +2321,15 @@ If you're interested, please let me know a time that works for you, or set up a style={{ borderRadius: "0px" }} onClick={() => { var error = false; - var fails = 0 + var fails = 0; for (var key in authenticationFields) { const item = authenticationFields[key]; if (item.value.length === 0) { - fails += 1 + fails += 1; console.log("ITEM: ", item); //var currentnode = cy.getElementById(data.id) var textfield = document.getElementById( - `authentication-${key}` + `authentication-${key}`, ); if (textfield !== null && textfield !== undefined) { console.log("HANDLE ERROR FOR KEY ", key); @@ -2069,13 +2338,17 @@ If you're interested, please let me know a time that works for you, or set up a } } - if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") { - selectedAuthentication.fields = [] - } + if ( + selectedAuthentication.type === "oauth" || + selectedAuthentication.type === "oauth2" || + selectedAuthentication.type === "oauth2-app" + ) { + selectedAuthentication.fields = []; + } if (error && fails === authenticationFields.length) { - toast("Updating auth with new name only") - saveAuthentication(selectedAuthentication); + toast("Updating auth with new name only"); + saveAuthentication(selectedAuthentication); } else { toast("Saving new version of this authentication"); selectedAuthentication.fields = authenticationFields; @@ -2144,20 +2417,16 @@ If you're interested, please let me know a time that works for you, or set up a ) : null; - - - - const editUserModal = ( { setSelectedUserModalOpen(false); - setImage2FA(""); - setValue2FA(""); - setSecret2FA(""); - setShow2faSetup(false); + setImage2FA(""); + setValue2FA(""); + setSecret2FA(""); + setShow2faSetup(false); }} PaperProps={{ style: { @@ -2305,6 +2574,80 @@ If you're interested, please let me know a time that works for you, or set up a ? "Disable 2FA" : "Enable 2FA"} + + {isCloud && userdata.support && selectedUser.id != userdata.id ? ( + + ) : null} + + {showDeleteAccountTextbox ? ( + { + setDeleteAccountText(e.target.value); + }} + /> + ) : null}
    {show2faSetup ? (
    { const [expanded, setExpanded] = React.useState(false); - const [showEdit, setShowEdit] = React.useState(false); - const [newValue, setNewValue] = React.useState(-100); + const [showEdit, setShowEdit] = React.useState(false); + const [newValue, setNewValue] = React.useState(-100); const primary = props.data.primary; const secondary = props.data.secondary; const primaryIcon = props.data.icon; - const secondaryIcon = props.data.active ? + const secondaryIcon = props.data.active ? ( - : + ) : ( + ); - const submitFeatureEdit = (sync_features) => { - if (!userdata.support) { - console.log("User does not have support access and can't edit features"); - return - } + const submitFeatureEdit = (sync_features) => { + if (!userdata.support) { + console.log( + "User does not have support access and can't edit features", + ); + return; + } - sync_features.editing = true - const data = { - org_id: selectedOrganization.id, - sync_features: sync_features, - }; + sync_features.editing = true; + const data = { + org_id: selectedOrganization.id, + sync_features: sync_features, + }; - 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 enableFeature = () => { - console.log("Enabling "+primary) + const enableFeature = () => { + console.log("Enabling " + primary); - console.log(selectedOrganization.sync_features) - // Check if primary is in sync_features - var tmpprimary = primary.replaceAll(" ", "_") - if (!(tmpprimary in selectedOrganization.sync_features)) { - console.log("Primary not in sync_features: "+tmpprimary) - return - } + console.log(selectedOrganization.sync_features); + // Check if primary is in sync_features + var tmpprimary = primary.replaceAll(" ", "_"); + if (!(tmpprimary in selectedOrganization.sync_features)) { + console.log("Primary not in sync_features: " + tmpprimary); + return; + } - if (props.data.active) { - selectedOrganization.sync_features[tmpprimary].active = false - } else { - selectedOrganization.sync_features[tmpprimary].active = true - } + if (props.data.active) { + selectedOrganization.sync_features[tmpprimary].active = false; + } else { + selectedOrganization.sync_features[tmpprimary].active = true; + } - setSelectedOrganization(selectedOrganization) - forceUpdate(Math.random()) - submitFeatureEdit(selectedOrganization.sync_features) - } - - const submitEdit = (e) => { - e.preventDefault(); - e.stopPropagation(); + setSelectedOrganization(selectedOrganization); + forceUpdate(Math.random()); + submitFeatureEdit(selectedOrganization.sync_features); + }; - // Check if primary is in sync_features - var tmpprimary = primary.replaceAll(" ", "_") - if (!(tmpprimary in selectedOrganization.sync_features)) { - console.log("Primary not in sync_features: "+tmpprimary) - return - } + const submitEdit = (e) => { + e.preventDefault(); + e.stopPropagation(); - // Make it into a number - var tmp = parseInt(newValue) - if (isNaN(tmp)) { - console.log("Not a number: "+newValue) - return - } + // Check if primary is in sync_features + var tmpprimary = primary.replaceAll(" ", "_"); + if (!(tmpprimary in selectedOrganization.sync_features)) { + console.log("Primary not in sync_features: " + tmpprimary); + return; + } - selectedOrganization.sync_features[tmpprimary].limit = tmp + // Make it into a number + var tmp = parseInt(newValue); + if (isNaN(tmp)) { + console.log("Not a number: " + newValue); + return; + } - setSelectedOrganization(selectedOrganization) - forceUpdate(Math.random()) - submitFeatureEdit(selectedOrganization.sync_features) - } + selectedOrganization.sync_features[tmpprimary].limit = tmp; + + setSelectedOrganization(selectedOrganization); + forceUpdate(Math.random()); + submitFeatureEdit(selectedOrganization.sync_features); + }; return ( - + { - setExpanded(!expanded); - }} - > + style={{ cursor: "pointer" }} + onClick={() => { + setExpanded(!expanded); + }} + > {primaryIcon} - {isCloud && userdata.support === true ? - - { - e.preventDefault(); - e.stopPropagation(); + {isCloud && userdata.support === true ? ( + + { + e.preventDefault(); + e.stopPropagation(); - if (showEdit) { - setShowEdit(false) - return - } + if (showEdit) { + setShowEdit(false); + return; + } - console.log("Edit") - - setShowEdit(true) - }} - /> - - : null} - - { - if (!isCloud || userdata.support !== true) { - return - } + console.log("Edit"); - e.preventDefault(); - e.stopPropagation(); + setShowEdit(true); + }} + /> + + ) : null} + + { + if (!isCloud || userdata.support !== true) { + return; + } - enableFeature() - }} - > - {secondaryIcon} - - + e.preventDefault(); + e.stopPropagation(); + + enableFeature(); + }} + > + {secondaryIcon} + + - {expanded ? + {expanded ? (
    Usage:  @@ -2573,50 +2918,59 @@ If you're interested, please let me know a time that works for you, or set up a "Unlimited" ) : ( - {props.data.usage} / {props.data.limit === "" ? "Unlimited" : props.data.limit} + {props.data.usage} /{" "} + {props.data.limit === "" ? "Unlimited" : props.data.limit} )} {/* Data sharing: {props.data.data_collection} */} - Description: {secondary} + + Description: {secondary} +
    - : null} + ) : null} - - {showEdit ? - { - console.log("Submit") - submitEdit(e) - }}> - - - { - setNewValue(event.target.value) - }} - /> - - - - : null} + {showEdit ? ( + { + console.log("Submit"); + submitEdit(e); + }} + > + + { + setNewValue(event.target.value); + }} + /> + + + + ) : null}
    ); @@ -2667,7 +3021,7 @@ If you're interested, please let me know a time that works for you, or set up a cloud sync @@ -2711,7 +3065,7 @@ If you're interested, please let me know a time that works for you, or set up a enableCloudSync( cloudSyncApikey, selectedOrganization, - selectedOrganization.cloud_sync + selectedOrganization.cloud_sync, ); }} color="primary" @@ -2747,25 +3101,37 @@ If you're interested, please let me know a time that works for you, or set up a ); var regiontag = "eu"; - if (userdata.region_url !== undefined && userdata.region_url !== null && userdata.region_url.length > 0) { + if ( + userdata.region_url !== undefined && + userdata.region_url !== null && + userdata.region_url.length > 0 + ) { const regionsplit = userdata.region_url.split("."); if (regionsplit.length > 2 && !regionsplit[0].includes("shuffler")) { - const namesplit = regionsplit[0].split("/"); - - regiontag = namesplit[namesplit.length - 1]; + const namesplit = regionsplit[0].split("/"); + + regiontag = namesplit[namesplit.length - 1]; + if (regiontag === "california") { + regiontag = "us"; + } } } - + const organizationView = curTab === 0 && selectedOrganization.id !== undefined ? (
    ) : (
    - {/* + {/* */} - {userdata.support === true ? - - {/**/} - - - Status - - - - : null} - {isCloud ? - - { - toast("Region change is not directly implemented yet, and requires support help.") + {userdata.support === true ? ( + + {/**/} + + + Status + + + + ) : null} + {isCloud ? ( + + { + if (userdata.support === false) { + toast( + "Region change is not directly implemented yet, and requires support help.", + ); - if (window.drift !== undefined) { - window.drift.api.startInteraction({ - interactionId: 386411, - }) - } - }} - > - {regiontag} - - - : null} + if (window.drift !== undefined) { + window.drift.api.startInteraction({ + interactionId: 386411, + }); + } + } else { + // Show region change modal + console.log("Should open region change modal"); + setRegionChangeModalOpen(true); + } + }} + > + {regiontag} + + + ) : null} + + - {selectedOrganization.defaults !== undefined && selectedOrganization.defaults.documentation_reference !== undefined && selectedOrganization.defaults.documentation_reference !== null && selectedOrganization.defaults.documentation_reference.includes("http") ? - - - - - - - - : null} + aria-label={"Open org docs"} + > + + + + + + + ) : null} {selectedOrganization.name.length > 0 ? ( ) : (
    )} - { - const newValue = parseInt(inputValue); - setAdminTab(newValue); + { + const newValue = parseInt(inputValue); + setAdminTab(newValue); - //const setConfig = (event, inputValue) => { - navigate(`/admin?admin_tab=${admin_views[newValue]}`); - }} - aria-label="disabled tabs example" - > - - Edit Details - - /> - - Cloud Synchronization - - /> - - Priorities - - /> - - Billing & Stats - - /> - - Branding (Beta) - - /> - + //const setConfig = (event, inputValue) => { + navigate(`/admin?admin_tab=${admin_views[newValue]}`); + }} + aria-label="disabled tabs example" + > + Edit Details /> + Cloud Synchronization /> + Priorities /> + Billing & Stats /> + Branding (Beta) /> + - {adminTab === 0 ? ( - - ) - : adminTab === 1 ? ( -
    - - Cloud syncronization - - What does{" "} - - cloud sync - {" "} - do? Cloud syncronization is a way of getting more out of Shuffle. - Shuffle will ALWAYS make every option open source, but - features relying on other users can't be done without a - collaborative approach. - {isCloud ? ( -
    -
    - - Currently syncronizing:{" "} - {selectedOrganization.cloud_sync_active === true - ? "True" - : "False"} - - {selectedOrganization.cloud_sync_active ? ( - - Syncronization interval:{" "} - {selectedOrganization.sync_config.interval === 0 - ? "60" - : selectedOrganization.sync_config.interval} - - ) : null} - - Your Apikey - -
    - - { - setShowApiKey(!showApiKey) - }} - > - {showApiKey ? : } - - - ) - }} - required - fullWidth={true} - disabled={true} - autoComplete="cloud apikey" - id="apikey_field" - margin="normal" - placeholder="Cloud Apikey" - variant="outlined" - defaultValue={userSettings.apikey} - type={!isCloud || showApiKey ? "text" : "password"} - /> - {selectedOrganization.cloud_sync_active ? ( - - ) : null} -
    -
    -
    - ) : ( -
    -
    - { - setCloudSyncApikey(event.target.value); - }} - /> - -
    - {orgSyncResponse.length > 0 ? ( - - Message from Shuffle Cloud: {orgSyncResponse} - - ) : null} -
    - )} - - Features - - - Features and Limitations that are currently available to you in your Cloud or Hybrid Organization. App Executions (App Runs) reset monthly. If the organization is a customer or in a trial, these features limitations are not always enforced. - - + {adminTab === 0 ? ( + + ) : adminTab === 1 ? ( +
    + + Cloud syncronization + + What does{" "} + + cloud sync + {" "} + do? Cloud syncronization is a way of getting more out of + Shuffle. Shuffle will ALWAYS make every option open + source, but features relying on other users can't be done + without a collaborative approach. + {isCloud ? ( +
    +
    + + Currently syncronizing:{" "} + {selectedOrganization.cloud_sync_active === true + ? "True" + : "False"} + + {selectedOrganization.cloud_sync_active ? ( + + Syncronization interval:{" "} + {selectedOrganization.sync_config.interval === 0 + ? "60" + : selectedOrganization.sync_config.interval} + + ) : null} + + Your Apikey + +
    + + { + setShowApiKey(!showApiKey); + }} + > + {showApiKey ? ( + + ) : ( + + )} + + + ), + }} + required + fullWidth={true} + disabled={true} + autoComplete="cloud apikey" + id="apikey_field" + margin="normal" + placeholder="Cloud Apikey" + variant="outlined" + defaultValue={userSettings.apikey} + type={!isCloud || showApiKey ? "text" : "password"} + /> + {selectedOrganization.cloud_sync_active ? ( + + ) : null} +
    +
    +
    + ) : ( +
    +
    + { + setCloudSyncApikey(event.target.value); + }} + /> + +
    + {orgSyncResponse.length > 0 ? ( + + Message from Shuffle Cloud: {orgSyncResponse} + + ) : null} +
    + )} + + Features + + + Features and Limitations that are currently available to you + in your Cloud or Hybrid Organization. App Executions (App + Runs) reset monthly. If the organization is a customer or in a + trial, these features limitations are not always enforced. + + + {selectedOrganization.sync_features === undefined || + selectedOrganization.sync_features === null + ? null + : Object.keys(selectedOrganization.sync_features).map( + function (key, index) { + if ( + key === "schedule" || + key === "apps" || + key === "updates" || + key === "editing" + ) { + return null; + } - {selectedOrganization.sync_features === undefined || - selectedOrganization.sync_features === null - ? null - : Object.keys(selectedOrganization.sync_features).map(function ( - key, - index - ) { + const item = selectedOrganization.sync_features[key]; + if (item === null || item === undefined) { + return null; + } - if (key === "schedule" || key === "apps" || key === "updates" || key === "editing") { - return null; - } + const newkey = key.replaceAll("_", " "); + // Name rewrites as these are structs + var newname = ""; + if (newkey.toLowerCase() === "shuffle gpt") { + newname = "Shuffle AI"; + } - const item = selectedOrganization.sync_features[key]; - if (item === null) { - return null - } + const griditem = { + primary: newkey, + secondary: + item.description === undefined || + item.description === null || + item.description.length === 0 + ? "Not defined yet" + : item.description, + limit: item.limit, + usage: + item.usage === undefined || item.usage === null + ? 0 + : item.usage, + data_collection: "None", + active: item.active, + icon: , - const newkey = key.replaceAll("_", " "); - const griditem = { - primary: newkey, - secondary: - item.description === undefined || - item.description === null || - item.description.length === 0 - ? "Not defined yet" - : item.description, - limit: item.limit, - usage: item.usage === undefined || - item.usage === null ? 0 : item.usage, - data_collection: "None", - active: item.active, - icon: , - }; + newname: newname, + }; - return ( - - - - ); - })} - -
    - ) - : adminTab === 2 ? - - : adminTab === 3 ? - - : adminTab === 4 ? - - : null - } + return ( + + + + ); + }, + )} +
    +
    + ) : adminTab === 2 ? ( + + ) : adminTab === 3 ? ( + + ) : adminTab === 4 ? ( + + ) : null} - -
    )}
    @@ -3291,7 +3716,11 @@ If you're interested, please let me know a time that works for you, or set up a > - {curTab === 1 ? "Add user" : curTab === 7 ? "Add Sub-Organization" : "Add environment"} + {curTab === 1 + ? "Add user" + : curTab === 7 + ? "Add Sub-Organization" + : "Add environment"} @@ -3452,7 +3881,7 @@ If you're interested, please let me know a time that works for you, or set up a Add, edit, block or change passwords.{" "} @@ -3485,112 +3914,128 @@ If you're interested, please let me know a time that works for you, or set up a }} /> - - {logsViewModal ? - { - setLogsViewModal(false); - }} - PaperProps={{ - style: { - backgroundColor: theme.palette.surfaceColor, - color: "white", - minWidth: "1200px", - minHeight: "320px", - }, - }} - > - - User Logs - - - {/* ask user for which IP they want to see logs for by iterating of user.login_info */} - - - User IP - + {logsViewModal ? ( + { + setLogsViewModal(false); + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: "1200px", + minHeight: "320px", + }, + }} + > + + User Logs + + + {/* ask user for which IP they want to see logs for by iterating of user.login_info */} + + + User IP + - { + setIpSelected(event.target.value); + await getLogs(event.target.value, userLogViewing.id); + }} + > + {(() => { + const uniqueIPs = new Set(); + + return userLogViewing.login_info.map((data, index) => { + if ( + data.ip.includes("127.0.0.1") || + uniqueIPs.has(data.ip) + ) { + return null; + } + + uniqueIPs.add(data.ip); + + return ( + + {data.ip} + + ); + }); + })()} + + + {logsLoading && ipSelected.length !== 0 ? ( +
    + + Loading logs +
    + ) : null} + + + {logs.map((data, index) => ( + // redirect user to logs + // using request id or trace id + - {(() => { - const uniqueIPs = new Set(); - - return userLogViewing.login_info.map((data, index) => { - if (data.ip.includes("127.0.0.1") || uniqueIPs.has(data.ip)) { - return null; - } - - uniqueIPs.add(data.ip); - - return ( - - {data.ip} - - ); - }); - })()} - -
    - {logsLoading && ipSelected.length !== 0 ? -
    - - Loading logs -
    - : null} - - - {logs.map((data, index) => ( - // redirect user to logs - // using request id or trace id - - - - - - ))} - - -
    - -
    - : null} - + + + + + ))} + +
    +
    + ) : null} @@ -3635,13 +4080,12 @@ If you're interested, please let me know a time that works for you, or set up a ) : null} - {users === undefined || users === null ? null @@ -3651,231 +4095,248 @@ If you're interested, please let me know a time that works for you, or set up a bgColor = "#1f2023"; } - const timeNow = new Date().getTime(); - - // Get the highest timestamp in data.login_info - var lastLogin = "N/A" - if (data.login_info !== undefined && data.login_info !== null) { - var loginInfo = 0 - for (var i = 0; i < data.login_info.length; i++) { - if (data.login_info[i].timestamp > loginInfo) { - loginInfo = data.login_info[i].timestamp - } - } + const timeNow = new Date().getTime(); - if (loginInfo > 0) { - lastLogin = new Date(loginInfo * 1000).toISOString().slice(0, 10) + " (" + data.login_info.length + ")" - } - } - - var userData = data.username - if (userdata.support === true) { - userData = { - setLogsViewModal(true) - setUserLogViewing(data) - }} - >{data.username} - } - - return ( - - - - - { - const elementName = "copy_element_shuffle"; - var copyText = - document.getElementById(elementName); - if ( - copyText !== null && - copyText !== undefined - ) { - const clipboard = navigator.clipboard; - if (clipboard === undefined) { - toast( - "Can only copy over HTTPS (port 3443)" - ); - return; - } - - navigator.clipboard.writeText(data.apikey); - copyText.select(); - copyText.setSelectionRange( - 0, - 99999 - ); /* For mobile devices */ - - /* Copy the text inside the text field */ - document.execCommand("copy"); - - toast("Apikey copied to clipboard"); - } - }} - > - - - - ) + // Get the highest timestamp in data.login_info + var lastLogin = "N/A"; + if (data.login_info !== undefined && data.login_info !== null) { + var loginInfo = 0; + for (var i = 0; i < data.login_info.length; i++) { + if (data.login_info[i].timestamp > loginInfo) { + loginInfo = data.login_info[i].timestamp; + } } - /> - { - console.log("VALUE: ", e.target.value); - setUser(data.id, "role", e.target.value); - }} + if (loginInfo > 0) { + lastLogin = + new Date(loginInfo * 1000).toISOString().slice(0, 10) + + " (" + + data.login_info.length + + ")"; + } + } + + var userData = data.username; + if (userdata.support === true) { + userData = ( + { + setLogsViewModal(true); + setUserLogViewing(data); }} > - - Org Admin - - - Org User - - - Org Reader - - - } - style={{ minWidth: 135, maxWidth: 135, marginRight: 15 }} - /> - - - - {selectedOrganization.child_orgs !== undefined && - selectedOrganization.child_orgs !== null && - selectedOrganization.child_orgs.length > 0 ? ( - - ) : null} - - { - setSelectedUserModalOpen(true); - setSelectedUser(data); + {data.username} + + ); + } - // Find matching orgs between current org and current user's access to those orgs - if ( - userdata.orgs !== undefined && - userdata.orgs !== null && - userdata.orgs.length > 0 && - selectedOrganization.child_orgs !== undefined && - selectedOrganization.child_orgs !== null && - selectedOrganization.child_orgs.length > 0 - ) { - var active = []; - for (var key in userdata.orgs) { - const found = - selectedOrganization.child_orgs.find( - (item) => item.id === userdata.orgs[key].id - ); - if (found !== null && found !== undefined) { - if ( - data.orgs === undefined || - data.orgs === null - ) { - continue; - } + return ( + + - const subfound = data.orgs.find( - (item) => item === found.id - ); - if ( - subfound !== null && - subfound !== undefined - ) { - active.push(subfound); - } - } - } + + { + const elementName = "copy_element_shuffle"; + var copyText = + document.getElementById(elementName); + if ( + copyText !== null && + copyText !== undefined + ) { + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast( + "Can only copy over HTTPS (port 3443)", + ); + return; + } - setMatchingOrganizations(active); + navigator.clipboard.writeText(data.apikey); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999, + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + + toast("Apikey copied to clipboard"); + } + }} + > + + + + ) } - }} - > - - - {/**/} - - - - - ); - })} + + + + + + ); + })}
    ) : null; @@ -3911,16 +4373,16 @@ If you're interested, please let me know a time that works for you, or set up a //setShow2faSetup(true); }; - - - const filesView = curTab !== 3 ? null : - + const filesView = + curTab !== 3 ? null : ( + + ); const schedulesView = curTab === 5 ? ( @@ -3931,7 +4393,7 @@ If you're interested, please let me know a time that works for you, or set up a Schedules used in Workflows. Makes locating and control easier.{" "} @@ -3980,10 +4442,13 @@ If you're interested, please let me know a time that works for you, or set up a 0 ? + schedule.environment === "cloud" || + schedule.environment === "" || + schedule.frequency.length > 0 ? ( schedule.frequency - : + ) : ( {schedule.seconds} seconds + ) } /> {schedule.workflow_id} } />

    App Authentication

    - Control the authentication options for individual apps. + Control the authentication options for individual apps.   @@ -4181,15 +4646,16 @@ If you're interested, please let me know a time that works for you, or set up a */} - + {authentication === undefined || authentication === null @@ -4222,7 +4688,8 @@ If you're interested, please let me know a time that works for you, or set up a ]; } - const isDistributed = data.suborg_distributed === true ? true : false; + const isDistributed = + data.suborg_distributed === true ? true : false; return ( @@ -4290,7 +4757,8 @@ If you're interested, please let me know a time that works for you, or set up a style={{ minWidth: 125, maxWidth: 125, - overflow: "hidden", + overflow: "auto", + marginRight: 10, }} /> { updateAppAuthentication(data); }} - disabled={data.org_id !== selectedOrganization.id ? true : false} + disabled={ + data.org_id !== selectedOrganization.id ? true : false + } > @@ -4318,14 +4788,17 @@ If you're interested, please let me know a time that works for you, or set up a > { editAuthenticationConfig(data.id); }} > - +
    ) : ( @@ -4337,17 +4810,21 @@ If you're interested, please let me know a time that works for you, or set up a {}} - disabled={data.org_id !== selectedOrganization.id ? true : false} + disabled={ + data.org_id !== selectedOrganization.id + ? true + : false + } > - + )} { deleteAuthentication(data); }} @@ -4356,32 +4833,39 @@ If you're interested, please let me know a time that works for you, or set up a - {selectedOrganization.id !== undefined && data.org_id !== selectedOrganization.id ? - - - - : - - { - changeDistribution(data, !isDistributed) - }} - /> - - } + {selectedOrganization.id !== undefined && + data.org_id !== selectedOrganization.id ? ( + + + + ) : ( + + { + changeDistribution(data, !isDistributed); + }} + /> + + )} ); @@ -4402,7 +4886,7 @@ If you're interested, please let me know a time that works for you, or set up a headers: { "Content-Type": "application/json; charset=utf-8", }, - }) + }) .then((response) => { return response.json(); }) @@ -4411,10 +4895,13 @@ If you're interested, please let me know a time that works for you, or set up a if (responseJson.success === true) { setLogs(responseJson.logs); } else { - if (responseJson.success === false || responseJson.reason !== undefined) { - console.log("Reason given: ", responseJson.reason) - toast("Failed getting logs: " + responseJson.reason) - setLogs([]) + if ( + responseJson.success === false || + responseJson.reason !== undefined + ) { + console.log("Reason given: ", responseJson.reason); + toast("Failed getting logs: " + responseJson.reason); + setLogs([]); } else { toast("Failed getting logs"); } @@ -4430,7 +4917,7 @@ If you're interested, please let me know a time that works for you, or set up a }); }; - const changeRecommendation = (recommendation, action) => { + const changeRecommendation = (recommendation, action) => { const data = { action: action, name: recommendation.name, @@ -4456,22 +4943,27 @@ If you're interested, please let me know a time that works for you, or set up a }) .then((responseJson) => { if (responseJson.success === true) { - if (checkLogin !== undefined) { - checkLogin() - getEnvironments() - } + if (checkLogin !== undefined) { + checkLogin(); + getEnvironments(); + } } else { - if (responseJson.success === false && responseJson.reason !== undefined) { - toast("Failed change recommendation: ", responseJson.reason) - } else { - toast("Failed change recommendation"); - } + if ( + responseJson.success === false && + responseJson.reason !== undefined + ) { + toast("Failed change recommendation: ", responseJson.reason); + } else { + toast("Failed change recommendation"); + } } }) .catch((error) => { - toast("Failed dismissing alert. Please contact support@shuffler.io if this persists."); + toast( + "Failed dismissing alert. Please contact support@shuffler.io if this persists.", + ); }); - } + }; const environmentView = curTab === 6 ? ( @@ -4479,10 +4971,12 @@ If you're interested, please let me know a time that works for you, or set up a

    Environments

    - Decides what Orborus environment to run your workflow actions. If you have scale problems, talk to our team: support@shuffler.io.  + Decides what Orborus environment to run your workflow actions. If + you have scale problems, talk to our team: + support@shuffler.io. 
    @@ -4521,14 +5015,14 @@ If you're interested, please let me know a time that works for you, or set up a }} /> - + 0) { - foundIndex = userdata.priorities.findIndex(prio => prio.name.includes("CPU") && prio.active === true) + // Check if there's a notification for it in userdata.priorities + var showCPUAlert = false; + var foundIndex = -1; + if ( + userdata !== undefined && + userdata !== null && + userdata.priorities !== undefined && + userdata.priorities !== null && + userdata.priorities.length > 0 + ) { + foundIndex = userdata.priorities.findIndex( + (prio) => prio.name.includes("CPU") && prio.active === true, + ); - if (foundIndex >= 0 && userdata.priorities[foundIndex].name.endsWith(environment.Name)) { - showCPUAlert = true - } - } + if ( + foundIndex >= 0 && + userdata.priorities[foundIndex].name.endsWith( + environment.Name, + ) + ) { + showCPUAlert = true; + } + } - const queueSize = environment.queue !== undefined && environment.queue !== null ? environment.queue < 0 ? 0 : environment.queue > 1000 ? ">1000" : environment.queue : 0 + const queueSize = + environment.queue !== undefined && environment.queue !== null + ? environment.queue < 0 + ? 0 + : environment.queue > 1000 + ? ">1000" + : environment.queue + : 0; return ( - - - - - - : environment.run_type === "docker" ? - - - - : environment.run_type === "k8s" ? - - - - : - - - - } - style={{ - minWidth: 50, - maxWidth: 50, - overflow: "hidden", - }} - /> - - - - : - - - - - - } - style={{ - minWidth: 85, - maxWidth: 85, - overflow: "hidden", - }} - /> - - - Not running -
    - : environment.running_ip.split(":")[0] - : "N/A" - } - style={{ - minWidth: 150, - maxWidth: 150, - overflow: "hidden", - }} - /> + + + + + + ) : environment.run_type === "docker" ? ( + + + + ) : environment.run_type === "k8s" ? ( + + + + ) : ( + + + + ) + } + style={{ + minWidth: 50, + maxWidth: 50, + overflow: "hidden", + }} + /> + + + + ) : ( + + + + + + ) + } + style={{ + minWidth: 85, + maxWidth: 85, + overflow: "hidden", + }} + /> + + Not running
    + ) : ( + environment.running_ip.split(":")[0] + ) + ) : ( + "N/A" + ) + } + style={{ + minWidth: 150, + maxWidth: 150, + overflow: "hidden", + }} + /> - - { - if (environment.Type === "cloud") { - toast("No Orborus necessary for environment cloud. Create and use a different environment to run executions on-premises.") - return - } + + { + if (environment.Type === "cloud") { + toast( + "No Orborus necessary for environment cloud. Create and use a different environment to run executions on-premises.", + ); + return; + } - if (props.userdata.active_org === undefined || props.userdata.active_org === null) { - toast("No active organization yet. Are you logged in?") - return - } + if ( + props.userdata.active_org === undefined || + props.userdata.active_org === null + ) { + toast( + "No active organization yet. Are you logged in?", + ); + return; + } - const elementName = "copy_element_shuffle"; - const auth = environment.auth === "" ? 'cb5st3d3Z!3X3zaJ*Pc' : environment.auth - const newUrl = globalUrl === "https://shuffler.io" ? "https://shuffle-backend-stbuwivzoq-nw.a.run.app" : globalUrl + const elementName = "copy_element_shuffle"; + const auth = + environment.auth === "" + ? "cb5st3d3Z!3X3zaJ*Pc" + : environment.auth; + const newUrl = + globalUrl === "https://shuffler.io" + ? "https://shuffle-backend-stbuwivzoq-nw.a.run.app" + : globalUrl; - const commandData = `docker run --volume "/var/run/docker.sock:/var/run/docker.sock" -e ENVIRONMENT_NAME="${environment.Name}" -e 'AUTH=${auth}' -e ORG="${props.userdata.active_org.id}" -e DOCKER_API_VERSION=1.40 -e BASE_URL="${newUrl}" --name="shuffle-orborus" -d ghcr.io/shuffle/shuffle-orborus:latest` - var copyText = document.getElementById(elementName); - if (copyText !== null && copyText !== undefined) { - const clipboard = navigator.clipboard; - if (clipboard === undefined) { - toast("Can only copy over HTTPS (port 3443)"); - return; - } + const commandData = `docker run --restart=always --volume "/var/run/docker.sock:/var/run/docker.sock" -e ENVIRONMENT_NAME="${environment.Name}" -e 'AUTH=${auth}' -e ORG="${props.userdata.active_org.id}" -e DOCKER_API_VERSION=1.40 -e BASE_URL="${newUrl}" --name="shuffle-orborus" -d ghcr.io/shuffle/shuffle-orborus:latest`; + var copyText = + document.getElementById(elementName); + if ( + copyText !== null && + copyText !== undefined + ) { + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast( + "Can only copy over HTTPS (port 3443)", + ); + return; + } - navigator.clipboard.writeText(commandData); - copyText.select(); - copyText.setSelectionRange( - 0, - 99999 - ); /* For mobile devices */ + navigator.clipboard.writeText(commandData); + 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"); - toast("Orborus command copied to clipboard"); - } - }} - > - - - - } - /> + toast("Orborus command copied to clipboard"); + } + }} + > + + + + } + /> - - + + + {environment.default ? null : ( + + )} + + +
    + + + - )} - - -
    - - - - - -
    -
    - - - {showCPUAlert === false ? null : - -
    -
    - - 90% CPU the server(s) hosting the Shuffle App Runner (Orborus) was found. - - - Need help with High Availability and Scale? Read documentation and Get in touch. - -
    -
    - -
    -
    -
    - } - + if ( + isCloud && + environment.Name.toLowerCase() === "cloud" + ) { + rerunCloudWorkflows(environment); + } else { + abortEnvironmentWorkflows(environment); + } + }} + color="primary" + > + {isCloud && + environment.Name.toLowerCase() === "cloud" + ? "Rerun" + : "Clear"} + +
    +
    +
    + + + {showCPUAlert === false ? null : ( + +
    +
    + + 90% CPU the server(s) hosting the Shuffle App + Runner (Orborus) was found. + + + Need help with High Availability and Scale?{" "} + + Read documentation + {" "} + and{" "} + + Get in touch + + . + +
    +
    + +
    +
    +
    + )} + ); })} - {/**/} + {/**/}
    ) : null; const imagesize = 40; const imageStyle = { - width: imagesize, - height: imagesize, - pointerEvents: "none", + width: imagesize, + height: imagesize, + pointerEvents: "none", }; const organizationsTab = @@ -4853,7 +5487,22 @@ If you're interested, please let me know a time that works for you, or set up a

    Organizations

    - Control sub organizations (tenants)! {isCloud ? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out." : ""}. Learn more + Control sub organizations (tenants)!{" "} + {isCloud + ? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out." + : ""} + .{" "} + + Learn more +
    - - - ); - })()} - - ) : null - } - - {subOrgs.length > 0 ? ( - - -
    -

    - Sub Organizations of the Current Organization ({subOrgs.length}) -

    -
    + {" "} + Your Parent Organization + +
    + + {(() => { + const image = + parentOrg.image === "" ? ( + {parentOrg.name} + ) : ( + {parentOrg.name} + ); + const bgColor = "#27292d"; - + return ( + + + + + + + + + + - - - - - - - - {subOrgs.map((data, index) => { - const image = - data.image === "" ? ( - {data.name} - ) : ( - {data.name} + + + ); + })()} + + ) : null} - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } + {subOrgs.length > 0 ? ( + + +
    +

    + Sub Organizations of the Current Organization ({subOrgs.length}) +

    +
    - return ( - + + + + - - - ); - })} -
    - - - ) : null - } + + {subOrgs.map((data, index) => { + const image = + data.image === "" ? ( + {data.name} + ) : ( + {data.name} + ); + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } - + return ( + + + + -
    -

    - All Tenants -

    -
    + +
    + ); + })} +
    + + + ) : null} - + + +
    +

    + All Tenants +

    +
    + + ) : null; - const cacheOrgView = + const cacheOrgView = curTab === 4 ? (
    - +
    ) : null; @@ -5296,7 +5953,7 @@ If you're interested, please let me know a time that works for you, or set up a - App Auth + App Auth /> - Datastore + Datastore /> { }, "open": false, "attachedTo": "", - }); + }) + + // New for generated stuff + const integrationApps = [{ + "id": "integration", + "name": "Integration Framework", + "type": "ACTION", + "app_version": "1.0.0", + "loop_versions": ["1.0.0"], + "authentication": { + "type": "", + }, + "description": "Support-use only", + "actions": [{ + "name": "Cases", + "description": "Available actions for case management", + "label": "Cases", + "parameters": [{ + "name": "action", + "value": "list_tickets", + "options": [ + "list_tickets", + "get_ticket", + "create_ticket", + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }, + /*{ + "name": "options", + "value": "deduplicate,enrich", + "required": false, + "multiselect": true, + "options": [ + "deduplicate", + "enrich", + ] + }*/ + ] + }] + }] + + /* + { + "name": "Email", + "label": "Email", + "parameters": [{ + "name": "action", + "value": "list_email", + "options": [ + "list_email", + "send_mail", + ], + "required": true, + }], + }] + }] + */ // For code editor const [codeEditorModalOpen, setCodeEditorModalOpen] = React.useState(false); @@ -577,8 +638,6 @@ const AngularWorkflow = (defaultprops) => { return } - console.log("Checking to update app with useeffect.") - for (let appkey in apps) { const curapp = apps[appkey] if (curapp.name !== selectedApp.name) { @@ -610,6 +669,7 @@ const AngularWorkflow = (defaultprops) => { setSelectedApp(curapp) } + break } @@ -1539,34 +1599,38 @@ const AngularWorkflow = (defaultprops) => { curworkflowAction.position = cyelements[cyelementsKey].position(); // workaround to fix some edgecases - if ( - curworkflowAction.parameters === "" || - curworkflowAction.parameters === null - ) { - curworkflowAction.parameters = []; - } + if ( + curworkflowAction.parameters === "" || + curworkflowAction.parameters === null + ) { + curworkflowAction.parameters = []; + } - if ( - curworkflowAction.example === undefined || - curworkflowAction.example === "" || - curworkflowAction.example === null - ) { - if (cyelements[cyelementsKey].data().example !== undefined) { - curworkflowAction.example = cyelements[cyelementsKey].data().example; - } - } + if ( + curworkflowAction.example === undefined || + curworkflowAction.example === "" || + curworkflowAction.example === null + ) { + if (cyelements[cyelementsKey].data().example !== undefined) { + curworkflowAction.example = cyelements[cyelementsKey].data().example; + } + } // Override just in this place curworkflowAction.errors = []; curworkflowAction.isValid = true; - // Cleans up OpenAPI items - var newparams = []; - for (let parametersKey in curworkflowAction.parameters) { - const thisitem = curworkflowAction.parameters[parametersKey]; - if (thisitem.name.startsWith("${") && thisitem.name.endsWith("}")) { - continue; - } + // Cleans up OpenAPI items + var newparams = []; + for (let parametersKey in curworkflowAction.parameters) { + const thisitem = curworkflowAction.parameters[parametersKey]; + if (thisitem.name.startsWith("${") && thisitem.name.endsWith("}")) { + continue; + } + + if (thisitem.value !== undefined && thisitem.value !== null && Array.isArray(thisitem.value)) { + thisitem.value = thisitem.value.join(",") + } newparams.push(thisitem); } @@ -1578,17 +1642,17 @@ const AngularWorkflow = (defaultprops) => { useworkflow.triggers = []; } - var curworkflowTrigger = useworkflow.triggers.find( - (a) => a.id === cyelements[cyelementsKey].data()["id"] - ); - if (curworkflowTrigger === undefined) { - curworkflowTrigger = cyelements[cyelementsKey].data(); - } + var curworkflowTrigger = useworkflow.triggers.find( + (a) => a.id === cyelements[cyelementsKey].data()["id"] + ); + if (curworkflowTrigger === undefined) { + curworkflowTrigger = cyelements[cyelementsKey].data(); + } - curworkflowTrigger.position = cyelements[cyelementsKey].position(); - if (curworkflowTrigger.canConnect === false) { - continue - } + curworkflowTrigger.position = cyelements[cyelementsKey].position(); + if (curworkflowTrigger.canConnect === false) { + continue + } newTriggers.push(curworkflowTrigger); } else if (type === "COMMENT") { @@ -2087,6 +2151,7 @@ const AngularWorkflow = (defaultprops) => { const pretend_apps = [{ "name": "TBD", + "id": "TBD", "app_name": "TBD", "app_version": "TBD", "description": "TBD", @@ -2113,6 +2178,7 @@ const AngularWorkflow = (defaultprops) => { console.log("No response") const pretend_apps = [{ "name": "TBD", + "id": "TBD", "app_name": "TBD", "app_version": "TBD", "description": "TBD", @@ -2131,42 +2197,53 @@ const AngularWorkflow = (defaultprops) => { return } - // Used for e.g. Liquid testing + // Used for e.g. Liquid testing const foundTools = responseJson.find((app) => app.name === "Shuffle Tools") if (foundTools !== undefined && foundTools !== null) { setToolsApp(foundTools) } setApps(responseJson); + // Set localstorage for the apps in the "apps" key + if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) { + try { + localStorage.setItem("apps", JSON.stringify(responseJson)) + } catch (e) { + console.log("Failed to set apps in localstorage: ", e) + } + } + + var handledPrioritizedApps = responseJson.filter((app) => internalIds.includes(app.name.toLowerCase())); + handledPrioritizedApps = [].concat(integrationApps, handledPrioritizedApps) if (isCloud) { setFilteredApps(responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase()))); - setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name.toLowerCase()))); + setPrioritizedApps(handledPrioritizedApps) } else { var tmpFiltered = responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase())) setFilteredApps(tmpFiltered) - setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name.toLowerCase()))); + setPrioritizedApps(handledPrioritizedApps) } setAppsLoaded(true) // Remove all cytoscape triggers first? if (cy !== undefined && cy !== null) { - cy.removeListener("select"); + cy.removeListener("select") } // Re-adding cytoscape triggers if (cy !== undefined && cy !== null) { cy.on("select", "node", (e) => { - onNodeSelect(e, appAuthentication); - }); + onNodeSelect(e, appAuthentication) + }) } }) .catch((error) => { - console.log("App loading error: " + error.toString()); + console.log("App loading error: " + error.toString()) setAppsLoaded(true) - //toast("App loading error: "+error.toString()); + //toast("App loading error: "+error.toString()) }); }; @@ -2575,7 +2652,6 @@ const AngularWorkflow = (defaultprops) => { const node = cy.getElementById(chunkJson.id) if (node !== undefined && node !== null) { - console.log("Node already exists: ", node) return } @@ -3455,9 +3531,9 @@ const AngularWorkflow = (defaultprops) => { cy.add(decoratorNode).unselectify(); } else { - console.log("Node already exists - don't add descriptor node"); + //console.log("Node already exists - don't add descriptor node"); } - } + } } originalLocation = { @@ -4014,7 +4090,7 @@ const AngularWorkflow = (defaultprops) => { ) if (curapp === undefined || curapp === null) { - console.log("Couldn't find ID - checking with name & version") + console.log("Couldn't find app with that ID - checking with name & version") curapp = newapps.find((a) => a.name === curaction.app_name && @@ -4024,34 +4100,60 @@ const AngularWorkflow = (defaultprops) => { ) } + if (curapp === undefined || curapp === null) { + curapp = integrationApps.find((a) => + a.name === curaction.app_name && + (a.app_version === curaction.app_version || + (a.loop_versions !== null && + a.loop_versions.includes(curaction.app_version))) + ) + } + if (curaction.template === true && curaction.name !== undefined) { //newapps. const parsedname = curaction.name.replaceAll(" ", "_").toLowerCase() console.log("FIND AN ACTION AMONG THE APPS THAT MATCHES NAME: ", parsedname) - curaction.matching_actions = [] - for (var newAppskey in newapps) { - for (let actionsSubkey in newapps[newAppskey].actions) { - const tmpaction = newapps[newAppskey].actions[actionsSubkey] - if (tmpaction.name.replaceAll(" ", "_").toLowerCase() === parsedname) { - console.log("MATCH!: ", newapps[newAppskey]) - curaction.matching_actions.push({ - "app_name": newapps[newAppskey].name, - "app_version": newapps[newAppskey].app_version, - "app_id": newapps[newAppskey].id, - "action": tmpaction, - "large_image": newapps[newAppskey].large_image, - "app_index": newAppskey, - "action_index": actionsSubkey, - }) - } + curaction.matching_actions = [] + for (var newAppskey in newapps) { + for (let actionsSubkey in newapps[newAppskey].actions) { + const tmpaction = newapps[newAppskey].actions[actionsSubkey] + if (tmpaction.name.replaceAll(" ", "_").toLowerCase() === parsedname) { + console.log("MATCH!: ", newapps[newAppskey]) + curaction.matching_actions.push({ + "app_name": newapps[newAppskey].name, + "app_version": newapps[newAppskey].app_version, + "app_id": newapps[newAppskey].id, + "action": tmpaction, + "large_image": newapps[newAppskey].large_image, + "app_index": newAppskey, + "action_index": actionsSubkey, + }) + } + } + } + } + + if (!curapp || curapp === undefined) { + // Check local storage has it + const foundapps = localStorage.getItem("apps") + if (foundapps !== null && foundapps !== undefined) { + const parsedapps = JSON.parse(foundapps) + if (parsedapps !== null && parsedapps !== undefined && parsedapps.length > 0) { + for (let appkey in parsedapps) { + if (parsedapps[appkey].name === curaction.app_name) { + curapp = parsedapps[appkey] + break } } } - if (!curapp || curapp === undefined) { - console.log("APPS - couldn't find it: ", newapps) + } else { + console.log("No apps found in local storage") + } + } + if (!curapp || curapp === undefined) { const tmpapp = { name: curaction.app_name, app_name: curaction.app_name, @@ -4361,7 +4463,7 @@ const AngularWorkflow = (defaultprops) => { } else { if (refresh === true) { setHighlightedApp(appid) - //toast("App activated for your organization! Refresh the page to use the app.") + //toast("App activated for your organisation! Refresh the page to use the app.") getApps() } @@ -6037,7 +6139,6 @@ const AngularWorkflow = (defaultprops) => { currentNode.data.isDescriptor ) { found = true; - console.log("FOUND THE NODE!"); break; } } @@ -8075,6 +8176,7 @@ const AngularWorkflow = (defaultprops) => { const AppView = (props) => { const { allApps, prioritizedApps, filteredApps, extraApps } = props; + //extraApps, const [visibleApps, setVisibleApps] = React.useState( Array.prototype.concat.apply( @@ -8090,9 +8192,9 @@ const AngularWorkflow = (defaultprops) => { const app = props.app; const [hover, setHover] = React.useState(false); - if (app.id === "" || app.name === "") { - return null - } + if (app.id === "" || app.name === "") { + return null + } const maxlen = 24; var newAppname = app.name; @@ -8103,9 +8205,9 @@ const AngularWorkflow = (defaultprops) => { newAppname = newAppname.replaceAll("_", " "); - if (app.large_image === undefined || app.large_image === null || app.large_image === "") { - app.large_image = theme.palette.defaultImage - } + if (app.large_image === undefined || app.large_image === null || app.large_image === "") { + app.large_image = theme.palette.defaultImage + } const image = app.large_image !== undefined && app.large_image !== null && app.large_image !== "" ? app.large_image : theme.palette.defaultImage @@ -8611,11 +8713,14 @@ const AngularWorkflow = (defaultprops) => { event.target.blur(event); } }} + onChange={(event) => { + runSearch(event.target.value) + }} onBlur={(event) => { //navigate(`?q=${event.target.value}`) - runSearch(event.target.value); + //runSearch(event.target.value) }} /> {visibleApps.length > extraApps.length ? ( @@ -8625,6 +8730,10 @@ const AngularWorkflow = (defaultprops) => { return null; } + if (app.id === "integration" && userdata.support !== true) { + return null + } + var extraMessage = "" if (index == 2) { extraMessage =
    @@ -8655,7 +8764,7 @@ const AngularWorkflow = (defaultprops) => { }} > - Click one of the relevant public apps below to Activate it for your organization. + Click one of the relevant public apps below to Activate it for your organisation. { console.log("CLICKED") @@ -8667,19 +8776,22 @@ const AngularWorkflow = (defaultprops) => {
    : -
    } +
    + + Apps need to be activated before they can be used. Search from our 2500+ apps to activate them for your organisation. + +
    + }
    ) : apps.length > 0 ? (
    { console.log("Should load in extra apps?") }} > - Couldn't find the apps you were looking for? Searching unactivated apps. Click one of the below apps to Activate it for your organization. + Couldn't find the apps you were looking for? Searching unactivated apps. Click one of these apps to Activate it for your organisation. { console.log("CLICKED") @@ -8741,12 +8853,20 @@ const AngularWorkflow = (defaultprops) => { return } - console.log("action input: ", e) + if (selectedApp.actions.length === 1) { + // Find if there's a new app + const newApp = apps.find((app) => (app.name === selectedApp.name && app.app_version !== selectedApp.app_version) || app.id == selectedApp.id) + if (newApp !== undefined && newApp !== null) { - const newaction = selectedApp.actions.find( - (a) => a.name === e.target.value - ); + if (selectedApp.actions !== undefined && selectedApp.actions !== null && selectedApp.actions.length > 1) { + setSelectedApp(newApp) + } + selectedApp.actions = newApp.actions + } + } + + const newaction = selectedApp.actions.find((a) => a.name === e.target.value) if (newaction === undefined || newaction === null) { toast("Failed to find the action you selected. Please try again or contact support@shuffler.io if it persists."); return; @@ -8833,7 +8953,6 @@ const AngularWorkflow = (defaultprops) => { if (newSelectedAction.app_name === "Shuffle Tools") { const iconInfo = GetIconInfo(newSelectedAction); - console.log("ICONINFO: ", iconInfo); const svg_pin = ``; const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); newSelectedAction.large_image = svgpin_Url; @@ -8860,8 +8979,6 @@ const AngularWorkflow = (defaultprops) => { // Further checks if those fields are already set in a previously used action newSelectedAction = RunAutocompleter(newSelectedAction); - console.log("newaction: ", newaction) - if ( newaction.return !== undefined && newaction.return !== null && @@ -13927,6 +14044,10 @@ const AngularWorkflow = (defaultprops) => { return null } + if (userdata.active_org === undefined || userdata.active_org === null) { + return null + } + const isCorrectOrg = workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id return ( @@ -13959,7 +14080,7 @@ const AngularWorkflow = (defaultprops) => { Warning: Change { - toast("Changing to correct organization. Please wait a few seconds.") + toast("Changing to correct organisation. Please wait a few seconds.") localStorage.setItem("globalUrl", ""); localStorage.setItem("getting_started_sidebar", "open"); @@ -13994,7 +14115,7 @@ const AngularWorkflow = (defaultprops) => { window.location.reload(); }, 2000); - toast("Successfully changed active organization - refreshing!"); + toast("Successfully changed active organisation - refreshing!"); } else { if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { toast(responseJson.reason); @@ -14316,7 +14437,7 @@ const AngularWorkflow = (defaultprops) => { ) } - const shownErrors = !isMobile && !workflow.public && workflow.errors !== undefined && workflow.errors !== null && workflow.errors.length > 0 && showErrors ? + const shownErrors = !isMobile && workflow.errors !== undefined && workflow.errors !== null && workflow.errors.length > 0 && showErrors && (!workflow.public || userdata.support === true) ?
    { {/**/} - - Workflow Issues: {workflow.errors.length} { )} {/*userdata.avatar === creatorProfile.github_avatar ? null :*/} - +
    ) @@ -17006,6 +17123,10 @@ const AngularWorkflow = (defaultprops) => { return "You can't use localhost in apps. Use the external ip or url of the server instead" } + if (stringjson.includes("manifest unknown")) { + return "The app's Docker Image is not available in the environment yet. Re-run the app to force a re-download of the app. If the problem persists, contact support" + } + if (result.status !== 200 && stringjson.includes("192.168") || stringjson.includes("172.16") || stringjson.includes("10.0")) { return "Consider whether your Orborus environment can connect to a local IP or not." } @@ -17241,7 +17362,7 @@ const AngularWorkflow = (defaultprops) => { width: imgsize, height: imgsize, border: `2px solid ${statusColor}`, - filter: curapp === undefined ? "grayscale(100%)" : null, + filter: curapp === undefined ? "grayscale(100%)" : null, }} /> )} @@ -19079,6 +19200,8 @@ const AngularWorkflow = (defaultprops) => {
    const changeActionParameterCodeMirror = (event, count, data, actionlist) => { + // Check if event.target.value is an array. If it is, split with comma + if (data.startsWith("${") && data.endsWith("}")) { // PARAM FIX - Gonna use the ID field, even though it's a hack const paramcheck = selectedAction.parameters.find(param => param.name === "body") diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index f8588bbb..354a60c3 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -2073,7 +2073,7 @@ const AppCreator = (defaultprops) => { }; if (item.action_label !== undefined && item.action_label !== "" && item.action_label !== "No Label") { - console.log("Action label: ", item.action_label) + //console.log("Action label: ", item.action_label) data.paths[item.url][item.method.toLowerCase()]["x-label"] = item.action_label } diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index ca0fc949..ae4c9b93 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -82,11 +82,16 @@ const chipStyle = { // Fixes names by making them uppercase and such // Used for labels. A lot of places don't use this yet export const FixName = (name) => { + if (name === undefined || name === null) { + return "" + } + const newAppname = ( name.charAt(0).toUpperCase() + name.substring(1) - ).replaceAll("_", " "); - return newAppname; -}; + ).replaceAll("_", " ") + + return newAppname +} // Takes input of e.g. $node.data.#.asd and a matching value from a json blob @@ -1832,7 +1837,11 @@ const Apps = (props) => { }) .then((responseJson) => { if (responseJson.success === false) { - toast("Failed to activate the app") + if (responseJson.reason !== undefined) { + toast("Failed to activate the app: "+responseJson.reason); + } else { + toast("Failed to activate the app"); + } } else { //toast("App activated for your organization! Refresh the page to use the app.") if (appExists) { @@ -2052,7 +2061,7 @@ const Apps = (props) => { to={`/apps/edit/${selectedApp.id}`} style={{ textDecoration: "none", color: "inherit" }} > - {selectedApp.name} + {FixName(selectedApp.name)} ) : null} diff --git a/frontend/src/views/Search.jsx b/frontend/src/views/Search.jsx index 1f696aad..b19050c7 100644 --- a/frontend/src/views/Search.jsx +++ b/frontend/src/views/Search.jsx @@ -1,33 +1,35 @@ import React, { useState, useEffect } from "react"; import theme from '../theme.jsx'; -import {isMobile} from "react-device-detect"; +import { isMobile } from "react-device-detect"; import AppGrid from "../components/AppGrid.jsx" import WorkflowGrid from "../components/WorkflowGrid.jsx" import CreatorGrid from "../components/CreatorGrid.jsx" import DocsGrid from "../components/DocsGrid.jsx" +import DiscordChat from "../components/DiscordChat.jsx"; import { useNavigate } from "react-router-dom"; -import { - Tabs, - Tab, +import { + Tabs, + Tab, } from "@mui/material"; import { Apps as AppsIcon, Code as CodeIcon, + Chat as ChatIcon, EmojiObjects as EmojiObjectsIcon, - Description as DescriptionIcon, + Description as DescriptionIcon, } from "@mui/icons-material"; // Should be different if logged in :| const Search = (props) => { - const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader} = props; + const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader } = props; let navigate = useNavigate(); - const [curTab, setCurTab] = useState(0); - const iconStyle = { marginRight: isHeader ? null : 10 }; + const [curTab, setCurTab] = useState(0); + const iconStyle = { marginRight: isHeader ? null : 10 }; useEffect(() => { if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { @@ -56,7 +58,7 @@ const Search = (props) => { maxWidth: 1024, scrollX: "hidden", overflowX: "hidden", - justifyContent: isHeader ? "center" : null, + justifyContent: isHeader ? "center" : null, } const boxStyle = { @@ -68,49 +70,52 @@ const Search = (props) => { paddingRight: isHeader ? null : 30, paddingBottom: isHeader ? null : 30, paddingTop: hidemargins === true ? 0 : isHeader ? null : 30, - display: "flex", + display: "flex", flexDirection: "column", overflowX: "hidden", minHeight: 400, } const views = { - 0: "apps", - 1: "workflows", - 2: "docs", - 3: "creators", - } + 0: "apps", + 1: "workflows", + 2: "docs", + 3: "creators", + 4: "discord", + } const setConfig = (event, inputValue) => { const newValue = parseInt(inputValue) - setCurTab(newValue) - if (newValue === 0) { - document.title = "Shuffle - search - apps"; - } else if (newValue === 1) { - document.title = "Shuffle - search - workflows"; - } else if (newValue === 2) { - document.title = "Shuffle - search - documentation"; - } else if (newValue === 3) { - document.title = "Shuffle - search - creators"; - } else { - document.title = "Shuffle - search"; - } + setCurTab(newValue) + if (newValue === 0) { + document.title = "Shuffle - search - apps"; + } else if (newValue === 1) { + document.title = "Shuffle - search - workflows"; + } else if (newValue === 2) { + document.title = "Shuffle - search - documentation"; + } else if (newValue === 3) { + document.title = "Shuffle - search - creators"; + } else if (newValue === 4) { + document.title = "Shuffle - search - Discord Chat"; + }else { + document.title = "Shuffle - search"; + } + - const urlSearchParams = new URLSearchParams(window.location.search) const params = Object.fromEntries(urlSearchParams.entries()) const foundQuery = params["q"] var extraQ = "" if (foundQuery !== null && foundQuery !== undefined) { - extraQ = "&q="+foundQuery + extraQ = "&q=" + foundQuery } - + if ((serverside === false || serverside === undefined) && window.location.pathname.includes("/search")) { - navigate(`/search?tab=${views[newValue]}`+extraQ) + navigate(`/search?tab=${views[newValue]}` + extraQ) } - } + } if (isLoaded === false) { return null @@ -118,18 +123,18 @@ const Search = (props) => { // Random names for type & autoComplete. Didn't research :^) - const landingpageDataBrowser = -
    + const landingpageDataBrowser = +
    @@ -143,36 +148,46 @@ const Search = (props) => { /> - Docs + Docs /> - Creators + Creators /> + + Discord Chat + + /> + - {curTab === 0 ? + {curTab === 0 ? - : - curTab === 1 ? - window.location.pathname === "/search" ? - + : + curTab === 1 ? + window.location.pathname === "/search" ? + + : + : - - : - curTab === 2 ? - - : - curTab === 3 ? - - : - null} + curTab === 2 ? + + : + curTab === 3 ? + + : + curTab === 4 ? + +: + + null}
    //{/*alternativeView={true} />*/} - const loadedCheck = isLoaded ? + const loadedCheck = isLoaded ?
    {landingpageDataBrowser}
    @@ -181,7 +196,7 @@ const Search = (props) => {
    // #1f2023? - return( + return (
    {loadedCheck}
    diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index be99104c..094b207e 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -9,14 +9,16 @@ import { Button, Divider, TextField, + Modal, } from "@mui/material"; //import { useAlert -import { ToastContainer, toast } from "react-toastify" +import { ToastContainer, toast } from "react-toastify"; +import "../codeeditor-index.css"; import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material"; import IconButton from "@mui/material/IconButton"; import { Tooltip } from "@mui/material"; - +import CloseIcon from "@mui/icons-material/Close"; const Settings = (props) => { const { globalUrl, isLoaded, userdata, setUserData } = props; @@ -44,14 +46,13 @@ const Settings = (props) => { // Used for error messages etc const [passwordFormMessage, setPasswordFormMessage] = useState(""); - const [firstrequest, setFirstRequest] = useState(true); - const [userSettings, setUserSettings] = useState({}); - const [showApiKey, setShowApiKey] = useState(false); const [apiKeyCopied, setApiKeyCopied] = useState(false); + const [accountDeleteButtonClicked, setAccountDeleteButtonClicked] = useState(false); + const handleCopyApiKey = () => { navigator.clipboard.writeText(userSettings.apikey); setApiKeyCopied(true); @@ -135,6 +136,241 @@ const Settings = (props) => { return currentOwner; }; + const handleAccountDelete = () => { + setAccountDeleteButtonClicked(true); + }; + + const DeleteAccountPopUp = () => { + const [userDeleteAccepted, setUserDeleteAccepted] = useState(false); + const [password, setPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [disabled, setDisabled] = useState(true); + const [open, setOpen] = useState(true); + + const boxStyling = { + position: "relative", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + zIndex: "9999", + backgroundColor: "#1a1a1a", + color: "white", + padding: 20, + borderRadius: 5, + boxShadow: "0 0 10px rgba(0, 0, 0, 0.3)", + width: 430, + height: 430, + }; + + const closeIconButtonStyling = { + color: "white", + border: "none", + backgroundColor: "transparent", + marginLeft: "90%", + width: 20, + height: 20, + cursor: "pointer", + }; + + const handlePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + const buttonStyle = { + marginTop: 20, + height: 50, + border: "none", + width: "100%", + fontSize: 16, + backgroundColor: disabled ? "gray" : "red", + color: "white", + cursor: disabled === false && "pointer", + }; + const checkboxStyle = { + position: "relative", + cursor: "pointer", + display: "inline-block", + width: 20, + height: 20, + backgroundColor: "#ccc", + borderRadius: 4, + }; + + const checkmarkStyle = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + content: "", + width: 10, + height: 10, + backgroundColor: "#fff", + borderRadius: 2, + display: "none", + }; + + + const handlePasswordChange = (e) => { + setPassword(e.target.value); + }; + + const handleCheckBoxEvent = () => { + setUserDeleteAccepted(!userDeleteAccepted); + }; + + useEffect(() => { + if (password.length > 8 && userDeleteAccepted) { + setDisabled(false); + } else { + setDisabled(true); + } + }, [password, userDeleteAccepted]); + + function removeAllCookies() { + + var cookies = document.cookie.split(";"); + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i]; + var eqPos = cookie.indexOf("="); + var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie; + document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/"; + } + } + + const handleDeleteAccount = () => { + const baseURL = globalUrl; + const userID = userdata.id; + + const url = `${baseURL}/api/v1/users/${userID}/remove`; + + fetch(url, { + mode: "cors", + method: "DELETE", + body: JSON.stringify({ password }), + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => response.json()) + .then((data) => { + if (data.success) { + toast.success( + "Your account delete successfully! redirecting in 3 sec.." + ); + removeAllCookies(); + + window.location.pathname = "/"; + } else { + toast.error(`${data.reason}`); + } + }) + .catch((error) => { + console.error( + "There was a problem with your fetch operation:", + error + ); + }); + }; + + return ( + +
    + +

    Account

    + {/*
    */} +
    + +
      +
    • + +
    • +
    • + +
    • +
    +
    + + +
    +
    + + + {showPassword ? : } + + ), + }} + /> +
    + +
    +
    + + ); + }; + const onPasswordChange = () => { const data = { username: userSettings.username, @@ -727,7 +963,8 @@ const Settings = (props) => { > Submit password change -

    {passwordFormMessage}

    +

    {passwordFormMessage}

    + {isCloud && ( <> @@ -820,6 +1057,22 @@ const Settings = (props) => {
    +

    Danger Area

    + + {loadedValidationWorkflows !== undefined && loadedValidationWorkflows !== null ? loadedValidationWorkflows.map((data, index) => { @@ -831,6 +1084,7 @@ const Settings = (props) => { }) : null} + {accountDeleteButtonClicked && }
    ); @@ -888,47 +1142,47 @@ const Settings = (props) => { console.log("redirect: ", redirectUri) - const client_id = "3d272b1b782b100b1e61" - const username = userdata.id; - const scopes = "read:user"; + const client_id = "3d272b1b782b100b1e61" + const username = userdata.id; + const scopes = "read:user"; - const url = `https://github.com/login/oauth/authorize?access_type=offline&prompt=consent&client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${scopes}&state=username%3D${username}%26type%3Dgithub` + const url = `https://github.com/login/oauth/authorize?access_type=offline&prompt=consent&client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${scopes}&state=username%3D${username}%26type%3Dgithub` - console.log("URL: ", url); + console.log("URL: ", url); - var newwin = window.open(url, "", "width=800,height=600"); + var newwin = window.open(url, "", "width=800,height=600"); - // Check whether we got a callback somewhere - //var id = setInterval(function () { - // fetch( - // globalUrl + "/api/v1/triggers/gmail/" + selectedTrigger.id, - // { - // method: "GET", - // headers: { "content-type": "application/json" }, - // credentials: "include", - // } - // ) - // .then((response) => { - // if (response.status !== 200) { - // throw new Error("No trigger info :o!"); - // } + // Check whether we got a callback somewhere + //var id = setInterval(function () { + // fetch( + // globalUrl + "/api/v1/triggers/gmail/" + selectedTrigger.id, + // { + // method: "GET", + // headers: { "content-type": "application/json" }, + // credentials: "include", + // } + // ) + // .then((response) => { + // if (response.status !== 200) { + // throw new Error("No trigger info :o!"); + // } - // return response.json(); - // }) - // .then((responseJson) => { - // console.log("RESPONSE: "); - // setTriggerAuthentication(responseJson); - // clearInterval(id); - // newwin.close(); - // setGmailFolders(); - // }) - // .catch((error) => { - // console.log(error.toString()); - // }); - //}, 2500); + // return response.json(); + // }) + // .then((responseJson) => { + // console.log("RESPONSE: "); + // setTriggerAuthentication(responseJson); + // clearInterval(id); + // newwin.close(); + // setGmailFolders(); + // }) + // .catch((error) => { + // console.log(error.toString()); + // }); + //}, 2500); - //saveWorkflow(workflow); - } + //saveWorkflow(workflow); + } const loadedCheck = isLoaded && !firstrequest ? ( diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index bf21e96b..707249e7 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -128,6 +128,7 @@ const useStyles = makeStyles((theme) => ({ export const GetIconInfo = (action) => { // Finds the icon based on the action. Should be verbs. const iconList = [ + { key: "cases", values: ["cases"] }, { key: "cache_add", values: ["set_cache"] }, { key: "cache_get", values: ["get_cache"] }, { key: "filter", values: ["filter"] }, @@ -222,6 +223,13 @@ export const GetIconInfo = (action) => { const defaultColor = "#f76b1c"; const defaultGradient = ["#fad961", "#f76b1c"]; const parsedIcons = { + cases: { + icon: "M11 3C6.58 3 3 4.79 3 7C3 9.21 6.58 11 11 11C15.42 11 19 9.21 19 7C19 4.79 15.42 3 11 3ZM3 9V12C3 14.21 6.58 16 11 16C15.42 16 19 14.21 19 12V9C19 11.21 15.42 13 11 13C6.58 13 3 11.21 3 9ZM3 14V17C3 19.21 6.58 21 11 21C12.41 21 13.79 20.81 15 20.46V17.46C13.79 17.81 12.41 18 11 18C6.58 18 3 16.21 3 14ZM20 14V17H17V19H20V22H22V19H25V17H22V14", + iconColor: "white", + iconBackgroundColor: "#8acc3f", + originalIcon: "", + fillGradient: ["#8acc3f", "#459622"], + }, cache_add: { icon: "M11 3C6.58 3 3 4.79 3 7C3 9.21 6.58 11 11 11C15.42 11 19 9.21 19 7C19 4.79 15.42 3 11 3ZM3 9V12C3 14.21 6.58 16 11 16C15.42 16 19 14.21 19 12V9C19 11.21 15.42 13 11 13C6.58 13 3 11.21 3 9ZM3 14V17C3 19.21 6.58 21 11 21C12.41 21 13.79 20.81 15 20.46V17.46C13.79 17.81 12.41 18 11 18C6.58 18 3 16.21 3 14ZM20 14V17H17V19H20V22H22V19H25V17H22V14", iconColor: "white", From 9908c9adada44be796406e8794dbd858c1863839 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 18 Apr 2024 04:11:57 +0200 Subject: [PATCH 061/142] Added discordchat searchengine file --- backend/go-app/go.mod | 2 +- frontend/src/components/DiscordChat.jsx | 222 ++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/DiscordChat.jsx diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c425a292..adfd9643 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.2 + github.com/shuffle/shuffle-shared v0.6.14 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 diff --git a/frontend/src/components/DiscordChat.jsx b/frontend/src/components/DiscordChat.jsx new file mode 100644 index 00000000..e1d226b8 --- /dev/null +++ b/frontend/src/components/DiscordChat.jsx @@ -0,0 +1,222 @@ +import React, { useState, useEffect } from 'react'; +import algoliasearch from 'algoliasearch'; +import theme from '../theme.jsx'; +import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom'; +import { + Grid, + Paper, + TextField, + Typography, + Button, + InputAdornment, + Avatar, + List, + ListItem, + ListItemAvatar, + ListItemText, +} from '@mui/material'; +import { Search as SearchIcon } from '@mui/icons-material'; + + +const searchClient = algoliasearch("JNSS5CFDZZ", "1e5f29b1550939855de5915eac3bf5f7"); + +const DiscordChat = props => { + const { isMobile, globalUrl } = props + const [value, setValue] = useState(""); + const [formMail, setFormMail] = React.useState(""); + const [message, setMessage] = React.useState(""); + const [formMessage, setFormMessage] = React.useState(""); + const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} + + const borderRadius = 3 + + const submitContact = (email, message) => { + const data = { + "firstname": "", + "lastname": "", + "title": "", + "companyname": "", + "email": email, + "phone": "", + "message": message, + } + + const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." + + fetch(globalUrl+"/api/v1/contact", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(response => { + if (response.success === true) { + setFormMessage(response.reason) + //toast("Thanks for submitting!") + } else { + setFormMessage(errorMessage) + } + + setFormMail("") + setMessage("") + }) + .catch(error => { + setFormMessage(errorMessage) + console.log(error) + }); + } + + const SearchBox = ({ currentRefinement, refine }) => { + return ( +
    + refine(event.currentTarget.value)} + placeholder="Search Discord Chats" + style={{ backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%", }} + InputProps={{ + style: { + color: "white", + fontSize: "1em", + height: 50, + }, + startAdornment: ( + + + + ), + }} + /> + + ); + }; + + const highlightText = (text) => { + if (!Array.isArray(text.matchedWords) || text.matchedWords.length === 0) { + return text.value; + } + + let highlightedText = ''; + let currentIndex = 0; + + text.matchedWords.forEach((word, index) => { + const startIndex = text.value.toLowerCase().indexOf(word.toLowerCase(), currentIndex); + const endIndex = startIndex + word.length; + highlightedText += text.value.substring(currentIndex, startIndex); + highlightedText += `${text.value.substring(startIndex, endIndex)}`; + currentIndex = endIndex; + }); + + highlightedText += text.value.substring(currentIndex); + return ; + }; + + const Hits = ({ hits }) => { + const handleHitClick = (url) => { + const modifiedUrl = url.replace('https://ptb.discord.com/', 'https://discord.com/'); + window.open(modifiedUrl, '_blank'); + }; + if (hits.length === 0) { + return No results found. Try refining your search.; + } + return ( + + {hits.map((chat, index) => ( + handleHitClick(chat.url)} style={{ cursor: "pointer", borderBottom: "1px solid rgba(255,255,255,0.4)" }}> + + + + + + ))} + + ); + }; + + const CustomSearchBox = connectSearchBox(SearchBox); + const CustomHits = connectHits(Hits); + + return ( +
    + +
    + +
    +
    + +
    +
    +
    + + Can't find what you're looking for? + +
    + setFormMail(e.target.value)} + /> + setMessage(e.target.value)} + /> +
    + + {formMessage} +
    + + + Search by + + + Algolia logo + + +
    + ); +}; + +export default DiscordChat; From e0fc97f590f3522d5b0a56e73c4f5da31580e7c0 Mon Sep 17 00:00:00 2001 From: Frikky Date: Sun, 21 Apr 2024 23:11:25 +0200 Subject: [PATCH 062/142] Fixed #1365 --- backend/app_sdk/app_base.py | 2 +- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index be5bee26..54b238e3 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -138,7 +138,7 @@ def as_object(a): return json.loads(str(a)) @shuffle_filters.register -def ast(a): +def ast_eval(a): return ast.literal_eval(str(a)) @shuffle_filters.register diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index adfd9643..f9bfdc7f 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -53,7 +53,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/frikky/schemaless v0.0.6 // indirect + github.com/frikky/schemaless v0.0.8 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 81f02ce3..f1acfbbb 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -178,8 +178,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= -github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= -github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.8 h1:9ekdXCgVSKb18g4e6/Jr0C3d5L1gfpYhJoJwQOOMMJo= +github.com/frikky/schemaless v0.0.8/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= @@ -457,8 +457,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.6.2 h1:wcy7rc8QnF18uTsicG7L+9Ce1Ah/ol7Jc8t5nu9p9/Y= -github.com/shuffle/shuffle-shared v0.6.2/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.6.14 h1:ZO6Stk5d+ZvH+cDNOJ844ex0EXmzLf0iMVep/NSj/g8= +github.com/shuffle/shuffle-shared v0.6.14/go.mod h1:fsWCs0nsCS/3PN9BHl6qPN0PrTctT/TWUMH9ElTsKTg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= From 2b64d71f053a9cb088f3a3ac8b44aa8c47cc72bf Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 23 Apr 2024 11:11:22 +0000 Subject: [PATCH 063/142] added webhooks to the trigger view --- backend/go-app/main.go | 2 +- frontend/src/views/Admin.jsx | 540 ++++++++++++++++++++++++++++------- 2 files changed, 433 insertions(+), 109 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 52d8bc78..45bd1b83 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4907,7 +4907,7 @@ func initHandlers() { r.HandleFunc("/api/v1/triggers/outlook/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS") - + //r.HandleFunc("/api/v1/triggers/all", shuffle.HandleGetTriggers).Methods("GET", "OPTIONS") //r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS") diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index a68a9831..aa866c0f 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -192,6 +192,8 @@ const Admin = (props) => { const [showApiKey, setShowApiKey] = useState(false); const [billingInfo, setBillingInfo] = React.useState({}); const [selectedStatus, setSelectedStatus] = React.useState([]); + const [webHooks, setWebHooks] = React.useState([]); + const [allSchedules, setAllSchedules] = React.useState([]); const [, forceUpdate] = React.useState(); @@ -231,6 +233,9 @@ const Admin = (props) => { else console.log("error in user data") }, [userdata]); + useEffect(() => { + handleGetAllTriggers() + }, []); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; @@ -560,6 +565,31 @@ If you're interested, please let me know a time that works for you, or set up a }); }; + const handleGetAllTriggers = () => { + fetch(globalUrl + "/api/v1/triggers/all", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for getting all triggers"); + } + + return response.json(); + }) + .then((responseJson) => { + setWebHooks(responseJson.webhooks || []); // Handling the case where the result is null or undefined + setAllSchedules(responseJson.schedules || []); + }) + .catch((error) => { + toast(error.toString()); + }); + }; + const deleteSchedule = (data) => { // FIXME - add some check here ROFL console.log("INPUT: ", data); @@ -585,18 +615,150 @@ If you're interested, please let me know a time that works for you, or set up a if (responseJson["success"] === false) { toast("Failed stopping schedule"); } else { - setTimeout(() => { - getSchedules(); - }, 1500); - //toast("Successfully stopped schedule!") + toast("Successfully stopped schedule!"); } - }) + setTimeout(handleGetAllTriggers, 1000); + }), ) .catch((error) => { console.log("Error in userdata: ", error); }); }; + const startSchedule = (trigger) => { + if (trigger.name.length <= 0) { + toast("Error: name can't be empty"); + return; + } + + toast("Creating schedule"); + const data = { + name: trigger.name, + frequency: trigger.frequency, + execution_argument: trigger.argument, + environment: trigger.environment, + id: trigger.id, + start: trigger.start_node, + }; + + fetch(`${globalUrl}/api/v1/workflows/${trigger.workflow_id}/schedule`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + toast("Failed to set schedule: " + responseJson.reason); + } else { + toast("Successfully created schedule"); + } + setTimeout(handleGetAllTriggers, 1000); + }) + .catch((error) => { + //toast(error.toString()); + console.log("Get schedule error: ", error.toString()); + }); + }; + + const deleteWebhook = (trigger) => { + if (trigger === undefined) { + return; + } + + fetch(globalUrl + "/api/v1/hooks/" + trigger.id + "/delete", { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success) { + toast("Successfully stopped webhook"); + } else { + if (responseJson.reason !== undefined) { + toast("Failed stopping webhook: " + responseJson.reason); + } + } + setTimeout(handleGetAllTriggers, 1000); + }) + .catch((error) => { + toast( + "Delete webhook error. Contact support or check logs if this persists.", + ); + }); + }; + + const startWebHook = (trigger) => { + const hookname = trigger.info.name; + if (hookname.length === 0) { + toast("Missing name"); + return; + } + + if (trigger.id.length !== 36) { + toast("Missing id"); + return; + } + + toast("Starting webhook"); + + const data = { + name: hookname, + type: "webhook", + id: trigger.id, + workflow: trigger.workflows[0], + start: trigger.start, + environment: trigger.environment, + auth: trigger.auth, + custom_response: trigger.custom_response, + version: trigger.version, + version_timeout: 15, + }; + + console.log("Trigger data: ", data); + + fetch(globalUrl + "/api/v1/hooks/new", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((response) => response.json()) + .then((responseJson) => { + if (responseJson.success) { + // Set the status + toast("Successfully started webhook"); + } else { + toast("Failed starting webhook: " + responseJson.reason); + } + setTimeout(handleGetAllTriggers, 1000); + }) + .catch((error) => { + //console.log(error.toString()); + console.log("New webhook error: ", error.toString()); + }); + }; + if (userdata.support === true && selectedOrganization.id !== "" && selectedOrganization.id !== undefined && selectedOrganization.id !== null && selectedOrganization.id !== userdata.active_org.id) { toast("Refreshing window to fix org support access") @@ -3923,110 +4085,272 @@ If you're interested, please let me know a time that works for you, or set up a /> const schedulesView = - curTab === 5 ? ( -
    -
    -

    Schedules

    - - Schedules used in Workflows. Makes locating and control easier.{" "} - - Learn more - - -
    - - - - - - - - - - - {schedules === undefined || schedules === null - ? null - : schedules.map((schedule, index) => { - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } - - return ( - - 0 ? - schedule.frequency - : - {schedule.seconds} seconds - } - /> - - - {schedule.workflow_id} - - } - /> - - - - - - ); - })} - + curTab === 5 ? ( +
    +
    +

    Schedules

    + + Schedules used in Workflows. Makes locating and control easier.{" "} + + Learn more + +
    - ) : null; + + + + + + + + + + + {allSchedules === undefined || allSchedules === null + ? null + : allSchedules.map((schedule, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + return ( + + 0 ? ( + schedule.frequency + ) : ( + {schedule.seconds} seconds + ) + } + /> + + + {schedule.workflow_id} + + } + /> + + + + + + ); + })} + + +
    +

    WebHooks

    +
    + + + + + + + + + + + + {webHooks === undefined || webHooks === null + ? null + : webHooks.map((webhook, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + return ( + + + + + {webhook.workflows[0]} + + } + /> + + + { + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(webhook.info.url); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999, + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + + toast("URL copied to clipboard"); + } + }} + > + + + + ) + } + /> + + + + + + ); + })} + + + {/*
    +

    Tenzir Pipelines

    + + Controls a pipeline to run things.{" "} + + Learn more + + +
    + + */} +
    +) : null; const appCategoryView = curTab === 8 ? ( From 2ac6001297fe59b108e76bde9c8591ad0c3e49ed Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 23 Apr 2024 23:28:48 +0200 Subject: [PATCH 064/142] Bumped everything to match shuffle-shared --- backend/go-app/go.mod | 4 +- backend/go-app/go.sum | 6 +-- functions/onprem/orborus/orborus.go | 83 ++++++++++++++++++++++------- functions/onprem/worker/go.mod | 4 +- functions/onprem/worker/go.sum | 10 ++++ functions/onprem/worker/worker.go | 40 ++++++++------ 6 files changed, 106 insertions(+), 41 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index f9bfdc7f..d11b6e37 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared go 1.19 @@ -53,7 +53,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/frikky/schemaless v0.0.8 // indirect + github.com/frikky/schemaless v0.0.9 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index f1acfbbb..c12f4bd9 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -178,8 +178,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= -github.com/frikky/schemaless v0.0.8 h1:9ekdXCgVSKb18g4e6/Jr0C3d5L1gfpYhJoJwQOOMMJo= -github.com/frikky/schemaless v0.0.8/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2+8= +github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= @@ -457,8 +457,6 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.6.14 h1:ZO6Stk5d+ZvH+cDNOJ844ex0EXmzLf0iMVep/NSj/g8= -github.com/shuffle/shuffle-shared v0.6.14/go.mod h1:fsWCs0nsCS/3PN9BHl6qPN0PrTctT/TWUMH9ElTsKTg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 7e01b46c..042fc6d6 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -74,6 +74,7 @@ var newWorkerImage = os.Getenv("SHUFFLE_WORKER_IMAGE") var dockerSwarmBridgeMTU = os.Getenv("SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU") var dockerSwarmBridgeInterface = os.Getenv("SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE") var isKubernetes = os.Getenv("IS_KUBERNETES") +var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") var maxCPUPercent = 95 // var baseimagename = "docker.pkg.github.com/shuffle/shuffle" @@ -103,6 +104,7 @@ var orborusLabel = os.Getenv("SHUFFLE_ORBORUS_LABEL") var memcached = os.Getenv("SHUFFLE_MEMCACHED") var executionIds = []string{} +var namespacemade = false // For K8s var dockercli *dockerclient.Client var containerId string @@ -663,15 +665,52 @@ func handleBackendImageDownload(ctx context.Context, images 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" { - if len(os.Getenv("REGISTRY_URL")) > 0 && os.Getenv("REGISTRY_URL") != "" { - env = append(env, fmt.Sprintf("REGISTRY_URL=%s", os.Getenv("REGISTRY_URL"))) - env = append(env, fmt.Sprintf("IS_KUBERNETES=%s", os.Getenv("IS_KUBERNETES"))) + env = append(env, fmt.Sprintf("IS_KUBERNETES=%s", os.Getenv("IS_KUBERNETES"))) + env = append(env, fmt.Sprintf("KUBERNETES_NAMESPACE=%s", os.Getenv("KUBERNETES_NAMESPACE"))) + + clientset, err := getKubernetesClient() + if err != nil { + log.Printf("[ERROR] Error getting kubernetes client:", err) + return err } - image = os.Getenv("SHUFFLE_KUBERNETES_WORKER") - log.Printf("[DEBUG] using worker image:", image) + // Check if namespace exist as variable. If so, make it + if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 && !namespacemade { + kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") + + // Make the namespace + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: os.Getenv("KUBERNETES_NAMESPACE"), + }, + } + + _, err := clientset.CoreV1().Namespaces().Create(context.Background(), namespace, metav1.CreateOptions{}) + if err != nil { + if !strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already exists") { + log.Printf("[ERROR] Failed creating Kubernetes namespace: %s", err) + } else { + namespacemade = true + } + } else { + namespacemade = true + } + } + + if len(kubernetesNamespace) == 0 { + kubernetesNamespace = "default" + } + + kubernetesImage := os.Getenv("SHUFFLE_KUBERNETES_WORKER") + if len(kubernetesImage) == 0 { + kubernetesImage = image + } + log.Printf("[DEBUG] Using Kubernetes worker image '%s'", kubernetesImage) // image = "shuffle-worker:v1" //hard coded image name to test locally envMap := make(map[string]string) @@ -682,12 +721,8 @@ func deployWorker(image string, identifier string, env []string, executionReques } } - clientset, err := getKubernetesClient() - if err != nil { - log.Printf("[ERROR] Error getting kubernetes client:", err) - return err - } - + // While testing: + // kubectl delete pods --all --all-namespaces; kubectl delete services --all --all-namespaces pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: identifier, @@ -695,27 +730,31 @@ func deployWorker(image string, identifier string, env []string, executionReques }, Spec: corev1.PodSpec{ RestartPolicy: "Never", - // once images is pushed, we can remove this - // keep this when running locally + DNSPolicy: "Default", // NodeSelector: map[string]string{ // "node": "master", // }, Containers: []corev1.Container{ { Name: identifier, - Image: image, + Image: kubernetesImage, Env: buildEnvVars(envMap), + + //ImagePullPolicy: "Never", + ImagePullPolicy: corev1.PullIfNotPresent, }, }, }, } + // Check if running on ARM or x86 to download the correct image + // Add environment variables // pod.Spec.Containers[0].Env = buildEnvVars(envMap) - createdPod, err := clientset.CoreV1().Pods("shuffle").Create(context.Background(), pod, metav1.CreateOptions{}) + createdPod, err := clientset.CoreV1().Pods(kubernetesNamespace).Create(context.Background(), pod, metav1.CreateOptions{}) if err != nil { - log.Printf("[ERROR] Failed creating pod: %v", err) + //log.Printf("[ERROR] Failed creating pod: %v", err) return err } @@ -1773,6 +1812,10 @@ func main() { executionIds = append(executionIds, execution.ExecutionId) } else { log.Printf("[WARNING] Execution ID '%s' failed to deploy: %s", execution.ExecutionId, err) + if strings.Contains(err.Error(), "already exists") { + toBeRemoved.Data = append(toBeRemoved.Data, execution) + executionIds = append(executionIds, execution.ExecutionId) + } } } @@ -1985,7 +2028,7 @@ func getRunningWorkers(ctx context.Context, workerTimeout int) int { //log.Printf("[DEBUG] Getting running workers with API version %s", dockerApiVersion) counter := 0 if isKubernetes == "true" { - log.Printf("[INFO] getting running workers in kubernetes") + log.Printf("[INFO] Getting running workers in kubernetes") thresholdTime := time.Now().Add(time.Duration(-workerTimeout) * time.Second) @@ -1996,7 +2039,7 @@ func getRunningWorkers(ctx context.Context, workerTimeout int) int { } labelSelector := "app=shuffle-worker" - pods, podErr := clientset.CoreV1().Pods("shuffle").List(ctx, metav1.ListOptions{ + pods, podErr := clientset.CoreV1().Pods(kubernetesNamespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) if podErr != nil { @@ -2009,6 +2052,10 @@ func getRunningWorkers(ctx context.Context, workerTimeout int) int { counter++ } } + + if counter > 0 { + log.Printf("[INFO] Found %d running workers in Orborus", counter) + } } else { containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index b51d219d..bae8288d 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/docker v23.0.3+incompatible github.com/gorilla/mux v1.8.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.98 + github.com/shuffle/shuffle-shared v0.6.16 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 @@ -38,7 +38,7 @@ require ( github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/frikky/kin-openapi v0.41.0 // indirect - github.com/frikky/schemaless v0.0.6 // indirect + github.com/frikky/schemaless v0.0.9 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index f2520ce2..3b7752e4 100644 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -89,6 +89,7 @@ github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnC github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -97,6 +98,8 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -122,11 +125,14 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2+8= +github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -224,6 +230,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -236,6 +243,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -365,6 +373,8 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shuffle/shuffle-shared v0.5.86 h1:ZHQgZ4siSWgi5gttxeSMdjsaH9SbGTzrA/GO6aICO2U= github.com/shuffle/shuffle-shared v0.5.86/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGSEja9zG6s= +github.com/shuffle/shuffle-shared v0.6.16/go.mod h1:HhQTn7xZZ69ZTc4EptO9OeNmgbKDyGlWAhFkUFUAHSA= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 58cb4de9..e47059f6 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -53,6 +53,7 @@ var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION")) var baseimagename = "frikky/shuffle" +var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") // var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") @@ -383,8 +384,11 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason // Deploys the internal worker whenever something happens func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error { if isKubernetes == "true" { - namespace := "shuffle" - localRegistry := os.Getenv("REGISTRY_URL") + if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 { + kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") + } else { + kubernetesNamespace = "default" + } envMap := make(map[string]string) for _, envStr := range env { @@ -397,7 +401,6 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] clientset, err := getKubernetesClient() if err != nil { log.Printf("[ERROR] Failed getting kubernetes: %s [INFO] Setting kubernetes to false to enable running Shuffle with Docker for the next iterations.", err) - isKubernetes = "false" return err } @@ -406,7 +409,8 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] value := strSplit[0] value = strings.ReplaceAll(value, "_", "-") - // checking if app is generated or not + // Checking if app is generated or not + localRegistry := os.Getenv("REGISTRY_URL") /* appDetails := strings.Split(image, ":")[1] appDetailsSplit := strings.Split(appDetails, "_") @@ -440,7 +444,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] } } - log.Printf("[DEBUG] Got kubernetes client to run image '%s'", image) + log.Printf("[DEBUG] Got kubernetes with namespace %#v to run image '%s'", kubernetesNamespace, image) //fix naming convention podUuid := uuid.NewV4().String() @@ -455,25 +459,30 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] }, }, Spec: corev1.PodSpec{ + RestartPolicy: "Never", // As a crash is not useful in this context + DNSPolicy: "Default", // NodeName: "worker1" - RestartPolicy: "Never", Containers: []corev1.Container{ { Name: value, Image: image, Env: buildEnvVars(envMap), - // ImagePullPolicy: corev1.PullAlways, + + // Pull if not available + ImagePullPolicy: corev1.PullIfNotPresent, }, }, }, } - createdPod, err := clientset.CoreV1().Pods(namespace).Create(context.Background(), pod, metav1.CreateOptions{}) + createdPod, err := clientset.CoreV1().Pods(kubernetesNamespace).Create(context.Background(), pod, metav1.CreateOptions{}) if err != nil { - fmt.Fprintf(os.Stderr, "Error creating pod: %v", err) + log.Printf("[ERROR] Failed creating pod: %v", err) // os.Exit(1) + } else { + log.Printf("[DEBUG] Created pod %#v in namespace %#v", createdPod.Name, kubernetesNamespace) } - log.Printf("[DEBUG] Created pod %q in namespace %q", createdPod.Name, createdPod.Namespace) + return nil } @@ -612,7 +621,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] return nil } -func cleanupExecution(clientset *kubernetes.Clientset, workflowExecution shuffle.WorkflowExecution, namespace string) error { +func cleanupKubernetesExecution(clientset *kubernetes.Clientset, workflowExecution shuffle.WorkflowExecution, namespace string) error { workerName := fmt.Sprintf("worker-%s", workflowExecution.ExecutionId) labelSelector := fmt.Sprintf("app=shuffle-app,executionId=%s", workflowExecution.ExecutionId) @@ -1385,7 +1394,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { log.Println("[ERROR] Error getting kubernetes client (1):", err) os.Exit(1) } - cleanupExecution(clientset, workflowExecution, "shuffle") + cleanupKubernetesExecution(clientset, workflowExecution, kubernetesNamespace) } else { shutdown(workflowExecution, "", "", true) } @@ -1595,7 +1604,7 @@ func handleSubflowPoller(ctx context.Context, workflowExecution shuffle.Workflow os.Exit(1) } - cleanupExecution(clientset, workflowExecution, "shuffle") + cleanupKubernetesExecution(clientset, workflowExecution, kubernetesNamespace) } else { shutdown(workflowExecution, "", "", true) } @@ -1691,7 +1700,7 @@ func handleDefaultExecutionWrapper(ctx context.Context, workflowExecution shuffl log.Println("[ERROR] Error getting kubernetes client (2):", err) os.Exit(1) } - cleanupExecution(clientset, workflowExecution, "shuffle") + cleanupKubernetesExecution(clientset, workflowExecution, kubernetesNamespace) } else { shutdown(workflowExecution, "", "", true) } @@ -1708,7 +1717,8 @@ func handleDefaultExecutionWrapper(ctx context.Context, workflowExecution shuffl log.Println("[ERROR] Error getting kubernetes client (3):", err) os.Exit(1) } - cleanupExecution(clientset, workflowExecution, "shuffle") + + cleanupKubernetesExecution(clientset, workflowExecution, kubernetesNamespace) } else { shutdown(workflowExecution, "", "", true) } From 9e8e01ed01631e10bc6b684fb30532db8a7ff672 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 24 Apr 2024 15:37:31 +0200 Subject: [PATCH 065/142] Moved to golang 1.22 and fixed some kubernetes bugs --- functions/onprem/orborus/Dockerfile | 27 +- functions/onprem/orborus/go.mod | 98 +- functions/onprem/orborus/go.sum | 207 ++-- functions/onprem/orborus/orborus.go | 102 +- functions/onprem/orborus/orborus.yaml | 82 ++ functions/onprem/worker/go.mod | 95 +- functions/onprem/worker/go.sum | 212 ++-- functions/onprem/worker/worker.go | 1432 +++++++++++++------------ 8 files changed, 1276 insertions(+), 979 deletions(-) create mode 100644 functions/onprem/orborus/orborus.yaml diff --git a/functions/onprem/orborus/Dockerfile b/functions/onprem/orborus/Dockerfile index 9eed284a..1396cf01 100755 --- a/functions/onprem/orborus/Dockerfile +++ b/functions/onprem/orborus/Dockerfile @@ -1,18 +1,23 @@ -FROM golang:1.19-buster as builder +FROM golang:1.22 as builder RUN mkdir /app WORKDIR /app COPY orborus.go /app/orborus.go +#COPY go.mod /app/go.mod +#COPY go.sum /app/go.sum RUN go mod init orborus -RUN go get github.com/docker/docker/api/types && \ - go get github.com/docker/docker/api/types/container && \ - go get github.com/docker/docker/client && \ - go get github.com/mackerelio/go-osstat/cpu && \ - go get github.com/mackerelio/go-osstat/memory && \ - go get github.com/satori/go.uuid && \ - go get github.com/shuffle/shuffle-shared && \ - go get github.com/shirou/gopsutil + +RUN go get github.com/docker/docker/api/types +RUN go get github.com/docker/docker/api/types/container +#RUN go get github.com/docker/docker/client +RUN go get github.com/mackerelio/go-osstat/cpu +RUN go get github.com/mackerelio/go-osstat/memory +RUN go get github.com/satori/go.uuid +RUN go get github.com/shuffle/shuffle-shared +RUN go get github.com/shirou/gopsutil +RUN go get k8s.io/client-go +RUN go get k8s.io/apimachinery RUN go get RUN go mod tidy @@ -25,9 +30,7 @@ COPY --from=builder /app/ / ENV ENVIRONMENT_NAME=Shuffle ENV BASE_URL=http://shuffle-backend:5001 -ENV DOCKER_API_VERSION=1.39 +ENV DOCKER_API_VERSION=1.40 ENV SHUFFLE_OPENSEARCH_URL=https://opensearch:9200 - - CMD ["./orborus"] diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 888c95f5..1d93d05d 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -1,22 +1,23 @@ module orborus -go 1.19 - -//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared +go 1.22.2 require ( - github.com/docker/docker v23.0.3+incompatible + github.com/docker/docker v26.1.0+incompatible github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.98 - k8s.io/api v0.28.1 - k8s.io/apimachinery v0.28.1 - k8s.io/client-go v0.28.1 + github.com/shuffle/shuffle-shared v0.6.16 + k8s.io/api v0.30.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 ) require ( - cloud.google.com/go v0.75.0 // indirect - cloud.google.com/go/datastore v1.4.0 // indirect - cloud.google.com/go/storage v1.12.0 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.24.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/datastore v1.15.0 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/storage v1.36.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -26,47 +27,51 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/frikky/kin-openapi v0.41.0 // indirect - github.com/frikky/schemaless v0.0.6 // indirect + github.com/frikky/schemaless v0.0.9 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-git/v5 v5.11.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v28 v28.1.1 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/jstemmer/go-junit-report v0.9.1 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opensearch-project/opensearch-go v1.1.0 // indirect github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect @@ -78,32 +83,41 @@ require ( github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - go.opencensus.io v0.22.5 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/trace v1.25.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect - google.golang.org/api v0.36.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.18.0 // indirect + google.golang.org/api v0.162.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595 // indirect - google.golang.org/grpc v1.34.1 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.4.0 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + gotest.tools/v3 v3.5.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 78bb4045..7b7c23a8 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -15,18 +15,26 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.75.0 h1:XgtDnVJRCPEUG21gjFiRPz4zI1Mjg16R+NYQjfmU4XY= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.4.0 h1:CFDJm15RpYXeEblQ0TMDUrYtqmBmbAWTy536nA8JIc8= cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= +cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= +cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -36,12 +44,14 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.12.0 h1:4y3gHptW1EHVtcPAVE0eBBlFuGqEejTTG3KdIE0lUX4= cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -78,6 +88,8 @@ github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xu github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -87,25 +99,29 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho= -github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v26.1.0+incompatible h1:W1G9MPNbskA6VZWL7b3ZljTh0pXI68FpINx0GKaOdaM= +github.com/docker/docker v26.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -113,10 +129,14 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= -github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= -github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2+8= +github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -135,10 +155,13 @@ github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lK github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -181,8 +204,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -195,6 +219,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -211,8 +236,9 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -225,13 +251,21 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -248,7 +282,6 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -267,8 +300,10 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -298,8 +333,9 @@ github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzm github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -316,12 +352,13 @@ github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3 github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= @@ -348,12 +385,12 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.5.93 h1:fZf9s2cEgDoyYXXvPYVeNh8UysuSlywQkc54IXkNL0k= -github.com/shuffle/shuffle-shared v0.5.93/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= -github.com/shuffle/shuffle-shared v0.5.98 h1:0qG/1UZZVmY+wIJBuC9bnFjCITJBr7iKLG5ZibpBkTI= -github.com/shuffle/shuffle-shared v0.5.98/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= +github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGSEja9zG6s= +github.com/shuffle/shuffle-shared v0.6.16/go.mod h1:HhQTn7xZZ69ZTc4EptO9OeNmgbKDyGlWAhFkUFUAHSA= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -373,8 +410,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -388,8 +426,27 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= +go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -406,8 +463,9 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -430,7 +488,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -449,8 +506,9 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -482,6 +540,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -502,8 +561,9 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -513,8 +573,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -528,8 +588,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -566,7 +627,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -590,8 +650,9 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -604,8 +665,9 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -628,8 +690,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -681,7 +743,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= @@ -690,12 +751,15 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -715,8 +779,9 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo= google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= +google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -762,8 +827,13 @@ google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595 h1:x7nk+/4+SvuTDI4wnzQUlhvi+DTpyfncXBo3QWTFs7U= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -780,8 +850,9 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.34.1 h1:ugq+9++ZQPFzM2pKUMCIK8gj9M0pFyuUWO9Q8kwEDQw= google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -795,8 +866,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -818,8 +889,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -827,25 +898,25 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.28.1 h1:i+0O8k2NPBCPYaMB+uCkseEbawEt/eFaiRqUx8aB108= -k8s.io/api v0.28.1/go.mod h1:uBYwID+66wiL28Kn2tBjBYQdEU0Xk0z5qF8bIBqk/Dg= -k8s.io/apimachinery v0.28.1 h1:EJD40og3GizBSV3mkIoXQBsws32okPOy+MkRyzh6nPY= -k8s.io/apimachinery v0.28.1/go.mod h1:X0xh/chESs2hP9koe+SdIAcXWcQ+RM5hy0ZynB+yEvw= -k8s.io/client-go v0.28.1 h1:pRhMzB8HyLfVwpngWKE8hDcXRqifh1ga2Z/PU9SXVK8= -k8s.io/client-go v0.28.1/go.mod h1:pEZA3FqOsVkCc07pFVzK076R+P/eXqsgx5zuuRWukNE= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 042fc6d6..ff0e68c5 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -357,8 +357,7 @@ func deployServiceWorkers(image string) { if len(os.Getenv("DOCKER_HOST")) > 0 { log.Printf("[DEBUG] Deploying docker socket proxy to the network %s as the DOCKER_HOST variable is set", networkName) - //if err == nil { - containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ + containers, err := dockercli.ContainerList(ctx, container.ListOptions{ All: true, }) @@ -670,15 +669,51 @@ func deployWorker(image string, identifier string, env []string, executionReques } if isKubernetes == "true" { - env = append(env, fmt.Sprintf("IS_KUBERNETES=%s", os.Getenv("IS_KUBERNETES"))) + env = append(env, fmt.Sprintf("IS_KUBERNETES=true")) env = append(env, fmt.Sprintf("KUBERNETES_NAMESPACE=%s", os.Getenv("KUBERNETES_NAMESPACE"))) - clientset, err := getKubernetesClient() + if len(os.Getenv("KUBERNETES_SERVICE_HOST")) > 0 { + env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_HOST=%s", os.Getenv("KUBERNETES_SERVICE_HOST"))) + } + + if len(os.Getenv("KUBERNETES_SERVICE_PORT")) > 0 { + env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_PORT=%s", os.Getenv("KUBERNETES_SERVICE_PORT"))) + } + + + clientset, config, err := getKubernetesClient() if err != nil { log.Printf("[ERROR] Error getting kubernetes client:", err) return err } + log.Printf("CONFIG: %s", config.String()) + env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String())) + + // Look for if there is a default service account in use + if len(os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) > 0 { + log.Printf("[DEBUG] Using Kubernetes service account %s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) + env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_ACCOUNT=%s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT"))) + + // use k8s downward API to find it if we are in a pod + } + + serviceAccounts, err := clientset.CoreV1().ServiceAccounts(kubernetesNamespace).List(context.Background(), metav1.ListOptions{}) + if err != nil { + log.Printf("[ERROR] Failed to list service accounts: %s", err) + } else { + log.Printf("[DEBUG] Found %d service accounts", len(serviceAccounts.Items)) + for _, serviceAccount := range serviceAccounts.Items { + log.Printf("[DEBUG] Service account: %s", serviceAccount.Name) + } + } + + for _, envVar := range os.Environ() { + if strings.Contains(strings.ToLower(envVar), "kubernetes") || strings.Contains(strings.ToLower(envVar), "k8s") { + log.Printf("[DEBUG] K8s var: %s", envVar) + } + } + // Check if namespace exist as variable. If so, make it if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 && !namespacemade { kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") @@ -742,6 +777,7 @@ func deployWorker(image string, identifier string, env []string, executionReques //ImagePullPolicy: "Never", ImagePullPolicy: corev1.PullIfNotPresent, + //ImagePullPolicy: "Always", }, }, }, @@ -840,7 +876,7 @@ func deployWorker(image string, identifier string, env []string, executionReques } } - containerStartOptions := types.ContainerStartOptions{} + containerStartOptions := container.StartOptions{} err = dockercli.ContainerStart(context.Background(), cont.ID, containerStartOptions) if err != nil { // Trying to recreate and start WITHOUT network if it's possible. No extended checks. Old execution system (<0.9.30) @@ -915,7 +951,7 @@ func stopWorker(containername string) error { log.Printf("[ERROR] Unable to stop container %s - running removal anyway, just in case: %s", containername, err) } - removeOptions := types.ContainerRemoveOptions{ + removeOptions := container.RemoveOptions{ RemoveVolumes: true, Force: true, } @@ -973,6 +1009,7 @@ func initializeImages() { reader, err := dockercli.ImagePull(ctx, image, pullOptions) if err != nil { log.Printf("[ERROR] Failed getting image %s: %s", image, err) + continue } @@ -1144,7 +1181,7 @@ func getOrborusStats(ctx context.Context) shuffle.OrborusStats { // Get list of all running containers - containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{}) + containers, err := dockercli.ContainerList(ctx, container.ListOptions{}) if err != nil { log.Printf("[ERROR] Failed getting container list: %s", err) return newStats @@ -1262,30 +1299,39 @@ func isRunningInCluster() bool { return existsHost && existsPort } -func getKubernetesClient() (*kubernetes.Clientset, error) { +func getKubernetesClient() (*kubernetes.Clientset, *rest.Config, error) { + + config := &rest.Config{} + var err error + if isRunningInCluster() { config, err := rest.InClusterConfig() if err != nil { - return nil, err + return nil, config, err } + clientset, err := kubernetes.NewForConfig(config) if err != nil { - return nil, err + return nil, config, err } - return clientset, nil - } else { - home := homedir.HomeDir() - kubeconfigPath := filepath.Join(home, ".kube", "config") - config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) - if err != nil { - return nil, err - } - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - return clientset, nil + + return clientset, config, nil + + } + + home := homedir.HomeDir() + kubeconfigPath := filepath.Join(home, ".kube", "config") + config, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + return nil, config, err } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, config, err + } + + return clientset, config, nil } @@ -1917,7 +1963,7 @@ func deployPipeline(image, identifier, command string) error { } } - containerStartOptions := types.ContainerStartOptions{} + containerStartOptions := container.StartOptions{} err = dockercli.ContainerStart( ctx, cont.ID, @@ -2032,7 +2078,7 @@ func getRunningWorkers(ctx context.Context, workerTimeout int) int { thresholdTime := time.Now().Add(time.Duration(-workerTimeout) * time.Second) - clientset, err := getKubernetesClient() + clientset, _, err := getKubernetesClient() if err != nil { log.Printf("[ERROR] Failed getting kubernetes client: %s", err) return 0 @@ -2058,7 +2104,7 @@ func getRunningWorkers(ctx context.Context, workerTimeout int) int { } } else { - containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ + containers, err := dockercli.ContainerList(ctx, container.ListOptions{ All: true, }) @@ -2125,7 +2171,7 @@ func zombiecheck(ctx context.Context, workerTimeout int) error { } log.Println("[INFO] Looking for old containers to remove") - containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ + containers, err := dockercli.ContainerList(ctx, container.ListOptions{ All: true, }) @@ -2199,7 +2245,7 @@ func zombiecheck(ctx context.Context, workerTimeout int) error { removeContainers = append(removeContainers, containername) } - removeOptions := types.ContainerRemoveOptions{ + removeOptions := container.RemoveOptions{ RemoveVolumes: true, Force: true, } diff --git a/functions/onprem/orborus/orborus.yaml b/functions/onprem/orborus/orborus.yaml new file mode 100644 index 00000000..36ac49e8 --- /dev/null +++ b/functions/onprem/orborus/orborus.yaml @@ -0,0 +1,82 @@ +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: default + name: pod-manager +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "create", "update", "delete"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create", "get", "list", "watch", "delete"] + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pod-manager-binding + namespace: default +subjects: +- kind: ServiceAccount + name: default + namespace: default +roleRef: + kind: Role + name: pod-manager + apiGroup: rbac.authorization.k8s.io + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: orborus + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + labels: + io.kompose.service: orborus +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: orborus + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: orborus + spec: + dnsPolicy: "Default" + containers: + - env: + - name: BASE_URL + value: "https://shuffler.io" + - name: SHUFFLE_SCALE_REPLICAS + value: "7" + - name: IS_KUBERNETES + value: "true" + - name: ENVIRONMENT_NAME + value: "environment test" + - name: ORG + value: "9c938e5b-d812-40d9-92f0-93783f43ec0d" + - name: AUTH + value: "3663a270-bb3a-4678-a365-d879601a1a0c" + - name: SHUFFLE_WORKER_IMAGE + value: "ghcr.io/shuffle/shuffle-worker:nightly" + + image: ghcr.io/shuffle/shuffle-orborus:nightly + #imagePullPolicy: Never + name: shuffle-orborus + resources: {} + hostname: shuffle-orborus + restartPolicy: Always diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index bae8288d..e4dcdd0a 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -1,26 +1,24 @@ module worker -go 1.19 - -//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared +go 1.22.2 require ( - github.com/docker/docker v23.0.3+incompatible - github.com/gorilla/mux v1.8.0 + github.com/docker/docker v26.1.0+incompatible + github.com/gorilla/mux v1.8.1 github.com/satori/go.uuid v1.2.0 github.com/shuffle/shuffle-shared v0.6.16 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/api v0.30.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 ) require ( - cloud.google.com/go v0.107.0 // indirect - cloud.google.com/go/compute v1.14.0 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/datastore v1.10.0 // indirect - cloud.google.com/go/iam v0.8.0 // indirect - cloud.google.com/go/storage v1.29.0 // indirect + cloud.google.com/go/datastore v1.15.0 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/storage v1.36.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -30,47 +28,51 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/frikky/kin-openapi v0.41.0 // indirect github.com/frikky/schemaless v0.0.9 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-git/v5 v5.11.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v28 v28.1.1 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opensearch-project/opensearch-go v1.1.0 // indirect github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect @@ -83,31 +85,40 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/trace v1.25.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.106.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.18.0 // indirect + google.golang.org/api v0.162.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.51.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.4.0 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + gotest.tools/v3 v3.5.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index 3b7752e4..798d90f5 100644 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -16,26 +16,25 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= -cloud.google.com/go/datastore v1.10.0 h1:4siQRf4zTiAVt/oeH4GureGkApgb2vtPQAtOmhpqQwE= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= +cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -46,12 +45,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -88,8 +88,9 @@ github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xu github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -98,39 +99,42 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho= -github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v26.1.0+incompatible h1:W1G9MPNbskA6VZWL7b3ZljTh0pXI68FpINx0GKaOdaM= +github.com/docker/docker v26.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= -github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= -github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2+8= github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -151,10 +155,13 @@ github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lK github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -197,8 +204,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -229,8 +237,8 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -245,17 +253,21 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -290,8 +302,10 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -321,8 +335,9 @@ github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzm github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -339,12 +354,13 @@ github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3 github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= @@ -371,12 +387,12 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.5.86 h1:ZHQgZ4siSWgi5gttxeSMdjsaH9SbGTzrA/GO6aICO2U= -github.com/shuffle/shuffle-shared v0.5.86/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGSEja9zG6s= github.com/shuffle/shuffle-shared v0.6.16/go.mod h1:HhQTn7xZZ69ZTc4EptO9OeNmgbKDyGlWAhFkUFUAHSA= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -396,8 +412,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -414,6 +431,24 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= +go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -430,8 +465,9 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -472,8 +508,9 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -526,8 +563,9 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -537,8 +575,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -552,8 +590,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -590,7 +629,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -614,8 +652,9 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -628,8 +667,9 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -652,8 +692,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -705,7 +745,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= @@ -714,8 +753,9 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -742,8 +782,8 @@ google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQW google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.106.0 h1:ffmW0faWCwKkpbbtvlY/K/8fUl+JKvNS5CVzRoyfCv8= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= +google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -790,8 +830,12 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -809,8 +853,8 @@ google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -824,8 +868,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -847,8 +891,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -856,25 +900,25 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index e47059f6..2367e187 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -400,7 +400,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] clientset, err := getKubernetesClient() if err != nil { - log.Printf("[ERROR] Failed getting kubernetes: %s [INFO] Setting kubernetes to false to enable running Shuffle with Docker for the next iterations.", err) + log.Printf("[ERROR] Failed getting kubernetes: %s", err) return err } @@ -702,7 +702,7 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont } } - err = cli.ContainerStart(ctx, cont.ID, types.ContainerStartOptions{}) + err = cli.ContainerStart(ctx, cont.ID, container.StartOptions{}) if err != nil { if strings.Contains(fmt.Sprintf("%s", err), "cannot join network") || strings.Contains(fmt.Sprintf("%s", err), "No such container") { parsedUuid := uuid.NewV4() @@ -738,7 +738,7 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont } log.Printf("[DEBUG] Running secondary check without network with worker") - err = cli.ContainerStart(ctx, cont.ID, types.ContainerStartOptions{}) + err = cli.ContainerStart(ctx, cont.ID, container.StartOptions{}) } if err != nil { @@ -791,7 +791,7 @@ func removeContainer(containername string) error { // log.Printf("Unable to stop container %s - running removal anyway, just in case: %s", containername, err) //} - removeOptions := types.ContainerRemoveOptions{ + removeOptions := container.RemoveOptions{ RemoveVolumes: true, Force: true, } @@ -1901,469 +1901,495 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar { } func getKubernetesClient() (*kubernetes.Clientset, error) { - if isRunningInCluster() { + kubeconfigContent := os.Getenv("KUBECONFIG_CONTENT") + if len(kubeconfigContent) > 0 { + log.Printf("[INFO] Using KUBERNETES_CONFIG to set up Kubernetes client: %#v", os.Getenv("KUBERNETES_CONFIG")) config, err := rest.InClusterConfig() if err != nil { return nil, err } + + // Replace client configuration with kubeconfig content + config, err = clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfigContent)) + if err != nil { + return nil, err + } + + // Create Kubernetes client clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, err } + return clientset, nil - } else { - home := homedir.HomeDir() - kubeconfigPath := filepath.Join(home, ".kube", "config") - config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + + } else if isRunningInCluster() { + config, err := rest.InClusterConfig() if err != nil { return nil, err } + clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, err } + return clientset, nil + } + + home := homedir.HomeDir() + kubeconfigPath := filepath.Join(home, ".kube", "config") + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + return nil, err } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + return clientset, nil } func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { - if request.Body == nil { - resp.WriteHeader(http.StatusBadRequest) - return +if request.Body == nil { + resp.WriteHeader(http.StatusBadRequest) + return +} + +defer request.Body.Close() +body, err := ioutil.ReadAll(request.Body) +if err != nil { + log.Printf("[WARNING] (3) Failed reading body for workflowqueue") + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + return +} + +var actionResult shuffle.ActionResult +err = json.Unmarshal(body, &actionResult) +if err != nil { + log.Printf("[ERROR] Failed shuffle.ActionResult unmarshaling (2): %s", err) + //resp.WriteHeader(401) + //resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + //return +} + +if len(actionResult.ExecutionId) == 0 { + log.Printf("[ERROR] No workflow execution id in action result. Data: %s", string(body)) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`))) + return +} + +// 1. Get the shuffle.WorkflowExecution(ExecutionId) from the database +// 2. if shuffle.ActionResult.Authentication != shuffle.WorkflowExecution.Authentication -> exit +// 3. Add to and update actionResult in workflowExecution +// 4. Push to db +// IF FAIL: Set executionstatus: abort or cancel +ctx := context.Background() +workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) +if err != nil { + log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err) + resp.WriteHeader(500) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId))) + return +} + +if workflowExecution.Authorization != actionResult.Authorization { + log.Printf("[ERROR][%s] Bad authorization key when updating node (workflowQueue). Want: %s, Have: %s", actionResult.ExecutionId, workflowExecution.Authorization, actionResult.Authorization) + resp.WriteHeader(403) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key"}`))) + return +} + +if workflowExecution.Status == "FINISHED" { + log.Printf("[DEBUG][%s] Workflowexecution is already FINISHED. No further action can be taken", workflowExecution.ExecutionId) + resp.WriteHeader(200) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is already finished because it has status %s. Lastnode: %s"}`, workflowExecution.Status, workflowExecution.LastNode))) + return +} + +if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { + log.Printf("[WARNING][%s] Workflowexecution already has status %s. No further action can be taken", workflowExecution.ExecutionId, workflowExecution.Status) + resp.WriteHeader(200) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status))) + return +} + +retries := 0 +retry, retriesok := request.URL.Query()["retries"] +if retriesok && len(retry) > 0 { + val, err := strconv.Atoi(retry[0]) + if err == nil { + retries = val } +} - defer request.Body.Close() - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("[WARNING] (3) Failed reading body for workflowqueue") - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } +log.Printf("[DEBUG][%s] Action: Received, Label: '%s', Action: '%s', Status: %s, Run status: %s, Extra=Retry:%d", workflowExecution.ExecutionId, actionResult.Action.Label, actionResult.Action.AppName, actionResult.Status, workflowExecution.Status, retries) - var actionResult shuffle.ActionResult - err = json.Unmarshal(body, &actionResult) - if err != nil { - log.Printf("[ERROR] Failed shuffle.ActionResult unmarshaling (2): %s", err) - //resp.WriteHeader(401) - //resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - //return - } - - if len(actionResult.ExecutionId) == 0 { - log.Printf("[ERROR] No workflow execution id in action result. Data: %s", string(body)) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`))) - return - } - - // 1. Get the shuffle.WorkflowExecution(ExecutionId) from the database - // 2. if shuffle.ActionResult.Authentication != shuffle.WorkflowExecution.Authentication -> exit - // 3. Add to and update actionResult in workflowExecution - // 4. Push to db - // IF FAIL: Set executionstatus: abort or cancel - ctx := context.Background() - workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) - if err != nil { - log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err) - resp.WriteHeader(500) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId))) - return - } - - if workflowExecution.Authorization != actionResult.Authorization { - log.Printf("[ERROR][%s] Bad authorization key when updating node (workflowQueue). Want: %s, Have: %s", actionResult.ExecutionId, workflowExecution.Authorization, actionResult.Authorization) - resp.WriteHeader(403) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key"}`))) - return - } - - if workflowExecution.Status == "FINISHED" { - log.Printf("[DEBUG][%s] Workflowexecution is already FINISHED. No further action can be taken", workflowExecution.ExecutionId) - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is already finished because it has status %s. Lastnode: %s"}`, workflowExecution.Status, workflowExecution.LastNode))) - return - } - - if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { - log.Printf("[WARNING][%s] Workflowexecution already has status %s. No further action can be taken", workflowExecution.ExecutionId, workflowExecution.Status) - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status))) - return - } - - retries := 0 - retry, retriesok := request.URL.Query()["retries"] - if retriesok && len(retry) > 0 { - val, err := strconv.Atoi(retry[0]) - if err == nil { - retries = val - } - } - - log.Printf("[DEBUG][%s] Action: Received, Label: '%s', Action: '%s', Status: %s, Run status: %s, Extra=Retry:%d", workflowExecution.ExecutionId, actionResult.Action.Label, actionResult.Action.AppName, actionResult.Status, workflowExecution.Status, retries) - - //results = append(results, actionResult) - //log.Printf("[INFO][%s] Time to execute %s (%s) with app %s:%s, function %s, env %s with %d parameters.", workflowExecution.ExecutionId, action.ID, action.Label, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters)) - //log.Printf("[DEBUG][%s] In workflowQueue with transaction", workflowExecution.ExecutionId) - runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp) +//results = append(results, actionResult) +//log.Printf("[INFO][%s] Time to execute %s (%s) with app %s:%s, function %s, env %s with %d parameters.", workflowExecution.ExecutionId, action.ID, action.Label, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters)) +//log.Printf("[DEBUG][%s] In workflowQueue with transaction", workflowExecution.ExecutionId) +runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp) } // Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workflowExecutionId string, actionResult shuffle.ActionResult, resp http.ResponseWriter) { - //log.Printf("[DEBUG][%s] IN WORKFLOWEXECUTION SUB!", actionResult.ExecutionId) - workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) - if err != nil { - log.Printf("[ERROR] Failed getting execution cache: %s", err) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) +//log.Printf("[DEBUG][%s] IN WORKFLOWEXECUTION SUB!", actionResult.ExecutionId) +workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) +if err != nil { + log.Printf("[ERROR] Failed getting execution cache: %s", err) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) + return +} + +resultLength := len(workflowExecution.Results) +setExecution := true + +workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, true, 0) +if err == nil { + if workflowExecution.Status != "EXECUTING" && workflowExecution.Status != "WAITING" { + log.Printf("[WARNING][%s] Execution is not executing, but %s. Stopping Transaction update.", workflowExecution.ExecutionId, workflowExecution.Status) + if resp != nil { + resp.WriteHeader(200) + resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Execution is not executing, but %s"}`, workflowExecution.Status))) + } + + + log.Printf("[DEBUG][%s] Shutting down (35)", workflowExecution.ExecutionId) + + // Force sending result + shutdownData, err := json.Marshal(workflowExecution) + if err != nil { + log.Printf("[ERROR][%s] Failed marshalling execution (35): %s", workflowExecution.ExecutionId, err) + } + + sendResult(*workflowExecution, shutdownData) + shutdown(*workflowExecution, "", "", false) + return + } +} else { + if strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already been ran") || strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already finished") { + log.Printf("[ERROR][%s] Skipping rerun of action result as it's already been ran: %s", workflowExecution.ExecutionId) return } - resultLength := len(workflowExecution.Results) - setExecution := true - - workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, true, 0) - if err == nil { - if workflowExecution.Status != "EXECUTING" && workflowExecution.Status != "WAITING" { - log.Printf("[WARNING][%s] Execution is not executing, but %s. Stopping Transaction update.", workflowExecution.ExecutionId, workflowExecution.Status) - if resp != nil { - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Execution is not executing, but %s"}`, workflowExecution.Status))) - } - - - log.Printf("[DEBUG][%s] Shutting down (35)", workflowExecution.ExecutionId) - - // Force sending result - shutdownData, err := json.Marshal(workflowExecution) - if err != nil { - log.Printf("[ERROR][%s] Failed marshalling execution (35): %s", workflowExecution.ExecutionId, err) - } - - sendResult(*workflowExecution, shutdownData) - shutdown(*workflowExecution, "", "", false) - return - } - } else { - if strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already been ran") || strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already finished") { - log.Printf("[ERROR][%s] Skipping rerun of action result as it's already been ran: %s", workflowExecution.ExecutionId) + log.Printf("[DEBUG] Rerunning transaction? %s", err) + if strings.Contains(fmt.Sprintf("%s", err), "Rerun this transaction") { + workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) + if err != nil { + log.Printf("[ERROR][%s] Failed getting execution cache (2): %s", workflowExecution.ExecutionId, err) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) return } - log.Printf("[DEBUG] Rerunning transaction? %s", err) - if strings.Contains(fmt.Sprintf("%s", err), "Rerun this transaction") { - workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) - if err != nil { - log.Printf("[ERROR][%s] Failed getting execution cache (2): %s", workflowExecution.ExecutionId, err) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) - return - } + resultLength = len(workflowExecution.Results) + setExecution = true - resultLength = len(workflowExecution.Results) - setExecution = true - - workflowExecution, dbSave, err = shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, false, 0) - if err != nil { - log.Printf("[ERROR][%s] Failed execution of parsedexecution (2): %s", workflowExecution.ExecutionId, err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) - return - } else { - log.Printf("[DEBUG][%s] Successfully got ParsedExecution with %d results!", workflowExecution.ExecutionId, len(workflowExecution.Results)) - } + workflowExecution, dbSave, err = shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, false, 0) + if err != nil { + log.Printf("[ERROR][%s] Failed execution of parsedexecution (2): %s", workflowExecution.ExecutionId, err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) + return } else { - log.Printf("[ERROR][%s] Failed execution of parsedexecution: %s", workflowExecution.ExecutionId, err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) - return + log.Printf("[DEBUG][%s] Successfully got ParsedExecution with %d results!", workflowExecution.ExecutionId, len(workflowExecution.Results)) } - } - - //log.Printf(`[DEBUG][%s] Got result %s from %s. Execution status: %s. Save: %#v. Parent: %#v`, actionResult.ExecutionId, actionResult.Status, actionResult.Action.ID, workflowExecution.Status, dbSave, workflowExecution.ExecutionParent) - //dbSave := false - - //if len(results) != len(workflowExecution.Results) { - // log.Printf("[DEBUG][%s] There may have been an issue in transaction queue. Result lengths: %d vs %d. Should check which exists the base results, but not in entire execution, then append.", workflowExecution.ExecutionId, len(results), len(workflowExecution.Results)) - //} - - // Validating that action results hasn't changed - // Handled using cachhing, so actually pretty fast - cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId) - cache, err := shuffle.GetCache(ctx, cacheKey) - if err == nil { - //parsedValue := value.(*shuffle.WorkflowExecution) - - parsedValue := &shuffle.WorkflowExecution{} - cacheData := []byte(cache.([]uint8)) - err = json.Unmarshal(cacheData, &workflowExecution) - if err != nil { - log.Printf("[ERROR][%s] Failed unmarshalling workflowexecution: %s", workflowExecution.ExecutionId, err) - } - - if len(parsedValue.Results) > 0 && len(parsedValue.Results) != resultLength { - setExecution = false - if attempts > 5 { - } - - attempts += 1 - log.Printf("[DEBUG][%s] Rerunning transaction as results has changed. %d vs %d", workflowExecution.ExecutionId, len(parsedValue.Results), resultLength) - /* - if len(workflowExecution.Results) <= len(workflowExecution.Workflow.Actions) { - log.Printf("[DEBUG][%s] Rerunning transaction as results has changed. %d vs %d", workflowExecution.ExecutionId, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) - runWorkflowExecutionTransaction(ctx, attempts, workflowExecutionId, actionResult, resp) - return - } - */ - } - } - - if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { - log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) - //result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results)) - err = setWorkflowExecution(ctx, *workflowExecution, dbSave) - if err != nil { - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution actionresult: %s"}`, err))) - return - } - } else { - log.Printf("[INFO][%s] Skipping setexec with status %s", workflowExecution.ExecutionId, workflowExecution.Status) + log.Printf("[ERROR][%s] Failed execution of parsedexecution: %s", workflowExecution.ExecutionId, err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) + return + } +} - // Just in case. Should MAYBE validate finishing another time as well. - // This fixes issues with e.g. shuffle.Action -> shuffle.Trigger -> shuffle.Action. - handleExecutionResult(*workflowExecution) +//log.Printf(`[DEBUG][%s] Got result %s from %s. Execution status: %s. Save: %#v. Parent: %#v`, actionResult.ExecutionId, actionResult.Status, actionResult.Action.ID, workflowExecution.Status, dbSave, workflowExecution.ExecutionParent) +//dbSave := false + +//if len(results) != len(workflowExecution.Results) { +// log.Printf("[DEBUG][%s] There may have been an issue in transaction queue. Result lengths: %d vs %d. Should check which exists the base results, but not in entire execution, then append.", workflowExecution.ExecutionId, len(results), len(workflowExecution.Results)) +//} + +// Validating that action results hasn't changed +// Handled using cachhing, so actually pretty fast +cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId) +cache, err := shuffle.GetCache(ctx, cacheKey) +if err == nil { + //parsedValue := value.(*shuffle.WorkflowExecution) + + parsedValue := &shuffle.WorkflowExecution{} + cacheData := []byte(cache.([]uint8)) + err = json.Unmarshal(cacheData, &workflowExecution) + if err != nil { + log.Printf("[ERROR][%s] Failed unmarshalling workflowexecution: %s", workflowExecution.ExecutionId, err) } - //if newExecutions && len(nextActions) > 0 { - // log.Printf("[DEBUG][%s] New execution: %#v. NextActions: %#v", newExecutions, nextActions) - // //handleExecutionResult(*workflowExecution) - //} + if len(parsedValue.Results) > 0 && len(parsedValue.Results) != resultLength { + setExecution = false + if attempts > 5 { + } - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) + attempts += 1 + log.Printf("[DEBUG][%s] Rerunning transaction as results has changed. %d vs %d", workflowExecution.ExecutionId, len(parsedValue.Results), resultLength) + /* + if len(workflowExecution.Results) <= len(workflowExecution.Workflow.Actions) { + log.Printf("[DEBUG][%s] Rerunning transaction as results has changed. %d vs %d", workflowExecution.ExecutionId, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) + runWorkflowExecutionTransaction(ctx, attempts, workflowExecutionId, actionResult, resp) + return + } + */ + } +} + +if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { + log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) + //result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results)) + err = setWorkflowExecution(ctx, *workflowExecution, dbSave) + if err != nil { + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution actionresult: %s"}`, err))) + return + } + +} else { + log.Printf("[INFO][%s] Skipping setexec with status %s", workflowExecution.ExecutionId, workflowExecution.Status) + + // Just in case. Should MAYBE validate finishing another time as well. + // This fixes issues with e.g. shuffle.Action -> shuffle.Trigger -> shuffle.Action. + handleExecutionResult(*workflowExecution) +} + +//if newExecutions && len(nextActions) > 0 { +// log.Printf("[DEBUG][%s] New execution: %#v. NextActions: %#v", newExecutions, nextActions) +// //handleExecutionResult(*workflowExecution) +//} + +resp.WriteHeader(200) +resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) } func sendSelfRequest(actionResult shuffle.ActionResult) { - data, err := json.Marshal(actionResult) +data, err := json.Marshal(actionResult) +if err != nil { + log.Printf("[ERROR][%s] Shutting down (24): Failed to unmarshal data for backend: %s", actionResult.ExecutionId, err) + return +} + +if actionResult.ExecutionId == "TBD" { + return +} + +log.Printf("[DEBUG][%s] Sending FAILURE to self to stop the workflow execution. Action: %s (%s), app %s:%s", actionResult.ExecutionId, actionResult.Action.Label, actionResult.Action.ID, actionResult.Action.AppName, actionResult.Action.AppVersion) + +// Literally sending to same worker to run it as a new request +streamUrl := fmt.Sprintf("http://localhost:33333/api/v1/streams") +hostenv := os.Getenv("WORKER_HOSTNAME") +if len(hostenv) > 0 { + streamUrl = fmt.Sprintf("http://%s:33333/api/v1/streams", hostenv) +} + +req, err := http.NewRequest( + "POST", + streamUrl, + bytes.NewBuffer([]byte(data)), +) + +if err != nil { + log.Printf("[ERROR][%s] Failed creating self request (1): %s", actionResult.ExecutionId, err) + return +} + +client := shuffle.GetExternalClient(streamUrl) +newresp, err := client.Do(req) +if err != nil { + log.Printf("[ERROR][%s] Error running finishing request (2): %s", actionResult.ExecutionId, err) + return +} + +defer newresp.Body.Close() +if newresp.Body != nil { + body, err := ioutil.ReadAll(newresp.Body) + //log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode) if err != nil { - log.Printf("[ERROR][%s] Shutting down (24): Failed to unmarshal data for backend: %s", actionResult.ExecutionId, err) - return - } - - if actionResult.ExecutionId == "TBD" { - return - } - - log.Printf("[DEBUG][%s] Sending FAILURE to self to stop the workflow execution. Action: %s (%s), app %s:%s", actionResult.ExecutionId, actionResult.Action.Label, actionResult.Action.ID, actionResult.Action.AppName, actionResult.Action.AppVersion) - - // Literally sending to same worker to run it as a new request - streamUrl := fmt.Sprintf("http://localhost:33333/api/v1/streams") - hostenv := os.Getenv("WORKER_HOSTNAME") - if len(hostenv) > 0 { - streamUrl = fmt.Sprintf("http://%s:33333/api/v1/streams", hostenv) - } - - req, err := http.NewRequest( - "POST", - streamUrl, - bytes.NewBuffer([]byte(data)), - ) - - if err != nil { - log.Printf("[ERROR][%s] Failed creating self request (1): %s", actionResult.ExecutionId, err) - return - } - - client := shuffle.GetExternalClient(streamUrl) - newresp, err := client.Do(req) - if err != nil { - log.Printf("[ERROR][%s] Error running finishing request (2): %s", actionResult.ExecutionId, err) - return - } - - defer newresp.Body.Close() - if newresp.Body != nil { - body, err := ioutil.ReadAll(newresp.Body) - //log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode) - if err != nil { - log.Printf("[ERROR][%s] Failed reading body: %s", actionResult.ExecutionId, err) - } else { - log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", actionResult.ExecutionId, string(body)) - } + log.Printf("[ERROR][%s] Failed reading body: %s", actionResult.ExecutionId, err) + } else { + log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", actionResult.ExecutionId, string(body)) } } +} func sendResult(workflowExecution shuffle.WorkflowExecution, data []byte) { - if workflowExecution.ExecutionSource == "default" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { - //log.Printf("[INFO][%s] Not sending backend info since source is default (not swarm)", workflowExecution.ExecutionId) - //return +if workflowExecution.ExecutionSource == "default" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { + //log.Printf("[INFO][%s] Not sending backend info since source is default (not swarm)", workflowExecution.ExecutionId) + //return +} else { +} + +// Basically to reduce backend strain +/* +if shuffle.ArrayContains(finishedExecutions, workflowExecution.ExecutionId) { + log.Printf("[INFO][%s] NOT sending backend info since it's already been sent before.", workflowExecution.ExecutionId) + return +} +*/ + +// Take it down again +/* +if len(finishedExecutions) > 100 { + log.Printf("[DEBUG][%s] Removing old execution from finishedExecutions: %s", workflowExecution.ExecutionId, finishedExecutions[0]) + finishedExecutions = finishedExecutions[99:] +} + +finishedExecutions = append(finishedExecutions, workflowExecution.ExecutionId) +*/ + +streamUrl := fmt.Sprintf("%s/api/v1/streams", baseUrl) +req, err := http.NewRequest( + "POST", + streamUrl, + bytes.NewBuffer([]byte(data)), +) + +if err != nil { + log.Printf("[ERROR][%s] Failed creating finishing request: %s", workflowExecution.ExecutionId, err) + log.Printf("[DEBUG][%s] Shutting down (22)", workflowExecution.ExecutionId) + shutdown(workflowExecution, "", "", false) + return +} + +client := shuffle.GetExternalClient(streamUrl) +newresp, err := client.Do(req) +if err != nil { + log.Printf("[ERROR][%s] Error running finishing request (1): %s", workflowExecution.ExecutionId, err) + log.Printf("[DEBUG][%s] Shutting down (23)", workflowExecution.ExecutionId) + shutdown(workflowExecution, "", "", false) + return +} + +defer newresp.Body.Close() +if newresp.Body != nil { + body, err := ioutil.ReadAll(newresp.Body) + //log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode) + if err != nil { + log.Printf("[ERROR][%s] Failed reading body: %s", workflowExecution.ExecutionId, err) } else { + log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", workflowExecution.ExecutionId, string(body)) } - - // Basically to reduce backend strain - /* - if shuffle.ArrayContains(finishedExecutions, workflowExecution.ExecutionId) { - log.Printf("[INFO][%s] NOT sending backend info since it's already been sent before.", workflowExecution.ExecutionId) - return - } - */ - - // Take it down again - /* - if len(finishedExecutions) > 100 { - log.Printf("[DEBUG][%s] Removing old execution from finishedExecutions: %s", workflowExecution.ExecutionId, finishedExecutions[0]) - finishedExecutions = finishedExecutions[99:] - } - - finishedExecutions = append(finishedExecutions, workflowExecution.ExecutionId) - */ - - streamUrl := fmt.Sprintf("%s/api/v1/streams", baseUrl) - req, err := http.NewRequest( - "POST", - streamUrl, - bytes.NewBuffer([]byte(data)), - ) - - if err != nil { - log.Printf("[ERROR][%s] Failed creating finishing request: %s", workflowExecution.ExecutionId, err) - log.Printf("[DEBUG][%s] Shutting down (22)", workflowExecution.ExecutionId) - shutdown(workflowExecution, "", "", false) - return - } - - client := shuffle.GetExternalClient(streamUrl) - newresp, err := client.Do(req) - if err != nil { - log.Printf("[ERROR][%s] Error running finishing request (1): %s", workflowExecution.ExecutionId, err) - log.Printf("[DEBUG][%s] Shutting down (23)", workflowExecution.ExecutionId) - shutdown(workflowExecution, "", "", false) - return - } - - defer newresp.Body.Close() - if newresp.Body != nil { - body, err := ioutil.ReadAll(newresp.Body) - //log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode) - if err != nil { - log.Printf("[ERROR][%s] Failed reading body: %s", workflowExecution.ExecutionId, err) - } else { - log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", workflowExecution.ExecutionId, string(body)) - } - } +} } func validateFinished(workflowExecution shuffle.WorkflowExecution) bool { - ctx := context.Background() - - newexec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) - if err != nil { - log.Printf("[ERROR][%s] Failed getting workflow execution: %s", workflowExecution.ExecutionId, err) - return false - } else { - workflowExecution = *newexec - } - - //startAction, extra, children, parents, visited, executed, nextActions, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId) - workflowExecution, _ = shuffle.Fixexecution(ctx, workflowExecution) - _, extra, _, _, _, _, _, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId) - - log.Printf("[INFO][%s] VALIDATION. Status: %s, shuffle.Actions: %d, Extra: %d, Results: %d. Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Workflow.Actions), extra, len(workflowExecution.Results), workflowExecution.ExecutionParent) - - if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || (len(environments) == 1 && requestsSent == 0 && len(workflowExecution.Results) >= 1 && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm") || (len(workflowExecution.Results) >= len(workflowExecution.Workflow.Actions)+extra && len(workflowExecution.Workflow.Actions) > 0) { - - if workflowExecution.Status == "FINISHED" { - for _, result := range workflowExecution.Results { - if result.Status == "EXECUTING" || result.Status == "WAITING" { - log.Printf("[WARNING] NOT returning full result, as a result may be unfinished: %s (%s) - %s", result.Action.Label, result.Action.ID, result.Status) - return false - } - } - } - - - log.Printf("[DEBUG][%s] Should send full result to %s", workflowExecution.ExecutionId, baseUrl) - - //data = fmt.Sprintf(`{"execution_id": "%s", "authorization": "%s"}`, executionId, authorization) - shutdownData, err := json.Marshal(workflowExecution) - if err != nil { - log.Printf("[ERROR][%s] Shutting down (32): Failed to unmarshal data for backend: %s", workflowExecution.ExecutionId, err) - shutdown(workflowExecution, "", "", true) - } - - cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId) - if len(workflowExecution.Authorization) > 0 { - err = shuffle.SetCache(ctx, cacheKey, shutdownData, 31) - if err != nil { - log.Printf("[ERROR][%s] Failed adding to cache during ValidateFinished", workflowExecution) - } - } - - shuffle.RunCacheCleanup(ctx, workflowExecution) - sendResult(workflowExecution, shutdownData) - return true - } +ctx := context.Background() +newexec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) +if err != nil { + log.Printf("[ERROR][%s] Failed getting workflow execution: %s", workflowExecution.ExecutionId, err) return false +} else { + workflowExecution = *newexec +} + +//startAction, extra, children, parents, visited, executed, nextActions, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId) +workflowExecution, _ = shuffle.Fixexecution(ctx, workflowExecution) +_, extra, _, _, _, _, _, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId) + +log.Printf("[INFO][%s] VALIDATION. Status: %s, shuffle.Actions: %d, Extra: %d, Results: %d. Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Workflow.Actions), extra, len(workflowExecution.Results), workflowExecution.ExecutionParent) + +if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || (len(environments) == 1 && requestsSent == 0 && len(workflowExecution.Results) >= 1 && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm") || (len(workflowExecution.Results) >= len(workflowExecution.Workflow.Actions)+extra && len(workflowExecution.Workflow.Actions) > 0) { + + if workflowExecution.Status == "FINISHED" { + for _, result := range workflowExecution.Results { + if result.Status == "EXECUTING" || result.Status == "WAITING" { + log.Printf("[WARNING] NOT returning full result, as a result may be unfinished: %s (%s) - %s", result.Action.Label, result.Action.ID, result.Status) + return false + } + } + } + + + log.Printf("[DEBUG][%s] Should send full result to %s", workflowExecution.ExecutionId, baseUrl) + + //data = fmt.Sprintf(`{"execution_id": "%s", "authorization": "%s"}`, executionId, authorization) + shutdownData, err := json.Marshal(workflowExecution) + if err != nil { + log.Printf("[ERROR][%s] Shutting down (32): Failed to unmarshal data for backend: %s", workflowExecution.ExecutionId, err) + shutdown(workflowExecution, "", "", true) + } + + cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId) + if len(workflowExecution.Authorization) > 0 { + err = shuffle.SetCache(ctx, cacheKey, shutdownData, 31) + if err != nil { + log.Printf("[ERROR][%s] Failed adding to cache during ValidateFinished", workflowExecution) + } + } + + shuffle.RunCacheCleanup(ctx, workflowExecution) + sendResult(workflowExecution, shutdownData) + return true +} + +return false } func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { - defer request.Body.Close() - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("[WARNING] Failed reading body for stream result queue") - resp.WriteHeader(500) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } +defer request.Body.Close() +body, err := ioutil.ReadAll(request.Body) +if err != nil { + log.Printf("[WARNING] Failed reading body for stream result queue") + resp.WriteHeader(500) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + return +} - var actionResult shuffle.ActionResult - err = json.Unmarshal(body, &actionResult) - if err != nil { - log.Printf("[WARNING] Failed shuffle.ActionResult unmarshaling: %s", err) - //resp.WriteHeader(400) - //resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - //return - } +var actionResult shuffle.ActionResult +err = json.Unmarshal(body, &actionResult) +if err != nil { + log.Printf("[WARNING] Failed shuffle.ActionResult unmarshaling: %s", err) + //resp.WriteHeader(400) + //resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + //return +} - if len(actionResult.ExecutionId) == 0 { - log.Printf("[WARNING] No workflow execution id in action result (2). Data: %s", string(body)) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`))) - return - } +if len(actionResult.ExecutionId) == 0 { + log.Printf("[WARNING] No workflow execution id in action result (2). Data: %s", string(body)) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`))) + return +} - ctx := context.Background() - workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) - if err != nil { - log.Printf("[INFO] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) - return - } +ctx := context.Background() +workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) +if err != nil { + log.Printf("[INFO] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) + return +} - // Authorization is done here - if workflowExecution.Authorization != actionResult.Authorization { - log.Printf("[ERROR] Bad authorization key when getting stream results from cache %s.", actionResult.ExecutionId) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) - return - } +// Authorization is done here +if workflowExecution.Authorization != actionResult.Authorization { + log.Printf("[ERROR] Bad authorization key when getting stream results from cache %s.", actionResult.ExecutionId) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) + return +} - newjson, err := json.Marshal(workflowExecution) - if err != nil { - resp.WriteHeader(500) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow execution"}`))) - return - } +newjson, err := json.Marshal(workflowExecution) +if err != nil { + resp.WriteHeader(500) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow execution"}`))) + return +} - resp.WriteHeader(200) - resp.Write(newjson) +resp.WriteHeader(200) +resp.Write(newjson) } @@ -2371,363 +2397,363 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { func getLocalIP() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "" - } - - for _, address := range addrs { - // check the address type and if it is not a loopback the display it - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } - } - +addrs, err := net.InterfaceAddrs() +if err != nil { return "" } -func getAvailablePort() (net.Listener, error) { - listener, err := net.Listen("tcp", ":0") - if err != nil { - log.Printf("[WARNING] Failed to assign port by default. Defaulting to 5001") - //return ":5001" - return nil, err +for _, address := range addrs { + // check the address type and if it is not a loopback the display it + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } } +} - //defer listener.Close() +return "" +} - return listener, nil - //return fmt.Sprintf(":%d", port) +func getAvailablePort() (net.Listener, error) { +listener, err := net.Listen("tcp", ":0") +if err != nil { + log.Printf("[WARNING] Failed to assign port by default. Defaulting to 5001") + //return ":5001" + return nil, err +} + +//defer listener.Close() + +return listener, nil +//return fmt.Sprintf(":%d", port) } func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener { - hostname = getLocalIP() - os.Setenv("WORKER_HOSTNAME", hostname) +hostname = getLocalIP() +os.Setenv("WORKER_HOSTNAME", hostname) - // FIXME: This MAY not work because of speed between first - // container being launched and port being assigned to webserver - listener, err := getAvailablePort() - if err != nil { - log.Printf("[ERROR] Failed to create init listener: %s", err) - return listener - } - - log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl) - - - port := listener.Addr().(*net.TCPAddr).Port - // Set the port environment variable - os.Setenv("WORKER_PORT", fmt.Sprintf("%d", port)) - - log.Printf("[DEBUG] Starting webserver (2) on port %d with hostname: %s", port, hostname) - appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, port) - - log.Printf("[INFO] NEW WORKER HOSTNAME: %s", appCallbackUrl) +// FIXME: This MAY not work because of speed between first +// container being launched and port being assigned to webserver +listener, err := getAvailablePort() +if err != nil { + log.Printf("[ERROR] Failed to create init listener: %s", err) return listener } +log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl) + + +port := listener.Addr().(*net.TCPAddr).Port +// Set the port environment variable +os.Setenv("WORKER_PORT", fmt.Sprintf("%d", port)) + +log.Printf("[DEBUG] Starting webserver (2) on port %d with hostname: %s", port, hostname) +appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, port) + +log.Printf("[INFO] NEW WORKER HOSTNAME: %s", appCallbackUrl) +return listener +} + func downloadDockerImageBackend(client *http.Client, imageName string) error { - // Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD - if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" { - //log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName) - return nil - } - - if arrayContains(downloadedImages, imageName) { - log.Printf("[DEBUG] Image %s already downloaded", imageName) - return nil - } - - log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages) - - downloadedImages = append(downloadedImages, imageName) - - data := fmt.Sprintf(`{"name": "%s"}`, imageName) - dockerImgUrl := fmt.Sprintf("%s/api/v1/get_docker_image", baseUrl) - - req, err := http.NewRequest( - "POST", - dockerImgUrl, - bytes.NewBuffer([]byte(data)), - ) - - authorization := os.Getenv("AUTHORIZATION") - if len(authorization) > 0 { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization)) - } else { - log.Printf("[WARNING] No auth found - running backend download without it.") - //return - } - - newresp, err := topClient.Do(req) - if err != nil { - log.Printf("[ERROR] Failed download request for %s: %s", imageName, err) - return err - } - - defer newresp.Body.Close() - if newresp.StatusCode != 200 { - log.Printf("[ERROR] Docker download for image %s (backend) StatusCode (1): %d", imageName, newresp.StatusCode) - return errors.New(fmt.Sprintf("Failed to get image - status code %d", newresp.StatusCode)) - } - - newImageName := strings.Replace(imageName, "/", "_", -1) - newFileName := newImageName + ".tar" - - tar, err := os.Create(newFileName) - if err != nil { - log.Printf("[WARNING] Failed creating file: %s", err) - return err - } - - defer tar.Close() - _, err = io.Copy(tar, newresp.Body) - if err != nil { - log.Printf("[WARNING] Failed response body copying: %s", err) - return err - } - tar.Seek(0, 0) - - dockercli, err := dockerclient.NewEnvClient() - if err != nil { - log.Printf("[ERROR] Unable to create docker client (3): %s", err) - return err - } - - defer dockercli.Close() - - imageLoadResponse, err := dockercli.ImageLoad(context.Background(), tar, true) - if err != nil { - log.Printf("[ERROR] Error loading images: %s", err) - return err - } - - defer imageLoadResponse.Body.Close() - body, err := ioutil.ReadAll(imageLoadResponse.Body) - if err != nil { - log.Printf("[ERROR] Error reading: %s", err) - return err - } - - if strings.Contains(string(body), "no such file") { - return errors.New(string(body)) - } - - baseTag := strings.Split(imageName, ":") - if len(baseTag) > 1 { - tag := baseTag[1] - log.Printf("[DEBUG] Creating tag copies of downloaded containers from tag %s", tag) - - // Remapping - ctx := context.Background() - dockercli.ImageTag(ctx, imageName, fmt.Sprintf("frikky/shuffle:%s", tag)) - dockercli.ImageTag(ctx, imageName, fmt.Sprintf("registry.hub.docker.com/frikky/shuffle:%s", tag)) - - downloadedImages = append(downloadedImages, fmt.Sprintf("frikky/shuffle:%s", tag)) - downloadedImages = append(downloadedImages, fmt.Sprintf("registry.hub.docker.com/frikky/shuffle:%s", tag)) - - } - - os.Remove(newFileName) - - log.Printf("[INFO] Successfully loaded image %s: %s", imageName, string(body)) +// Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD +if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" { + //log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName) return nil } -func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) { +if arrayContains(downloadedImages, imageName) { + log.Printf("[DEBUG] Image %s already downloaded", imageName) + return nil +} + +log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages) + +downloadedImages = append(downloadedImages, imageName) + +data := fmt.Sprintf(`{"name": "%s"}`, imageName) +dockerImgUrl := fmt.Sprintf("%s/api/v1/get_docker_image", baseUrl) + +req, err := http.NewRequest( + "POST", + dockerImgUrl, + bytes.NewBuffer([]byte(data)), +) + +authorization := os.Getenv("AUTHORIZATION") +if len(authorization) > 0 { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization)) +} else { + log.Printf("[WARNING] No auth found - running backend download without it.") + //return +} + +newresp, err := topClient.Do(req) +if err != nil { + log.Printf("[ERROR] Failed download request for %s: %s", imageName, err) + return err +} + +defer newresp.Body.Close() +if newresp.StatusCode != 200 { + log.Printf("[ERROR] Docker download for image %s (backend) StatusCode (1): %d", imageName, newresp.StatusCode) + return errors.New(fmt.Sprintf("Failed to get image - status code %d", newresp.StatusCode)) +} + +newImageName := strings.Replace(imageName, "/", "_", -1) +newFileName := newImageName + ".tar" + +tar, err := os.Create(newFileName) +if err != nil { + log.Printf("[WARNING] Failed creating file: %s", err) + return err +} + +defer tar.Close() +_, err = io.Copy(tar, newresp.Body) +if err != nil { + log.Printf("[WARNING] Failed response body copying: %s", err) + return err +} +tar.Seek(0, 0) + +dockercli, err := dockerclient.NewEnvClient() +if err != nil { + log.Printf("[ERROR] Unable to create docker client (3): %s", err) + return err +} + +defer dockercli.Close() + +imageLoadResponse, err := dockercli.ImageLoad(context.Background(), tar, true) +if err != nil { + log.Printf("[ERROR] Error loading images: %s", err) + return err +} + +defer imageLoadResponse.Body.Close() +body, err := ioutil.ReadAll(imageLoadResponse.Body) +if err != nil { + log.Printf("[ERROR] Error reading: %s", err) + return err +} + +if strings.Contains(string(body), "no such file") { + return errors.New(string(body)) +} + +baseTag := strings.Split(imageName, ":") +if len(baseTag) > 1 { + tag := baseTag[1] + log.Printf("[DEBUG] Creating tag copies of downloaded containers from tag %s", tag) + + // Remapping ctx := context.Background() - nodes, err := dockercli.NodeList(ctx, types.NodeListOptions{}) + dockercli.ImageTag(ctx, imageName, fmt.Sprintf("frikky/shuffle:%s", tag)) + dockercli.ImageTag(ctx, imageName, fmt.Sprintf("registry.hub.docker.com/frikky/shuffle:%s", tag)) + + downloadedImages = append(downloadedImages, fmt.Sprintf("frikky/shuffle:%s", tag)) + downloadedImages = append(downloadedImages, fmt.Sprintf("registry.hub.docker.com/frikky/shuffle:%s", tag)) + +} + +os.Remove(newFileName) + +log.Printf("[INFO] Successfully loaded image %s: %s", imageName, string(body)) +return nil +} + +func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) { +ctx := context.Background() +nodes, err := dockercli.NodeList(ctx, types.NodeListOptions{}) +if err != nil { + return 1, err +} + +nodeCount := int64(0) +for _, node := range nodes { + //log.Printf("ID: %s - %#v", node.ID, node.Status.State) + if node.Status.State == "ready" { + nodeCount += 1 + } +} + +// Check for SHUFFLE_MAX_NODES +maxNodesString := os.Getenv("SHUFFLE_MAX_SWARM_NODES") +// Make it into a number and check if it's lower than nodeCount +if len(maxNodesString) > 0 { + maxNodes, err := strconv.ParseInt(maxNodesString, 10, 64) if err != nil { - return 1, err + return nodeCount, err } - nodeCount := int64(0) - for _, node := range nodes { - //log.Printf("ID: %s - %#v", node.ID, node.Status.State) - if node.Status.State == "ready" { - nodeCount += 1 - } + if nodeCount > maxNodes { + nodeCount = maxNodes } +} - // Check for SHUFFLE_MAX_NODES - maxNodesString := os.Getenv("SHUFFLE_MAX_SWARM_NODES") - // Make it into a number and check if it's lower than nodeCount - if len(maxNodesString) > 0 { - maxNodes, err := strconv.ParseInt(maxNodesString, 10, 64) - if err != nil { - return nodeCount, err - } +return nodeCount, nil - if nodeCount > maxNodes { - nodeCount = maxNodes - } - } - - return nodeCount, nil - - /* - containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ - All: true, - }) - */ +/* + containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ + All: true, + }) +*/ } // Runs data discovery func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, action *shuffle.Action, workflowExecution *shuffle.WorkflowExecution) error { - parsedRequest := shuffle.OrborusExecutionRequest{ - Cleanup: cleanupEnv, - ExecutionId: workflowExecution.ExecutionId, - Authorization: workflowExecution.Authorization, - EnvironmentName: os.Getenv("ENVIRONMENT_NAME"), - Timezone: os.Getenv("TZ"), - HTTPProxy: os.Getenv("HTTP_PROXY"), - HTTPSProxy: os.Getenv("HTTPS_PROXY"), - ShufflePassProxyToApp: os.Getenv("SHUFFLE_PASS_APP_PROXY"), - Url: baseUrl, - BaseUrl: baseUrl, - Action: *action, - FullExecution: *workflowExecution, +parsedRequest := shuffle.OrborusExecutionRequest{ + Cleanup: cleanupEnv, + ExecutionId: workflowExecution.ExecutionId, + Authorization: workflowExecution.Authorization, + EnvironmentName: os.Getenv("ENVIRONMENT_NAME"), + Timezone: os.Getenv("TZ"), + HTTPProxy: os.Getenv("HTTP_PROXY"), + HTTPSProxy: os.Getenv("HTTPS_PROXY"), + ShufflePassProxyToApp: os.Getenv("SHUFFLE_PASS_APP_PROXY"), + Url: baseUrl, + BaseUrl: baseUrl, + Action: *action, + FullExecution: *workflowExecution, +} +// Sometimes makes it have the wrong data due to timing + +// Specific for subflow to ensure worker matches the backend correctly + +parsedBaseurl := incomingUrl +if strings.Count(baseUrl, ":") >= 2 { + baseUrlSplit := strings.Split(baseUrl, ":") + if len(baseUrlSplit) >= 3 { + parsedBaseurl = strings.Join(baseUrlSplit[0:2], ":") + //parsedRequest.BaseUrl = fmt.Sprintf("%s:33333", parsedBaseurl) } - // Sometimes makes it have the wrong data due to timing +} - // Specific for subflow to ensure worker matches the backend correctly +if len(parsedRequest.Url) == 0 { + // Fixed callback url to the worker itself + if strings.Count(parsedBaseurl, ":") >= 2 { + parsedRequest.Url = parsedBaseurl + } else { + // Callback to worker + parsedRequest.Url = fmt.Sprintf("%s:%d", parsedBaseurl, baseport) - parsedBaseurl := incomingUrl - if strings.Count(baseUrl, ":") >= 2 { - baseUrlSplit := strings.Split(baseUrl, ":") - if len(baseUrlSplit) >= 3 { - parsedBaseurl = strings.Join(baseUrlSplit[0:2], ":") - //parsedRequest.BaseUrl = fmt.Sprintf("%s:33333", parsedBaseurl) - } + //parsedRequest.Url } - if len(parsedRequest.Url) == 0 { - // Fixed callback url to the worker itself - if strings.Count(parsedBaseurl, ":") >= 2 { - parsedRequest.Url = parsedBaseurl - } else { - // Callback to worker - parsedRequest.Url = fmt.Sprintf("%s:%d", parsedBaseurl, baseport) + //log.Printf("[DEBUG][%s] Should add a baseurl for the app to get back to: %s", workflowExecution.ExecutionId, parsedRequest.Url) +} - //parsedRequest.Url - } +// Swapping because this was confusing during dev +// No real reason, just variable names +tmp := parsedRequest.Url +parsedRequest.Url = parsedRequest.BaseUrl +parsedRequest.BaseUrl = tmp - //log.Printf("[DEBUG][%s] Should add a baseurl for the app to get back to: %s", workflowExecution.ExecutionId, parsedRequest.Url) - } +// Run with proper hostname, but set to shuffle-worker to avoid specific host target. +// This means running with VIP instead. +if len(hostname) > 0 { + parsedRequest.BaseUrl = fmt.Sprintf("http://%s:%d", hostname, baseport) + //parsedRequest.BaseUrl = fmt.Sprintf("http://shuffle-workers:%d", baseport) + //log.Printf("[DEBUG][%s] Changing hostname to local hostname in Docker network for WORKER URL: %s", workflowExecution.ExecutionId, parsedRequest.BaseUrl) - // Swapping because this was confusing during dev - // No real reason, just variable names - tmp := parsedRequest.Url - parsedRequest.Url = parsedRequest.BaseUrl - parsedRequest.BaseUrl = tmp - - // Run with proper hostname, but set to shuffle-worker to avoid specific host target. - // This means running with VIP instead. - if len(hostname) > 0 { + if parsedRequest.Action.AppName == "shuffle-subflow" || parsedRequest.Action.AppName == "shuffle-subflow-v2" || parsedRequest.Action.AppName == "User Input" { parsedRequest.BaseUrl = fmt.Sprintf("http://%s:%d", hostname, baseport) - //parsedRequest.BaseUrl = fmt.Sprintf("http://shuffle-workers:%d", baseport) - //log.Printf("[DEBUG][%s] Changing hostname to local hostname in Docker network for WORKER URL: %s", workflowExecution.ExecutionId, parsedRequest.BaseUrl) - - if parsedRequest.Action.AppName == "shuffle-subflow" || parsedRequest.Action.AppName == "shuffle-subflow-v2" || parsedRequest.Action.AppName == "User Input" { - parsedRequest.BaseUrl = fmt.Sprintf("http://%s:%d", hostname, baseport) - //parsedRequest.Url = parsedRequest.BaseUrl - } + //parsedRequest.Url = parsedRequest.BaseUrl } +} - // Making sure to get the LATEST execution data - // This is due to cache timing issues - exec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) - if err == nil && len(exec.ExecutionId) > 0 { - parsedRequest.FullExecution = *exec - } +// Making sure to get the LATEST execution data +// This is due to cache timing issues +exec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) +if err == nil && len(exec.ExecutionId) > 0 { + parsedRequest.FullExecution = *exec +} - data, err := json.Marshal(parsedRequest) +data, err := json.Marshal(parsedRequest) +if err != nil { + log.Printf("[ERROR] Failed marshalling worker request: %s", err) + return err +} + +streamUrl := fmt.Sprintf("http://%s:%d/api/v1/run", appName, port) +//log.Printf("[DEBUG][%s] Worker URL: %s, Backend URL: %s, Target App: %s", workflowExecution.ExecutionId, parsedRequest.BaseUrl, parsedRequest.Url, streamUrl) +req, err := http.NewRequest( + "POST", + streamUrl, + bytes.NewBuffer([]byte(data)), +) + +// Checking as LATE as possible, ensuring we don't rerun what's already ran +//ctx = context.Background() +newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID) +_, err = shuffle.GetCache(ctx, newExecId) +if err == nil { + log.Printf("[DEBUG] Result for %s already found (PRE REQUEST) - returning", newExecId) + return nil +} + +cacheData := []byte("1") +err = shuffle.SetCache(ctx, newExecId, cacheData, 30) +if err != nil { + log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) +} else { + //log.Printf("[DEBUG][%s] Adding %s to cache (%#v)", workflowExecution.ExecutionId, newExecId, action.Name) +} + +client := shuffle.GetExternalClient(streamUrl) +customTimeout := os.Getenv("SHUFFLE_APP_REQUEST_TIMEOUT") +if len(customTimeout) > 0 { + // convert to int + timeoutInt, err := strconv.Atoi(customTimeout) if err != nil { - log.Printf("[ERROR] Failed marshalling worker request: %s", err) - return err + log.Printf("[ERROR] Failed converting SHUFFLE_APP_REQUEST_TIMEOUT to int: %s", err) + } else { + log.Printf("[DEBUG] Setting client timeout to %d seconds for app request", timeoutInt) + client.Timeout = time.Duration(timeoutInt) * time.Second } +} - streamUrl := fmt.Sprintf("http://%s:%d/api/v1/run", appName, port) - //log.Printf("[DEBUG][%s] Worker URL: %s, Backend URL: %s, Target App: %s", workflowExecution.ExecutionId, parsedRequest.BaseUrl, parsedRequest.Url, streamUrl) - req, err := http.NewRequest( - "POST", - streamUrl, - bytes.NewBuffer([]byte(data)), - ) - - // Checking as LATE as possible, ensuring we don't rerun what's already ran - //ctx = context.Background() - newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID) - _, err = shuffle.GetCache(ctx, newExecId) - if err == nil { - log.Printf("[DEBUG] Result for %s already found (PRE REQUEST) - returning", newExecId) +newresp, err := client.Do(req) +if err != nil { + // Another timeout issue here somewhere + // context deadline + if strings.Contains(fmt.Sprintf("%s", err), "context deadline exceeded") || strings.Contains(fmt.Sprintf("%s", err), "Client.Timeout exceeded") { return nil } - cacheData := []byte("1") - err = shuffle.SetCache(ctx, newExecId, cacheData, 30) - if err != nil { - log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) + if strings.Contains(fmt.Sprintf("%s", err), "timeout awaiting response") { + return nil + } + + newerr := fmt.Sprintf("%s", err) + if strings.Contains(newerr, "connection refused") || strings.Contains(newerr, "no such host") { + newerr = fmt.Sprintf("Failed connecting to app %s. Is the Docker image available?", appName) } else { - //log.Printf("[DEBUG][%s] Adding %s to cache (%#v)", workflowExecution.ExecutionId, newExecId, action.Name) + // escape quotes and newlines + newerr = strings.ReplaceAll(strings.ReplaceAll(newerr, "\"", "\\\""), "\n", "\\n") } - client := shuffle.GetExternalClient(streamUrl) - customTimeout := os.Getenv("SHUFFLE_APP_REQUEST_TIMEOUT") - if len(customTimeout) > 0 { - // convert to int - timeoutInt, err := strconv.Atoi(customTimeout) - if err != nil { - log.Printf("[ERROR] Failed converting SHUFFLE_APP_REQUEST_TIMEOUT to int: %s", err) - } else { - log.Printf("[DEBUG] Setting client timeout to %d seconds for app request", timeoutInt) - client.Timeout = time.Duration(timeoutInt) * time.Second - } + if strings.Contains(fmt.Sprintf("%s", err), "no such host") { + log.Printf("[DEBUG] SHOULD be Removing references to location for app %s as to be rediscovered", action.AppName) + + //for k, v := range portMappings { + // if strings.Contains(strings.ToLower(strings.ReplaceAll(action.AppName, " ", "_"))) { + // } + //} + + //var portMappings map[string]int } - newresp, err := client.Do(req) - if err != nil { - // Another timeout issue here somewhere - // context deadline - if strings.Contains(fmt.Sprintf("%s", err), "context deadline exceeded") || strings.Contains(fmt.Sprintf("%s", err), "Client.Timeout exceeded") { - return nil - } - - if strings.Contains(fmt.Sprintf("%s", err), "timeout awaiting response") { - return nil - } - - newerr := fmt.Sprintf("%s", err) - if strings.Contains(newerr, "connection refused") || strings.Contains(newerr, "no such host") { - newerr = fmt.Sprintf("Failed connecting to app %s. Is the Docker image available?", appName) - } else { - // escape quotes and newlines - newerr = strings.ReplaceAll(strings.ReplaceAll(newerr, "\"", "\\\""), "\n", "\\n") - } - - if strings.Contains(fmt.Sprintf("%s", err), "no such host") { - log.Printf("[DEBUG] SHOULD be Removing references to location for app %s as to be rediscovered", action.AppName) - - //for k, v := range portMappings { - // if strings.Contains(strings.ToLower(strings.ReplaceAll(action.AppName, " ", "_"))) { - // } - //} - - //var portMappings map[string]int - } - - log.Printf("[ERROR][%s] Error running app run request: %s", workflowExecution.ExecutionId, err) - actionResult := shuffle.ActionResult{ - Action: *action, - ExecutionId: workflowExecution.ExecutionId, - Authorization: workflowExecution.Authorization, - Result: fmt.Sprintf(`{"success": false, "reason": "Failed to connect to app %s in swarm. Try the action again, restart Orborus if this is recurring, or contact support@shuffler.io.", "details": "%s"}`, streamUrl, newerr), - StartedAt: int64(time.Now().Unix()), - CompletedAt: int64(time.Now().Unix()), + log.Printf("[ERROR][%s] Error running app run request: %s", workflowExecution.ExecutionId, err) + actionResult := shuffle.ActionResult{ + Action: *action, + ExecutionId: workflowExecution.ExecutionId, + Authorization: workflowExecution.Authorization, + Result: fmt.Sprintf(`{"success": false, "reason": "Failed to connect to app %s in swarm. Try the action again, restart Orborus if this is recurring, or contact support@shuffler.io.", "details": "%s"}`, streamUrl, newerr), + StartedAt: int64(time.Now().Unix()), + CompletedAt: int64(time.Now().Unix()), Status: "FAILURE", } From a84e85dd96ee21166df904b37b60ee5fc6b33217 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 24 Apr 2024 15:41:09 +0200 Subject: [PATCH 066/142] Re-bump of versions --- functions/onprem/orborus/go.mod | 2 +- functions/onprem/worker/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 1d93d05d..2e1a4551 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -1,6 +1,6 @@ module orborus -go 1.22.2 +go 1.22 require ( github.com/docker/docker v26.1.0+incompatible diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile index b1bbbe43..a8e675ea 100755 --- a/functions/onprem/worker/Dockerfile +++ b/functions/onprem/worker/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19-buster as builder +FROM golang:1.22 as builder WORKDIR /app #RUN go get github.com/docker/docker/api/types github.com/docker/docker/api/types/container github.com/docker/docker/client From 584e7eac87a57b65525ff17a910ba6c8f1569e36 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 24 Apr 2024 15:44:25 +0200 Subject: [PATCH 067/142] Bumped backend versions to match golang 1.22 --- backend/go-app/docker.go | 7 +- backend/go-app/go.mod | 132 +++++++------- backend/go-app/go.sum | 379 ++++++++++++++++++--------------------- 3 files changed, 250 insertions(+), 268 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 5b1bc1ee..03312201 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -20,6 +20,7 @@ import ( //"github.com/docker/docker" "github.com/docker/docker/api/types" //"github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" newdockerclient "github.com/fsouza/go-dockerclient" "github.com/go-git/go-billy/v5" @@ -565,7 +566,7 @@ func imageCheckBuilder(images []string) error { return err } - filteredImages := []types.ImageSummary{} + filteredImages := []image.Summary{} for _, image := range allImages { found := false for _, repoTag := range image.RepoTags { @@ -636,10 +637,10 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { All: true, }) - img := types.ImageSummary{} + img := image.Summary{} tagFound := "" - img2 := types.ImageSummary{} + img2 := image.Summary{} tagFound2 := "" alternativeNameSplit := strings.Split(version.Name, "/") diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index d11b6e37..320c36f6 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,122 +1,134 @@ -module shuffle-shared +module shuffle -replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared - -go 1.19 +go 1.22.2 require ( - cloud.google.com/go/datastore v1.11.0 - cloud.google.com/go/storage v1.30.1 + cloud.google.com/go/datastore v1.15.0 + cloud.google.com/go/storage v1.40.0 github.com/basgys/goxml2json v1.1.0 github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 - github.com/docker/docker v24.0.2+incompatible + github.com/docker/docker v26.1.0+incompatible github.com/frikky/kin-openapi v0.42.0 - github.com/fsouza/go-dockerclient v1.9.7 + github.com/fsouza/go-dockerclient v1.11.0 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.5.0 - github.com/go-git/go-git/v5 v5.11.0 - github.com/gorilla/mux v1.8.0 + github.com/go-git/go-git/v5 v5.12.0 + github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.14 - golang.org/x/crypto v0.16.0 - google.golang.org/api v0.125.0 - google.golang.org/grpc v1.55.0 + github.com/shuffle/shuffle-shared v0.6.16 + golang.org/x/crypto v0.22.0 + google.golang.org/api v0.176.1 + google.golang.org/grpc v1.63.2 gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.22.5 - k8s.io/apimachinery v0.22.5 - k8s.io/client-go v0.22.5 + k8s.io/api v0.30.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.3 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/auth v0.3.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.7 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/adrg/strutil v0.2.3 // indirect github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect github.com/bitly/go-simplejson v0.5.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect - github.com/cloudflare/circl v1.3.3 // indirect - github.com/containerd/containerd v1.6.18 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/containerd/containerd v1.6.26 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/frikky/schemaless v0.0.9 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-github/v28 v28.1.1 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/s2a-go v0.1.4 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.10.0 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.11.13 // indirect - github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect - github.com/moby/patternmatcher v0.5.0 // indirect + github.com/klauspost/compress v1.15.9 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/opencontainers/runc v1.1.5 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect github.com/opensearch-project/opensearch-go v1.1.0 // indirect github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sashabaranov/go-openai v1.19.2 // indirect - github.com/sergi/go-diff v1.1.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/skeema/knownhosts v1.2.1 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/skeema/knownhosts v1.2.2 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/src-d/gcfg v1.4.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/oauth2 v0.19.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - golang.org/x/tools v0.13.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.18.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/klog/v2 v2.30.0 // indirect - k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index c12f4bd9..96ffd90c 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -16,25 +16,27 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/auth v0.3.0 h1:PRyzEpGfx/Z9e8+lHsbkoUVXD0gnu4MNmm7Gp8TQNIs= +cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= -cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= +cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= +cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= +cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -45,21 +47,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= -cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw= +cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -67,12 +63,11 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/Microsoft/hcsshim v0.9.10 h1:TxXGNmcbQxBKVWvjvTocNb6jrPyeHlk5EiDhhgHgggs= +github.com/Microsoft/hcsshim v0.9.10/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= +github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= @@ -82,10 +77,8 @@ github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= @@ -111,55 +104,44 @@ github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3 github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 h1:9bAydALqAjBfPHd/eAiJBHnMZUYov8m2PkXVr+YGQeI= github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA14J0sA3Hph4dt+AfCjPrYR13+vVodshQSM7km9qw= +github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns= -github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/containerd/containerd v1.6.26 h1:VVfrE6ZpyisvB1fzoY8Vkiq4sy+i5oF4uk7zu03RaHs= +github.com/containerd/containerd v1.6.26/go.mod h1:I4TRdsdoo5MlKob5khDJS2EPT1l1oMNaE2MBm6FrwxM= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg= -github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v26.1.0+incompatible h1:W1G9MPNbskA6VZWL7b3ZljTh0pXI68FpINx0GKaOdaM= +github.com/docker/docker v26.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -167,14 +149,10 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= @@ -182,13 +160,14 @@ github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2 github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= -github.com/fsouza/go-dockerclient v1.9.7/go.mod h1:vx9C32kE2D15yDSOMCDaAEIARZpDQDFBHeqL3MgQy/U= +github.com/fsouza/go-dockerclient v1.11.0 h1:4ZAk6W7rPAtPXm7198EFqA5S68rwnNQORxlOA5OurCA= +github.com/fsouza/go-dockerclient v1.11.0/go.mod h1:0I3TQCRseuPTzqlY4Y3ajfsg2VAdMQoazrkxJTiJg8s= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= @@ -196,27 +175,30 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -248,11 +230,13 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -273,7 +257,6 @@ github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv3 github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= @@ -281,6 +264,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -292,26 +276,23 @@ github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg= -github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= @@ -320,14 +301,13 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -337,10 +317,9 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -350,38 +329,36 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= -github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= -github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= @@ -397,7 +374,8 @@ github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxe github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -414,16 +392,13 @@ github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3 github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= +github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= @@ -431,7 +406,6 @@ github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5E github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -441,39 +415,36 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGSEja9zG6s= +github.com/shuffle/shuffle-shared v0.6.16/go.mod h1:HhQTn7xZZ69ZTc4EptO9OeNmgbKDyGlWAhFkUFUAHSA= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -488,12 +459,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= @@ -511,7 +479,26 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0 h1:Ydage/P0fRrSPpZeCVxzjqGcI6iVmG2xb43+IR8cjqM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= +go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -522,10 +509,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= @@ -533,8 +517,9 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -575,8 +560,9 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -590,7 +576,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -615,7 +600,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -631,8 +615,9 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -642,8 +627,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -657,8 +642,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -668,14 +654,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -690,7 +674,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -703,10 +686,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -726,11 +706,10 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -742,8 +721,9 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -766,8 +746,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -829,14 +809,15 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -857,8 +838,8 @@ google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQW google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.125.0 h1:7xGvEY4fyWbhWMHf3R2/4w7L4fXyfpRGE9g6lp8+DCk= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4= +google.golang.org/api v0.176.1/go.mod h1:j2MaSDYcvYV1lkZ1+SMW4IeF90SrEyFA+tluDYWRrFg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -891,7 +872,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -902,17 +882,16 @@ google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc= +google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -927,14 +906,11 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -947,14 +923,12 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -970,21 +944,18 @@ gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQb gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -992,27 +963,25 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/client-go v0.22.5 h1:I8Zn/UqIdi2r02aZmhaJ1hqMxcpfJ3t5VqvHtctHYFo= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 8a50b3a5d08a12a18257acc00895a43805313315 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 24 Apr 2024 15:48:04 +0200 Subject: [PATCH 068/142] Another version bump for dockerfile --- backend/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index cb287e8f..e34f9c11 100755 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19.3-buster as builder +FROM golang:1.22 as builder # Add files RUN mkdir /app @@ -41,4 +41,4 @@ COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certifica WORKDIR /app EXPOSE 5001 -CMD ["./webapp"] \ No newline at end of file +CMD ["./webapp"] From a8671ffaafeffc41f1ae1c883270a47fb214e118 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 24 Apr 2024 16:34:33 +0200 Subject: [PATCH 069/142] Bumped worker to use specific permissions --- functions/onprem/orborus/go.mod | 4 ++- functions/onprem/orborus/orborus.go | 45 ++++++++++----------------- functions/onprem/orborus/orborus.yaml | 2 +- functions/onprem/worker/worker.go | 38 ++++++++++++---------- 4 files changed, 41 insertions(+), 48 deletions(-) diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 2e1a4551..30e0deca 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -1,6 +1,8 @@ module orborus -go 1.22 +go 1.22.0 + +toolchain go1.22.2 require ( github.com/docker/docker v26.1.0+incompatible diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index ff0e68c5..74109e92 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -687,7 +687,6 @@ func deployWorker(image string, identifier string, env []string, executionReques return err } - log.Printf("CONFIG: %s", config.String()) env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String())) // Look for if there is a default service account in use @@ -698,22 +697,6 @@ func deployWorker(image string, identifier string, env []string, executionReques // use k8s downward API to find it if we are in a pod } - serviceAccounts, err := clientset.CoreV1().ServiceAccounts(kubernetesNamespace).List(context.Background(), metav1.ListOptions{}) - if err != nil { - log.Printf("[ERROR] Failed to list service accounts: %s", err) - } else { - log.Printf("[DEBUG] Found %d service accounts", len(serviceAccounts.Items)) - for _, serviceAccount := range serviceAccounts.Items { - log.Printf("[DEBUG] Service account: %s", serviceAccount.Name) - } - } - - for _, envVar := range os.Environ() { - if strings.Contains(strings.ToLower(envVar), "kubernetes") || strings.Contains(strings.ToLower(envVar), "k8s") { - log.Printf("[DEBUG] K8s var: %s", envVar) - } - } - // Check if namespace exist as variable. If so, make it if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 && !namespacemade { kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") @@ -968,24 +951,24 @@ func initializeImages() { if appSdkVersion == "" { appSdkVersion = "latest" - log.Printf("[WARNING] SHUFFLE_APP_SDK_VERSION not defined. Defaulting to %s", appSdkVersion) + log.Printf("[WARNING] SHUFFLE_APP_SDK_VERSION not defined. Defaulting to %#v", appSdkVersion) } if workerVersion == "" { workerVersion = "latest" - log.Printf("[WARNING] SHUFFLE_WORKER_VERSION not defined. Defaulting to %s", workerVersion) + log.Printf("[WARNING] SHUFFLE_WORKER_VERSION not defined. Defaulting to %#v", workerVersion) } if baseimageregistry == "" { baseimageregistry = "docker.io" // Dockerhub baseimageregistry = "ghcr.io" // Github - log.Printf("[DEBUG] Setting baseimageregistry") + log.Printf("[DEBUG] Setting baseimageregistry to %#v", baseimageregistry) } if baseimagename == "" { baseimagename = "frikky/shuffle" // Dockerhub baseimagename = "shuffle" // Github (ghcr.io) - log.Printf("[DEBUG] Setting baseimagename") + log.Printf("[DEBUG] Setting baseimagename to %#v", baseimagename) } log.Printf("[DEBUG] Setting swarm config to %#v. Default is empty.", swarmConfig) @@ -1005,16 +988,20 @@ func initializeImages() { pullOptions := types.ImagePullOptions{} for _, image := range images { - log.Printf("[DEBUG] Pulling image %s", image) - reader, err := dockercli.ImagePull(ctx, image, pullOptions) - if err != nil { - log.Printf("[ERROR] Failed getting image %s: %s", image, err) + if isKubernetes == "true" { + log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image) + } else { + log.Printf("[DEBUG] Pulling image %s", image) + reader, err := dockercli.ImagePull(ctx, image, pullOptions) + if err != nil { + log.Printf("[ERROR] Failed getting image %s: %s", image, err) - continue + continue + } + + io.Copy(os.Stdout, reader) + log.Printf("[DEBUG] Successfully downloaded and built %s", image) } - - io.Copy(os.Stdout, reader) - log.Printf("[DEBUG] Successfully downloaded and built %s", image) } } diff --git a/functions/onprem/orborus/orborus.yaml b/functions/onprem/orborus/orborus.yaml index 36ac49e8..0ff50ef7 100644 --- a/functions/onprem/orborus/orborus.yaml +++ b/functions/onprem/orborus/orborus.yaml @@ -75,7 +75,7 @@ spec: value: "ghcr.io/shuffle/shuffle-worker:nightly" image: ghcr.io/shuffle/shuffle-orborus:nightly - #imagePullPolicy: Never + imagePullPolicy: Always name: shuffle-orborus resources: {} hostname: shuffle-orborus diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 2367e187..87780f2b 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -1901,29 +1901,33 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar { } func getKubernetesClient() (*kubernetes.Clientset, error) { - kubeconfigContent := os.Getenv("KUBECONFIG_CONTENT") + + // Gets the config content from Orborus. + kubeconfigContent := os.Getenv("KUBERNETES_CONFIG") if len(kubeconfigContent) > 0 { log.Printf("[INFO] Using KUBERNETES_CONFIG to set up Kubernetes client: %#v", os.Getenv("KUBERNETES_CONFIG")) config, err := rest.InClusterConfig() if err != nil { - return nil, err + log.Printf("[ERROR] Failed to create Kubernetes client from in-cluster config: %s", err) + } else { + // Replace client configuration with kubeconfig content + config, err = clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfigContent)) + if err != nil { + log.Printf("[ERROR] Failed to create Kubernetes client from KUBERNETES_CONFIG: %s", err) + } else { + // Create Kubernetes client + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + return clientset, nil + } } + } - // Replace client configuration with kubeconfig content - config, err = clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfigContent)) - if err != nil { - return nil, err - } - - // Create Kubernetes client - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - - return clientset, nil - - } else if isRunningInCluster() { + // Fallback + if isRunningInCluster() { config, err := rest.InClusterConfig() if err != nil { return nil, err From 7e7a59ba5ab8175c9ace7a9f35a908bff5f0e618 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 25 Apr 2024 18:10:34 +0200 Subject: [PATCH 070/142] Changed from docker-compose to docker compose --- .github/install-guide.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index f033838a..bfc3857d 100755 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -15,7 +15,7 @@ The Docker setup is done with docker-compose **PS: if you're setting up Shuffle on Windows, go to the next step (Windows Docker setup)** -1. Make sure you have [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed, and that you have a minimum of **2Gb of RAM** available. +1. Make sure you have [Docker](https://docs.docker.com/get-docker/) installed, and that you have a minimum of **2Gb of RAM** available. 2. Download Shuffle ```bash git clone https://github.com/Shuffle/Shuffle @@ -32,7 +32,7 @@ sudo swapoff -a # Disable swap 4. Run docker-compose. ```bash -docker-compose up -d +docker compose up -d ``` 5. Recommended for Opensearch to work well @@ -57,9 +57,9 @@ This step is for setting up with Docker on windows from scratch. OUTER_HOSTNAME=YOUR.IP.HERE ``` -6. Run docker-compose +6. Run docker compose ```bash -docker-compose up -d +docker compose up -d ``` ### Configurations (high availability, scale, proxies, default users etc.) @@ -124,8 +124,8 @@ Large portions of the backend is written in another repository - [shuffle-shared ## Database - Opensearch Make sure this is running through the docker-compose, and that the backend points to it with SHUFFLE_OPENSEARCH_URL defined. -So essentially, what that means is: -1. Make sure you have docker-compose installed +What it means: +1. Make sure you have docker compose installed 2. Make sure you have the docker-compose.yml file from this repository 3. Run `docker-compose up opensearch -d` From de672b317ac1b302cfe07e22c93c9ed1dc6c2182 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 25 Apr 2024 18:13:32 +0200 Subject: [PATCH 071/142] Added more references to production readiness --- .github/install-guide.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index bfc3857d..9853efdf 100755 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -6,12 +6,12 @@ Shuffle Installation -Installation of Shuffle is currently available for docker and kubernetes. Looking for how to update Shuffle? Check the [updating guide](https://shuffler.io/docs/configuration#updating_shuffle) +Installation of Shuffle is currently available for [docker](https://shuffler.io/docs/configuration#production-readiness) and [kubernetes](https://shuffler.io/docs/configuration#Kubernetes). Looking for how to update Shuffle? Check the [updating guide](https://shuffler.io/docs/configuration#updating_shuffle) -This document outlines an introduction environment which is not scalable. [Read here](https://shuffler.io/docs/configuration#production_readiness) for information on production readiness. This also includes system requirements and configurations for Swarm or Kubernetes. +This document outlines an introduction environment which is **not** scalable. [Read here](https://shuffler.io/docs/configuration#production_readiness) for information on production readiness and scalability. This also includes system requirements and configurations for **Docker Swarm** or **Kubernetes**. # Docker - *nix -The Docker setup is done with docker-compose +The Docker setup is the default setup, and is ran with docker compose. This is [NOT a scalable build](https://shuffler.io/docs/configuration#production-readiness) without changes. **PS: if you're setting up Shuffle on Windows, go to the next step (Windows Docker setup)** @@ -40,7 +40,7 @@ docker compose up -d sudo sysctl -w vm.max_map_count=262144 # https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html ``` -When you're done, skip to the [After installation](#after-installation) step below. +When you're done, go to the [After installation](#after-installation) step below. ## Windows with WSL This step is for setting up with Docker on windows from scratch. From e05e487db9ce96a549eef8128ab62d3ec27a28ad Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 25 Apr 2024 18:26:18 +0200 Subject: [PATCH 072/142] Added more docs for running hybrid cloud --- .github/install-guide.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index 9853efdf..bc7f855d 100755 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -6,7 +6,10 @@ Shuffle Installation -Installation of Shuffle is currently available for [docker](https://shuffler.io/docs/configuration#production-readiness) and [kubernetes](https://shuffler.io/docs/configuration#Kubernetes). Looking for how to update Shuffle? Check the [updating guide](https://shuffler.io/docs/configuration#updating_shuffle) +Installation of Shuffle is currently available for [docker](https://shuffler.io/docs/configuration#production-readiness) and [kubernetes](https://shuffler.io/docs/configuration#Kubernetes). + +- Looking to run workflows onprem, but don't want to run the entire stack? Looks into [Environments & Orborus hosting Onprem](https://shuffler.io/docs/organizations#Environments) +- Looking for how to update Shuffle? Check the [updating guide](https://shuffler.io/docs/configuration#updating_shuffle) This document outlines an introduction environment which is **not** scalable. [Read here](https://shuffler.io/docs/configuration#production_readiness) for information on production readiness and scalability. This also includes system requirements and configurations for **Docker Swarm** or **Kubernetes**. From 9e34930718e0780a79c15a8a639a0bd0936d5d30 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 26 Apr 2024 17:45:07 +0200 Subject: [PATCH 073/142] Loads of minor updates with cloud/onprem sync --- frontend/src/components/Billing.jsx | 2 +- frontend/src/components/Branding.jsx | 4 +- frontend/src/components/CacheView.jsx | 224 ++++++++++++------------- frontend/src/components/Files.jsx | 31 ++-- frontend/src/components/NewHeader.jsx | 2 +- frontend/src/components/Priorities.jsx | 21 +-- frontend/src/components/Priority.jsx | 6 +- frontend/src/views/AngularWorkflow.jsx | 26 ++- frontend/src/views/AppCreator.jsx | 112 +++++++------ 9 files changed, 229 insertions(+), 199 deletions(-) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index c3687489..d3a65927 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -970,7 +970,7 @@ const Billing = (props) => { const isChildOrg = userdata.active_org.creator_org !== "" && userdata.active_org.creator_org !== undefined && userdata.active_org.creator_org !== null return ( -
    +
    {addDealModal} Billing & Licensing diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 9dd40791..c3c32058 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -15,7 +15,7 @@ import { //import { useAlert const Branding = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; + const { globalUrl, userdata, serverside, billingInfo,clickedFromOrgTab, stripeKey, selectedOrganization, handleGetOrg, } = props; //const alert = useAlert(); const [publishingInfo, setPublishingInfo] = useState(""); const [publishRequirements, setPublishRequirements] = useState([]) @@ -103,7 +103,7 @@ const Branding = (props) => { } return ( -
    +

    Branding

    diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 4d4d02fe..4e4f2fdd 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -1,10 +1,10 @@ import React, { useState, useEffect } from "react"; import theme from "../theme.jsx"; -import { toast } from 'react-toastify'; -import ReactJson from "react-json-view"; - +import { toast } from 'react-toastify'; +import ReactJson from "react-json-view"; + import { - Typography, + Typography, Tooltip, Divider, TextField, @@ -22,8 +22,8 @@ import { } from "@mui/material"; import { - Link as LinkIcon, - AutoFixHigh as AutoFixHighIcon, + Link as LinkIcon, + AutoFixHigh as AutoFixHighIcon, Edit as EditIcon, FileCopy as FileCopyIcon, SelectAll as SelectAllIcon, @@ -46,7 +46,7 @@ import { Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon, } from "@mui/icons-material"; -import { validateJson, } from "../views/Workflows.jsx"; +import { validateJson, } from "../views/Workflows.jsx"; const scrollStyle1 = { height: 100, @@ -64,9 +64,9 @@ const scrollStyle2 = { overflow: "scroll", } - + const CacheView = (props) => { - const { globalUrl, userdata, serverside, orgId } = props; + const { globalUrl, userdata, serverside, orgId, isSelectedDataStore } = props; const [orgCache, setOrgCache] = React.useState(""); const [listCache, setListCache] = React.useState([]); const [addCache, setAddCache] = React.useState(""); @@ -79,7 +79,7 @@ const CacheView = (props) => { const [dataValue, setDataValue] = React.useState({}); const [editCache, setEditCache] = React.useState(false); const [show, setShow] = useState({}); - + useEffect(() => { listOrgCache(orgId); }, []); @@ -155,22 +155,22 @@ const CacheView = (props) => { const deleteCache = (orgId, key) => { toast("Attempting to delete Cache"); - + // method: "DELETE", - const method = "POST" - //const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/${key}` - const url = `${globalUrl}/api/v1/orgs/${orgId}/delete_cache` - const parsed = { - "org_id": orgId, - "key": key, - } - + const method = "POST" + //const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/${key}` + const url = `${globalUrl}/api/v1/orgs/${orgId}/delete_cache` + const parsed = { + "org_id": orgId, + "key": key, + } + fetch(url, { - method: method, + method: method, headers: { Accept: "application/json", }, - body: JSON.stringify(parsed), + body: JSON.stringify(parsed), credentials: "include", }) .then((response) => { @@ -255,20 +255,20 @@ const CacheView = (props) => { }); }; - const isValidJson = validateJson(value) - const autoFixJson = (inputvalue) => { - console.log("inputvalue: ", inputvalue) - try { - var parsedjson = JSON.parse(inputvalue) - - // setValue() with the parsed json as string - setValue(JSON.stringify(parsedjson, null, 2)) - } catch (e) { - console.log("Error parsing JSON: ", e) - //return JSON.stringify(inputvalue); - } - } - + const isValidJson = validateJson(value) + const autoFixJson = (inputvalue) => { + console.log("inputvalue: ", inputvalue) + try { + var parsedjson = JSON.parse(inputvalue) + + // setValue() with the parsed json as string + setValue(JSON.stringify(parsedjson, null, 2)) + } catch (e) { + console.log("Error parsing JSON: ", e) + //return JSON.stringify(inputvalue); + } + } + const modalView = ( // console.log("key:", dataValue.key), //console.log("value:",dataValue.value), @@ -316,21 +316,21 @@ const CacheView = (props) => { />
    -
    - - Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON) - - - { - autoFixJson(value) - }} - > - - - -
    +
    + + Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON) + + + { + autoFixJson(value) + }} + > + + + +
    { id="Valuefield" margin="normal" variant="outlined" - multiline - minRows={4} - maxRows={12} + multiline + minRows={4} + maxRows={12} //defaultValue={editCache ? dataValue.value : ""} - value={value} + value={value} onChange={(e) => setValue(e.target.value)} />
    - - - + />} + + { if (index % 2 === 0) { bgColor = "#1f2023"; } - - const validate = validateJson(data.value); + + const validate = validateJson(data.value); return ( { primary={data.key} /> { - //handleReactJsonClipboard(copy); - }} - displayDataTypes={false} - onSelect={(select) => { - //HandleJsonCopy(showResult, select, data.action.label); - //console.log("SELECTED!: ", select); - }} - name={"value"} - /> - : - data.value + primary={validate.valid ? + { + //handleReactJsonClipboard(copy); + }} + displayDataTypes={false} + onSelect={(select) => { + //HandleJsonCopy(showResult, select, data.action.label); + //console.log("SELECTED!: ", select); + }} + name={"value"} + /> + : + data.value } /> { style={{ padding: "6px" }} onClick={() => { setEditCache(true) - setDataValue({ - "key": data.key, - "value":data.value - }) - setValue(data.value) + setDataValue({ + "key": data.key, + "value":data.value + }) + setValue(data.value) setModalOpen(true) }} > @@ -525,18 +525,18 @@ const CacheView = (props) => { { - window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank"); - }} + window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank"); + }} > - + diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index 106cd18d..c13bf067 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -41,7 +41,7 @@ import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import theme from "../theme.jsx"; const Files = (props) => { - const { globalUrl, userdata, serverside, selectedOrganization, isCloud, } = props; + const { globalUrl, userdata, serverside, selectedOrganization, isCloud,isSelectedFiles } = props; const [files, setFiles] = React.useState([]); const [selectedNamespace, setSelectedNamespace] = React.useState("default"); @@ -617,16 +617,16 @@ const Files = (props) => { style={{ maxWidth: window.innerWidth > 1366 ? 1366 : 1200, margin: "auto", - padding: 20, + padding: isSelectedFiles ? null : 20, }} onDrop={uploadFile} > -
    +
    setLoadFileModalOpen(true)} > @@ -636,15 +636,15 @@ const Files = (props) => { {fileDownloadModal} -
    -

    Files

    - +
    +

    Files

    + Files from Workflows are a way to store as well as edit files.{" "} Learn more @@ -659,6 +659,7 @@ const Files = (props) => { onClick={() => { upload.click(); }} + style={{backgroundColor: isSelectedFiles?'rgba(255, 132, 68, 0.2)':null, color:isSelectedFiles?"#FF8444":null, borderRadius:isSelectedFiles?200:null, width:isSelectedFiles?162:null, height:isSelectedFiles?40:null}} > Upload files @@ -678,7 +679,7 @@ const Files = (props) => { }} /> + - - - ); - })} - - -
    -

    WebHooks

    -
    - - - - - - - - - - - - {webHooks === undefined || webHooks === null - ? null - : webHooks.map((webhook, index) => { - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } - - return ( - - - - + + - - - ); - })} - - - {/*
    -

    Tenzir Pipelines

    - - Controls a pipeline to run things.{" "} - - Learn more - - + { + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(webhook.info.url); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999, + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + + toast("URL copied to clipboard"); + } + }} + > + + + + ) + } + /> + + + + + + ); + })} + + + {/*
    +

    Tenzir Pipelines

    + + Controls a pipeline to run things.{" "} + + Learn more + + +
    + + */}
    - - */} -
    -) : null; + ) : null; const appCategoryView = curTab === 8 ? ( From 1c97e6785568fa3ac024ac6c9d4d77f63d33556b Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Mon, 29 Apr 2024 07:35:37 +0000 Subject: [PATCH 081/142] adding pipelines to the ui --- frontend/src/views/Admin.jsx | 532 +++++++++++++++++++++-------------- 1 file changed, 327 insertions(+), 205 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index eb4173ef..81feaaab 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -203,7 +203,7 @@ const Admin = (props) => { const [selectedStatus, setSelectedStatus] = React.useState([]); const [webHooks, setWebHooks] = React.useState([]); const [allSchedules, setAllSchedules] = React.useState([]); - + const [pipelines, setPipelines] = React.useState([]); const [, forceUpdate] = React.useState(); const [showDeleteAccountTextbox, setShowDeleteAccountTextbox] = @@ -756,6 +756,7 @@ If you're interested, please let me know a time that works for you, or set up a .then((responseJson) => { setWebHooks(responseJson.webhooks || []); // Handling the case where the result is null or undefined setAllSchedules(responseJson.schedules || []); + setPipelines(responseJson.pipelines || []); }) .catch((error) => { toast(error.toString()); @@ -933,6 +934,14 @@ If you're interested, please let me know a time that works for you, or set up a }); }; + const changePipelineState = (pipeline, state) => { + if (state.trim() === ''){ + toast("state is not defined") + return + } + + } + if ( userdata.support === true && selectedOrganization.id !== "" && @@ -4573,94 +4582,112 @@ If you're interested, please let me know a time that works for you, or set up a backgroundColor: theme.palette.inputColor, }} /> - - - - - - - - - - {allSchedules === undefined || allSchedules === null - ? null - : allSchedules.map((schedule, index) => { - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } + {allSchedules === undefined || + allSchedules === null || + allSchedules.length === 0 ? ( +
    + No schedules found. +
    + ) : ( + + + + + + + + + + { allSchedules.map((schedule, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } - return ( - - 0 ? ( - schedule.frequency - ) : ( - {schedule.seconds} seconds - ) - } - /> - - - {schedule.workflow_id} - - } - /> - - - - - - ); - })} - + + + + ); + })} +
    + )}

    WebHooks

    @@ -4673,126 +4700,140 @@ If you're interested, please let me know a time that works for you, or set up a backgroundColor: theme.palette.inputColor, }} /> + {webHooks === undefined || webHooks === null || webHooks.length === 0 ? ( +
    + No webhooks found. +
    + ) : ( + + + + + + + + + {webHooks.map((webhook, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } - - - - - - - - - {webHooks === undefined || webHooks === null - ? null - : webHooks.map((webhook, index) => { - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } - - return ( - - - - - {webhook.workflows[0]} - - } - /> - - - { - const elementName = "copy_element_shuffle"; - var copyText = document.getElementById(elementName); - if (copyText !== null && copyText !== undefined) { - const clipboard = navigator.clipboard; - if (clipboard === undefined) { - toast("Can only copy over HTTPS (port 3443)"); - return; - } - - navigator.clipboard.writeText(webhook.info.url); - copyText.select(); - copyText.setSelectionRange( - 0, - 99999, - ); /* For mobile devices */ - - /* Copy the text inside the text field */ - document.execCommand("copy"); - - toast("URL copied to clipboard"); - } - }} - > - - - - ) - } - /> - - - - - - ); - })} - + {webhook.workflows[0]} + + } + /> - {/*
    + + { + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(webhook.info.url); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999, + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + + toast("URL copied to clipboard"); + } + }} + > + + + + ) + } + /> + + + + + + ); + })} + + )} + +

    Tenzir Pipelines

    Controls a pipeline to run things.{" "} @@ -4813,9 +4854,90 @@ If you're interested, please let me know a time that works for you, or set up a marginBottom: 20, backgroundColor: theme.palette.inputColor, }} - /> */} + /> + {pipelines === undefined || pipelines === null || pipelines.length === 0 ? ( +
    + No pipelines found. +
    + ) : ( + + + + + + + + {pipelines.map((pipeline, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + return ( + + + + + {pipeline.workflow_id} + + } + /> + + + + + ); + })} + + )}
    - ) : null; + ) : null; const appCategoryView = curTab === 8 ? ( From 8567b7e722d4a0171a650be080b0d3fed0cc1eb3 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 29 Apr 2024 11:14:04 +0200 Subject: [PATCH 082/142] Fixed a looping bug with SHUFFLE_NO_SPLITTER where it sometimes didn't do loops at all --- backend/app_sdk/app_base.py | 149 +++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 71 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 54b238e3..6f9afbd5 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -3434,95 +3434,102 @@ class AppBase: # Loop WITH variables go in else. handled = False + self.logger.info("ACTUALITEM: %s" % actualitem) + # Has a loop without a variable used inside if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": tmpitem = value - index = 0 - replacement = actualitem[index][2] - if replacement.endswith("}$"): - replacement = replacement[:-2] + #index = 0 + for index in range(len(actualitem)): + # Check if it's SHUFFLE_NO_SPLITTER + if actualitem[index][1] != "SHUFFLE_NO_SPLITTER": + continue - if replacement.startswith("\"") and replacement.endswith("\""): - replacement = replacement[1:len(replacement)-1] + replacement = actualitem[index][2] + if replacement.endswith("}$"): + replacement = replacement[:-2] - #json_replacement = tmpitem.replace(actualitem[index][0], replacement, 1) - json_replacement = replacement - try: - json_replacement = json.loads(replacement) - except json.decoder.JSONDecodeError as e: + if replacement.startswith("\"") and replacement.endswith("\""): + replacement = replacement[1:len(replacement)-1] + + #json_replacement = tmpitem.replace(actualitem[index][0], replacement, 1) + json_replacement = replacement try: - replacement = replacement.replace("\'", "\"", -1) json_replacement = json.loads(replacement) - except: - self.logger.info("JSON error singular: %s" % e) - - if len(json_replacement) > minlength: - minlength = len(json_replacement) - - self.logger.info("PRE new_replacement") - - new_replacement = [] - for i in range(len(json_replacement)): - if isinstance(json_replacement[i], dict) or isinstance(json_replacement[i], list): - tmp_replacer = json.dumps(json_replacement[i]) - newvalue = tmpitem.replace(str(actualitem[index][0]), str(tmp_replacer), 1) - else: - newvalue = tmpitem.replace(str(actualitem[index][0]), str(json_replacement[i]), 1) - - try: - newvalue = parse_liquid(newvalue, self) - except Exception as e: - self.logger.info(f"[WARNING] Failed liquid parsing in loop (2): {e}") - - try: - newvalue = json.loads(newvalue) except json.decoder.JSONDecodeError as e: - pass + try: + replacement = replacement.replace("\'", "\"", -1) + json_replacement = json.loads(replacement) + except: + self.logger.info("JSON error singular: %s" % e) - new_replacement.append(newvalue) + if len(json_replacement) > minlength: + minlength = len(json_replacement) + + self.logger.info("PRE new_replacement") + + new_replacement = [] + for i in range(len(json_replacement)): + if isinstance(json_replacement[i], dict) or isinstance(json_replacement[i], list): + tmp_replacer = json.dumps(json_replacement[i]) + newvalue = tmpitem.replace(str(actualitem[index][0]), str(tmp_replacer), 1) + else: + newvalue = tmpitem.replace(str(actualitem[index][0]), str(json_replacement[i]), 1) + + try: + newvalue = parse_liquid(newvalue, self) + except Exception as e: + self.logger.info(f"[WARNING] Failed liquid parsing in loop (2): {e}") + + try: + newvalue = json.loads(newvalue) + except json.decoder.JSONDecodeError as e: + pass + + new_replacement.append(newvalue) - # FIXME: Should this use new_replacement? - tmpitem = tmpitem.replace(actualitem[index][0], replacement, 1) + # FIXME: Should this use new_replacement? + tmpitem = tmpitem.replace(actualitem[index][0], replacement, 1) - # This code handles files. - resultarray = [] - isfile = False - try: - if parameter["schema"]["type"] == "file" and len(value) > 0: - self.logger.info("(1) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % tmpitem) - # This is silly :) - # Q: Is there something wrong with the download system? - # It seems to return "FILE CONTENT: %s" with the ID as %s - for tmp_file_split in json.loads(tmpitem): - file_value = self.get_file(tmp_file_split) - resultarray.append(file_value) + # This code handles files. + resultarray = [] + isfile = False + try: + if parameter["schema"]["type"] == "file" and len(value) > 0: + self.logger.info("(1) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % tmpitem) + # This is silly :) + # Q: Is there something wrong with the download system? + # It seems to return "FILE CONTENT: %s" with the ID as %s + for tmp_file_split in json.loads(tmpitem): + file_value = self.get_file(tmp_file_split) + resultarray.append(file_value) - isfile = True - except NameError as e: - self.logger.info("(1) SCHEMA NAMEERROR IN FILE HANDLING: %s" % e) - except KeyError as e: - self.logger.info("(1) SCHEMA KEYERROR IN FILE HANDLING: %s" % e) - except json.decoder.JSONDecodeError as e: - self.logger.info("(1) JSON ERROR IN FILE HANDLING: %s" % e) + isfile = True + except NameError as e: + self.logger.info("(1) SCHEMA NAMEERROR IN FILE HANDLING: %s" % e) + except KeyError as e: + self.logger.info("(1) SCHEMA KEYERROR IN FILE HANDLING: %s" % e) + except json.decoder.JSONDecodeError as e: + self.logger.info("(1) JSON ERROR IN FILE HANDLING: %s" % e) - if not isfile: - params[parameter["name"]] = tmpitem - multi_parameters[parameter["name"]] = new_replacement - else: - params[parameter["name"]] = resultarray - multi_parameters[parameter["name"]] = resultarray + if not isfile: + params[parameter["name"]] = tmpitem + multi_parameters[parameter["name"]] = new_replacement + else: + params[parameter["name"]] = resultarray + multi_parameters[parameter["name"]] = resultarray - #if len(resultarray) == 0: - # self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (1)") - # action_result["status"] = "SUCCESS" - # action_result["result"] = "[]" - # self.send_result(action_result, headers, stream_path) - # return + #if len(resultarray) == 0: + # self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (1)") + # action_result["status"] = "SUCCESS" + # action_result["result"] = "[]" + # self.send_result(action_result, headers, stream_path) + # return - multi_execution_lists.append(new_replacement) + multi_execution_lists.append(new_replacement) #self.logger.info("MULTI finished: %s" % json_replacement) else: # This is here to handle for loops within variables.. kindof From 7e92c8d399364ef7810d42af96d1e5be56286829 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 29 Apr 2024 11:35:21 +0200 Subject: [PATCH 083/142] Fixed duplicate parameter parsing. Requires full app update --- backend/app_sdk/app_base.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 6f9afbd5..8dcfc01d 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -2864,8 +2864,12 @@ class AppBase: # Handles for loops etc. # FIXME: Should it dump to string here? Doesn't that defeat the purpose? # Trying without string dumping. - + #self.logger.info("TO BE REPLACED: %s" % to_be_replaced) value, is_loop = get_json_value(fullexecution, to_be_replaced) + self.logger.info("OUTPUT (%s): %s" % (to_be_replaced, value)) + self.logger.info("PRE VALUE:\n%s" % parameter["value"]) + + #self.logger.info(f"\n\nType of value: {type(value)}") if isinstance(value, str): # Could we take it here? @@ -2879,26 +2883,25 @@ class AppBase: # returnvalue = fix_json_string_value(value) # value = returnvalue - - parameter["value"] = parameter["value"].replace(to_be_replaced, value) + parameter["value"] = parameter["value"].replace(to_be_replaced, value, 1) elif isinstance(value, dict) or isinstance(value, list): # Changed from JSON dump to str() 28.05.2021 # This makes it so the parameters gets lists and dicts straight up - parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value), 1) #try: - # parameter["value"] = parameter["value"].replace(to_be_replaced, str(value)) - #except: # parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + #except: + # parameter["value"] = parameter["value"].replace(to_be_replaced, str(value)) # self.logger.info("Failed parsing value as string?") else: self.logger.error("[ERROR] Unknown type %s" % type(value)) try: - parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value), 1) except json.decoder.JSONDecodeError as e: - parameter["value"] = parameter["value"].replace(to_be_replaced, value) + parameter["value"] = parameter["value"].replace(to_be_replaced, value, 1) - #self.logger.info("VALUE: %s" % parameter["value"]) + self.logger.info("POST VALUE: \n%s" % parameter["value"]) else: #self.logger.info(f"[ERROR] Not running static variant regex parsing (slow) on value with length {len(parameter['value'])}. Max is 5Mb~.") pass @@ -3434,8 +3437,6 @@ class AppBase: # Loop WITH variables go in else. handled = False - self.logger.info("ACTUALITEM: %s" % actualitem) - # Has a loop without a variable used inside if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": From 5e789d4a536fb17be76f6898011718ecde8cb894 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 29 Apr 2024 12:40:23 +0200 Subject: [PATCH 084/142] Fixed SHUFFLE_NO_SPLITTER loop bugs --- backend/app_sdk/app_base.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 8dcfc01d..7d20fa1a 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1127,6 +1127,8 @@ class AppBase: #param_multiplier = await self.get_param_multipliers(newparams) param_multiplier = self.get_param_multipliers(newparams) + #self.logger.info("PARAM MULTIPLIER: %s" % param_multiplier) + # FIXME: This does a deduplication of the data new_params = self.validate_unique_fields(param_multiplier) #self.logger.info(f"NEW PARAMS: {new_params}") @@ -2866,9 +2868,6 @@ class AppBase: # Trying without string dumping. #self.logger.info("TO BE REPLACED: %s" % to_be_replaced) value, is_loop = get_json_value(fullexecution, to_be_replaced) - self.logger.info("OUTPUT (%s): %s" % (to_be_replaced, value)) - self.logger.info("PRE VALUE:\n%s" % parameter["value"]) - #self.logger.info(f"\n\nType of value: {type(value)}") if isinstance(value, str): @@ -2901,7 +2900,6 @@ class AppBase: except json.decoder.JSONDecodeError as e: parameter["value"] = parameter["value"].replace(to_be_replaced, value, 1) - self.logger.info("POST VALUE: \n%s" % parameter["value"]) else: #self.logger.info(f"[ERROR] Not running static variant regex parsing (slow) on value with length {len(parameter['value'])}. Max is 5Mb~.") pass @@ -3469,8 +3467,6 @@ class AppBase: if len(json_replacement) > minlength: minlength = len(json_replacement) - self.logger.info("PRE new_replacement") - new_replacement = [] for i in range(len(json_replacement)): if isinstance(json_replacement[i], dict) or isinstance(json_replacement[i], list): @@ -3484,11 +3480,14 @@ class AppBase: except Exception as e: self.logger.info(f"[WARNING] Failed liquid parsing in loop (2): {e}") + tmpitem = str(newvalue) + try: newvalue = json.loads(newvalue) except json.decoder.JSONDecodeError as e: pass + # The list to use for the multi execution IF not a file list new_replacement.append(newvalue) @@ -3517,6 +3516,7 @@ class AppBase: self.logger.info("(1) JSON ERROR IN FILE HANDLING: %s" % e) if not isfile: + # Should be here in normal circumstances params[parameter["name"]] = tmpitem multi_parameters[parameter["name"]] = new_replacement else: @@ -3661,7 +3661,7 @@ class AppBase: except KeyError as e: self.logger.info("SCHEMA ERROR IN FILE HANDLING: %s" % e) - + #remove_params.append(parameter["name"]) # Fix lists here # FIXME: This doesn't really do anything anymore @@ -3940,7 +3940,8 @@ class AppBase: # 1. Use number of executions based on the arrays being similar # 2. Find the right value from the parsed multi_params - self.logger.info("[INFO] Running WITHOUT outer loop (looping)") + #self.logger.info("[INFO] Running WITH loop. MULTI: %s", multi_parameters) + self.logger.info("[INFO] Running WITH loop") json_object = False #results = await self.run_recursed_items(func, multi_parameters, {}) results = self.run_recursed_items(func, multi_parameters, {}) From 9837e155023489050081fde345e1c1838111e278 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Mon, 29 Apr 2024 10:55:09 +0000 Subject: [PATCH 085/142] some minor nits --- frontend/src/views/Admin.jsx | 199 +++++++++++++++---------- frontend/src/views/AngularWorkflow.jsx | 30 ++-- 2 files changed, 136 insertions(+), 93 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 81feaaab..327bfa55 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -756,7 +756,7 @@ If you're interested, please let me know a time that works for you, or set up a .then((responseJson) => { setWebHooks(responseJson.webhooks || []); // Handling the case where the result is null or undefined setAllSchedules(responseJson.schedules || []); - setPipelines(responseJson.pipelines || []); + // setPipelines(responseJson.pipelines || []); }) .catch((error) => { toast(error.toString()); @@ -935,13 +935,54 @@ If you're interested, please let me know a time that works for you, or set up a }; const changePipelineState = (pipeline, state) => { - if (state.trim() === ''){ - toast("state is not defined") - return + if (state.trim() === "") { + toast("state is not defined"); + return; } - - } - + + const data = { + name: pipeline.name, + type: state, + environment: pipeline.environment, + workflow_id: pipeline.workflow_id, + trigger_id: pipeline.trigger_id, + }; + + if (state === "start") toast("starting the pipeline"); + else toast("stopping the pipeline"); + + const url = `${globalUrl}/api/v1/triggers/pipeline`; + fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); + toast("Failed to update the pipeline state"); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + toast("Failed to update the pipeline: " + responseJson.reason); + } else { + if (state === "start") toast("Successfully created pipeline"); + else toast("Sucessfully stopped the pipeline"); + } + }) + .catch((error) => { + //toast(error.toString()); + console.log("Get schedule error: ", error.toString()); + }); + }; + if ( userdata.support === true && selectedOrganization.id !== "" && @@ -4618,74 +4659,72 @@ If you're interested, please let me know a time that works for you, or set up a - { allSchedules.map((schedule, index) => { - var bgColor = "#27292d"; - if (index % 2 === 0) { - bgColor = "#1f2023"; - } + {allSchedules.map((schedule, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } - return ( - - 0 ? ( - schedule.frequency - ) : ( - {schedule.seconds} seconds - ) - } - /> - - - {schedule.workflow_id} - - } - /> - - - - - - ); - })} + return ( + + 0 ? ( + schedule.frequency + ) : ( + {schedule.seconds} seconds + ) + } + /> + + + {schedule.workflow_id} + + } + /> + + + + + + ); + })} )} @@ -4833,7 +4872,7 @@ If you're interested, please let me know a time that works for you, or set up a )} -
    + {/*

    Tenzir Pipelines

    Controls a pipeline to run things.{" "} @@ -4855,7 +4894,9 @@ If you're interested, please let me know a time that works for you, or set up a backgroundColor: theme.palette.inputColor, }} /> - {pipelines === undefined || pipelines === null || pipelines.length === 0 ? ( + {pipelines === undefined || + pipelines === null || + pipelines.length === 0 ? (
    {pipeline.status === "running" - ? "Stop webhook" - : "Start Webhook"} + ? "Stop pipeline" + : "Start pipeline"} ); })} - )} + )}*/}
    ) : null; diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 76953e68..5832f76f 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -13124,7 +13124,7 @@ const AngularWorkflow = (defaultprops) => { if (trigger.id === undefined) { return; } - + fetch(globalUrl + "/api/v1/hooks/" + trigger.id + "/delete", { method: "DELETE", headers: { @@ -13137,32 +13137,34 @@ const AngularWorkflow = (defaultprops) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!"); } - + return response.json(); }) .then((responseJson) => { + if (!responseJson.success) { + if (responseJson.reason !== undefined) { + toast("Failed to stop webhook: " + responseJson.reason); + } + } else { + toast("Successfully stopped webhook"); + } if (workflow.triggers[triggerindex] !== undefined) { workflow.triggers[triggerindex].status = "stopped"; } - - if (responseJson.success) { - // Set the status - saveWorkflow(workflow); - } else { - if (responseJson.reason !== undefined) { - toast("Failed stopping webhook: " + responseJson.reason); - } - } - trigger.status = "stopped"; - setWorkflow(workflow); setSelectedTrigger(trigger); + setWorkflow(workflow); + saveWorkflow(workflow); + }) .catch((error) => { //toast(error.toString()); - toast("Delete webhook error. Contact support or check logs if this persists.") + toast( + "Delete webhook error. Contact support or check logs if this persists.", + ); }); }; + // POST to /api/v1/workflows const createWorkflow = (workflow, trigger_index) => { From 45a6089629fa3e04978c12e987b3a706df9bb0b0 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 29 Apr 2024 13:00:12 +0200 Subject: [PATCH 086/142] Removed custom handler for SHUFFLE_NO_SPLITTER loop handler and further debug logs --- backend/app_sdk/app_base.py | 263 +++++++++++------------------------- 1 file changed, 80 insertions(+), 183 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 7d20fa1a..d5194909 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -2526,7 +2526,7 @@ class AppBase: return template if "${" in template and "}$" in template: - self.logger.info("[DEBUG] Shuffle loop shouldn't run in liquid. Data length: %d" % len(template)) + #self.logger.info("[DEBUG] Shuffle loop shouldn't run in liquid. Data length: %d" % len(template)) return template @@ -3436,203 +3436,101 @@ class AppBase: handled = False # Has a loop without a variable used inside - if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": + + # This is here to handle for loops within variables.. kindof + # 1. Find the length of the longest array + # 2. Build an array with the base values based on parameter["value"] + # 3. Get the n'th value of the generated list from values + # 4. Execute all n answers + replacements = {} + curminlength = 0 + for replace in actualitem: + try: + to_be_replaced = replace[0] + actualitem = replace[2] + if actualitem.endswith("}$"): + actualitem = actualitem[:-2] - tmpitem = value + except IndexError: + self.logger.info("[WARNING] Indexerror") + continue - #index = 0 - for index in range(len(actualitem)): - # Check if it's SHUFFLE_NO_SPLITTER - if actualitem[index][1] != "SHUFFLE_NO_SPLITTER": - continue + try: + itemlist = json.loads(actualitem) + if len(itemlist) > minlength: + minlength = len(itemlist) - replacement = actualitem[index][2] - if replacement.endswith("}$"): - replacement = replacement[:-2] + if len(itemlist) > curminlength: + curminlength = len(itemlist) + + except json.decoder.JSONDecodeError as e: + self.logger.info("JSON Error (replace): %s in %s" % (e, actualitem)) + + replacements[to_be_replaced] = actualitem + + + # Parses the data as string with length, split etc. before moving on. + #self.logger.info("In second part of else: %s" % (len(itemlist))) + # This is a result array for JUST this value.. + # What if there are more? + resultarray = [] + for i in range(0, curminlength): + tmpitem = json.loads(json.dumps(parameter["value"])) + for key, value in replacements.items(): + replacement = value + try: + replacement = json.dumps(json.loads(value)[i]) + except IndexError as e: + self.logger.info(f"[ERROR] Failed handling value parsing with index: {e}") + pass if replacement.startswith("\"") and replacement.endswith("\""): replacement = replacement[1:len(replacement)-1] + #except json.decoder.JSONDecodeError as e: - #json_replacement = tmpitem.replace(actualitem[index][0], replacement, 1) - json_replacement = replacement + #self.logger.info("REPLACING %s with %s" % (key, replacement)) + #replacement = parse_wrapper_start(replacement) + tmpitem = tmpitem.replace(key, replacement, -1) try: - json_replacement = json.loads(replacement) - except json.decoder.JSONDecodeError as e: - try: - replacement = replacement.replace("\'", "\"", -1) - json_replacement = json.loads(replacement) - except: - self.logger.info("JSON error singular: %s" % e) - - if len(json_replacement) > minlength: - minlength = len(json_replacement) - - new_replacement = [] - for i in range(len(json_replacement)): - if isinstance(json_replacement[i], dict) or isinstance(json_replacement[i], list): - tmp_replacer = json.dumps(json_replacement[i]) - newvalue = tmpitem.replace(str(actualitem[index][0]), str(tmp_replacer), 1) - else: - newvalue = tmpitem.replace(str(actualitem[index][0]), str(json_replacement[i]), 1) - - try: - newvalue = parse_liquid(newvalue, self) - except Exception as e: - self.logger.info(f"[WARNING] Failed liquid parsing in loop (2): {e}") - - tmpitem = str(newvalue) - - try: - newvalue = json.loads(newvalue) - except json.decoder.JSONDecodeError as e: - pass - - # The list to use for the multi execution IF not a file list - new_replacement.append(newvalue) + tmpitem = parse_liquid(tmpitem, self) + except Exception as e: + self.logger.info(f"[WARNING] Failed liquid parsing in loop (2): {e}") - # FIXME: Should this use new_replacement? - tmpitem = tmpitem.replace(actualitem[index][0], replacement, 1) + # This code handles files. + isfile = False + try: + if parameter["schema"]["type"] == "file" and len(value) > 0: + self.logger.info("(2) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % parameter["value"]) - # This code handles files. - resultarray = [] - isfile = False - try: - if parameter["schema"]["type"] == "file" and len(value) > 0: - self.logger.info("(1) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % tmpitem) - # This is silly :) - # Q: Is there something wrong with the download system? - # It seems to return "FILE CONTENT: %s" with the ID as %s - for tmp_file_split in json.loads(tmpitem): - file_value = self.get_file(tmp_file_split) - resultarray.append(file_value) - - isfile = True - except NameError as e: - self.logger.info("(1) SCHEMA NAMEERROR IN FILE HANDLING: %s" % e) - except KeyError as e: - self.logger.info("(1) SCHEMA KEYERROR IN FILE HANDLING: %s" % e) - except json.decoder.JSONDecodeError as e: - self.logger.info("(1) JSON ERROR IN FILE HANDLING: %s" % e) - - if not isfile: - # Should be here in normal circumstances - params[parameter["name"]] = tmpitem - multi_parameters[parameter["name"]] = new_replacement - else: - params[parameter["name"]] = resultarray - multi_parameters[parameter["name"]] = resultarray - - #if len(resultarray) == 0: - # self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (1)") - # action_result["status"] = "SUCCESS" - # action_result["result"] = "[]" - # self.send_result(action_result, headers, stream_path) - # return - - multi_execution_lists.append(new_replacement) - #self.logger.info("MULTI finished: %s" % json_replacement) - else: - # This is here to handle for loops within variables.. kindof - # 1. Find the length of the longest array - # 2. Build an array with the base values based on parameter["value"] - # 3. Get the n'th value of the generated list from values - # 4. Execute all n answers - replacements = {} - curminlength = 0 - for replace in actualitem: - try: - to_be_replaced = replace[0] - actualitem = replace[2] - if actualitem.endswith("}$"): - actualitem = actualitem[:-2] - - except IndexError: - self.logger.info("[WARNING] Indexerror") - continue - - #self.logger.info(f"\n\nTMPITEM: {actualitem}\n\n") - #actualitem = parse_wrapper_start(actualitem) - #self.logger.info(f"\n\nTMPITEM2: {actualitem}\n\n") - - try: - itemlist = json.loads(actualitem) - if len(itemlist) > minlength: - minlength = len(itemlist) - - if len(itemlist) > curminlength: - curminlength = len(itemlist) - - except json.decoder.JSONDecodeError as e: - self.logger.info("JSON Error (replace): %s in %s" % (e, actualitem)) - - replacements[to_be_replaced] = actualitem + for tmp_file_split in json.loads(parameter["value"]): + file_value = self.get_file(tmp_file_split) + resultarray.append(file_value) - # Parses the data as string with length, split etc. before moving on. + isfile = True + except KeyError as e: + self.logger.info("(2) SCHEMA ERROR IN FILE HANDLING: %s" % e) + except json.decoder.JSONDecodeError as e: + self.logger.info("(2) JSON ERROR IN FILE HANDLING: %s" % e) + if not isfile: + tmpitem = tmpitem.replace("\\\\", "\\", -1) + resultarray.append(tmpitem) - #self.logger.info("In second part of else: %s" % (len(itemlist))) - # This is a result array for JUST this value.. - # What if there are more? - resultarray = [] - for i in range(0, curminlength): - tmpitem = json.loads(json.dumps(parameter["value"])) - for key, value in replacements.items(): - replacement = value - try: - replacement = json.dumps(json.loads(value)[i]) - except IndexError as e: - self.logger.info(f"[ERROR] Failed handling value parsing with index: {e}") - pass + # With this parameter ready, add it to... a greater list of parameters. Rofl + if len(resultarray) == 0: + self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (0)") + self.action_result["status"] = "SUCCESS" + self.action_result["result"] = "[]" + self.send_result(self.action_result, headers, stream_path) + return - if replacement.startswith("\"") and replacement.endswith("\""): - replacement = replacement[1:len(replacement)-1] - #except json.decoder.JSONDecodeError as e: + #self.logger.info("RESULTARRAY: %s" % resultarray) + if resultarray not in multi_execution_lists: + multi_execution_lists.append(resultarray) - #self.logger.info("REPLACING %s with %s" % (key, replacement)) - #replacement = parse_wrapper_start(replacement) - tmpitem = tmpitem.replace(key, replacement, -1) - try: - tmpitem = parse_liquid(tmpitem, self) - except Exception as e: - self.logger.info(f"[WARNING] Failed liquid parsing in loop (2): {e}") - - - # This code handles files. - isfile = False - try: - if parameter["schema"]["type"] == "file" and len(value) > 0: - self.logger.info("(2) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % parameter["value"]) - - for tmp_file_split in json.loads(parameter["value"]): - file_value = self.get_file(tmp_file_split) - resultarray.append(file_value) - - - isfile = True - except KeyError as e: - self.logger.info("(2) SCHEMA ERROR IN FILE HANDLING: %s" % e) - except json.decoder.JSONDecodeError as e: - self.logger.info("(2) JSON ERROR IN FILE HANDLING: %s" % e) - - if not isfile: - tmpitem = tmpitem.replace("\\\\", "\\", -1) - resultarray.append(tmpitem) - - # With this parameter ready, add it to... a greater list of parameters. Rofl - if len(resultarray) == 0: - self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (0)") - self.action_result["status"] = "SUCCESS" - self.action_result["result"] = "[]" - self.send_result(self.action_result, headers, stream_path) - return - - #self.logger.info("RESULTARRAY: %s" % resultarray) - if resultarray not in multi_execution_lists: - multi_execution_lists.append(resultarray) - - multi_parameters[parameter["name"]] = resultarray + multi_parameters[parameter["name"]] = resultarray else: # Parses things like int(value) #self.logger.info("[DEBUG] Normal parsing (not looping)")#with data %s" % value) @@ -3662,7 +3560,6 @@ class AppBase: self.logger.info("SCHEMA ERROR IN FILE HANDLING: %s" % e) - #remove_params.append(parameter["name"]) # Fix lists here # FIXME: This doesn't really do anything anymore #self.logger.info("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) @@ -3941,7 +3838,7 @@ class AppBase: # 2. Find the right value from the parsed multi_params #self.logger.info("[INFO] Running WITH loop. MULTI: %s", multi_parameters) - self.logger.info("[INFO] Running WITH loop") + self.logger.info("[INFO] Running WITH loop") json_object = False #results = await self.run_recursed_items(func, multi_parameters, {}) results = self.run_recursed_items(func, multi_parameters, {}) From d9d7611ccb9cc4827d1c5329d618bae100d4760f Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Mon, 29 Apr 2024 11:17:01 +0000 Subject: [PATCH 087/142] removing awful white background color --- frontend/src/views/Admin.jsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 327bfa55..a678bf3b 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -4630,7 +4630,6 @@ If you're interested, please let me know a time that works for you, or set up a style={{ textAlign: "center", padding: "20px", - backgroundColor: "#f0f0f0", color: "#666", borderRadius: "5px", }} @@ -4741,14 +4740,13 @@ If you're interested, please let me know a time that works for you, or set up a /> {webHooks === undefined || webHooks === null || webHooks.length === 0 ? (
    + style={{ + textAlign: "center", + padding: "20px", + color: "#666", + borderRadius: "5px", + }} + > No webhooks found.
    ) : ( @@ -4901,7 +4899,6 @@ If you're interested, please let me know a time that works for you, or set up a style={{ textAlign: "center", padding: "20px", - backgroundColor: "#f0f0f0", color: "#666", borderRadius: "5px", }} From 74e90a083e8745bbb5680d55710c08cbdf4f0a5d Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 29 Apr 2024 14:37:26 +0200 Subject: [PATCH 088/142] New 1.4.0 page view looks and bugfixes --- frontend/src/components/AppGrid.jsx | 2300 ++++++++++++++++++++---- frontend/src/components/Billing.jsx | 18 +- frontend/src/components/Branding.jsx | 4 +- frontend/src/components/CacheView.jsx | 60 +- frontend/src/components/Files.jsx | 30 +- frontend/src/components/Priorities.jsx | 4 +- frontend/src/components/Priority.jsx | 4 +- frontend/src/views/Search.jsx | 460 +++-- 8 files changed, 2302 insertions(+), 578 deletions(-) diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index 5bb08119..c75f2449 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -1,289 +1,1912 @@ -import React, {useEffect, useState} from 'react'; +import React, { useEffect, useState, useRef } from "react"; -import theme from '../theme.jsx'; -import ReactGA from 'react-ga4'; -import {Link} from 'react-router-dom'; -import { removeQuery } from '../components/ScrollToTop.jsx'; +import theme from "../theme.jsx"; +import ReactGA from "react-ga4"; +import { Link } from "react-router-dom"; +import { removeQuery } from "../components/ScrollToTop.jsx"; +import { useMemo } from "react"; -import { - Search as SearchIcon, - CloudQueue as CloudQueueIcon, - Code as CodeIcon -} from '@mui/icons-material'; +import { Tabs, Tab } from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ExpandLessIcon from "@mui/icons-material/ExpandLess"; +import { + Search as SearchIcon, + CloudQueue as CloudQueueIcon, + Code as CodeIcon, +} from "@mui/icons-material"; +import { toast } from "react-toastify" +import ClearIcon from '@mui/icons-material/Clear'; +import Box from '@mui/material/Box'; -import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights } from 'react-instantsearch-dom'; +import noImage from "../no_image.png" -import aa from 'search-insights' +import CircularProgress from '@mui/material/CircularProgress'; -import { - Zoom, - Grid, - Paper, - TextField, - ButtonBase, - InputAdornment, - Typography, - Button, - Tooltip -} from '@mui/material'; +import algoliasearch from "algoliasearch/lite"; +import { + InstantSearch, + Configure, + connectSearchBox, + connectHits, + connectHitInsights, + RefinementList, + ClearRefinements, + connectStateResults +} from "react-instantsearch-dom"; -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +import aa from "search-insights"; + +import "./FilterCSS.css"; + +import { + Zoom, + Grid, + Paper, + TextField, + ButtonBase, + InputAdornment, + Typography, + Button, + Tooltip, +} from "@mui/material"; + +const searchClient = algoliasearch( + "JNSS5CFDZZ", + "db08e40265e2941b9a7d8f644b6e5240" +); //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") -const AppGrid = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, isHeader } = props + +const AppGrid = (props) => { + const { + maxRows, + showName, + showSuggestion, + isMobile, + globalUrl, + parsedXs, + isHeader, + } = props; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows - const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs - //const [apps, setApps] = React.useState([]); - //const [filteredApps, setFilteredApps] = React.useState([]); - const [formMail, setFormMail] = React.useState(""); - const [message, setMessage] = React.useState(""); - const [formMessage, setFormMessage] = React.useState(""); + const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows; + const xs = + parsedXs === undefined || parsedXs === null ? (isMobile ? 6 : 3) : parsedXs; - const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} + const [formMail, setFormMail] = React.useState(""); + const [message, setMessage] = React.useState(""); + const [formMessage, setFormMessage] = React.useState(""); - const innerColor = "rgba(255,255,255,0.65)" - const borderRadius = 3 - window.title = "Shuffle | Apps | Find and integrate any app" + const buttonStyle = { + borderRadius: 30, + height: 50, + width: 220, + margin: isMobile ? "15px auto 15px auto" : 20, + fontSize: 18, + }; + const innerColor = "rgba(255,255,255,0.65)"; + const borderRadius = 3; + window.title = "Shuffle | Apps | Find and integrate any app"; - const submitContact = (email, message) => { - const data = { - "firstname": "", - "lastname": "", - "title": "", - "companyname": "", - "email": email, - "phone": "", - "message": message, - } - - const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." - fetch(globalUrl+"/api/v1/contact", { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }) - .then(response => response.json()) - .then(response => { - if (response.success === true) { - setFormMessage(response.reason) - //toast("Thanks for submitting!") - } else { - setFormMessage(errorMessage) - } + const submitContact = (email, message) => { + const data = { + firstname: "", + lastname: "", + title: "", + companyname: "", + email: email, + phone: "", + message: message, + }; - setFormMail("") - setMessage("") + const errorMessage = + "Something went wrong. Please contact frikky@shuffler.io directly."; + + fetch(globalUrl + "/api/v1/contact", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), }) - .catch(error => { - setFormMessage(errorMessage) - console.log(error) - }); - } + .then((response) => response.json()) + .then((response) => { + if (response.success === true) { + setFormMessage(response.reason); + //toast("Thanks for submitting!") + } else { + setFormMessage(errorMessage); + } - const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { - var defaultSearch = "" - //useEffect(() => { - if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { - const urlSearchParams = new URLSearchParams(window.location.search) - const params = Object.fromEntries(urlSearchParams.entries()) - const foundQuery = params["q"] - if (foundQuery !== null && foundQuery !== undefined) { - console.log("Got query: ", foundQuery) - refine(foundQuery) - defaultSearch = foundQuery - } - } - //}, []) + setFormMail(""); + setMessage(""); + }) + .catch((error) => { + setFormMessage(errorMessage); + console.log(error); + }); + }; - return ( -
    - - - - ), - }} - autoComplete='off' - type="search" - color="primary" - placeholder="Find Apps..." - id="shuffle_search_field" - onChange={(event) => { - // Remove "q" from URL - removeQuery("q") + const SearchBox = ({ currentRefinement, refine, isSearchStalled }) => { + var defaultSearch = ""; - refine(event.currentTarget.value) - }} - limit={5} - /> - {/*isSearchStalled ? 'My search is stalled' : ''*/} - - ) - } + var [searchQuery, setSearchQuery] = useState(""); - var workflowDelay = -50 - const Hits = ({ hits, insights }) => { - const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) - var counted = 0 + //useEffect(() => { + if ( + window !== undefined && + window.location !== undefined && + window.location.search !== undefined && + window.location.search !== null + ) { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundQuery = params["q"]; + if (foundQuery !== null && foundQuery !== undefined) { + console.log("Got query: ", foundQuery); + refine(foundQuery); + defaultSearch = foundQuery; + searchQuery = foundQuery + } + } + //}, []) - //console.log(hits) - //var curhits = hits - //if (hits.length > 0 && defaultApps.length === 0) { - // setDefaultApps(hits) - //} - //const [defaultApps, setDefaultApps] = React.useState([]) - //console.log(hits) - //if (hits.length > 0 && hits.length !== innerHits.length) { - // setInnerHits(hits) - //} + const handleSearch = () => { + refine(searchQuery.trim()); + }; - return ( - - {hits.map((data, index) => { + return ( +
    + + + + ), + endAdornment: ( + + {searchQuery.length > 0 && ( + { + setSearchQuery('') + removeQuery("q"); + refine('') + }} + /> + )} + + + ), - workflowDelay += 50 + }} + autoComplete="off" + color="primary" + placeholder="Find Apps" + id="shuffle_search_field" + onChange={(event) => { + setSearchQuery(event.currentTarget.value); + removeQuery("q"); + refine(event.currentTarget.value); + }} + limit={5} + /> + {/*isSearchStalled ? 'My search is stalled' : ''*/} + + ); + }; - const paperStyle = { - backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, - color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", - border: `1px solid ${innerColor}`, - padding: isHeader ? null : 15, - cursor: "pointer", - position: "relative", - minHeight: 116, - } - - if (counted === 12/xs*rowHandler) { - return null - } + const [currTab, setCurrTab] = useState(0); - counted += 1 - var parsedname = "" - for (var key = 0; key < data.name.length; key++) { - var character = data.name.charAt(key) - if (character === character.toUpperCase()) { - //console.log(data.name[key], data.name[key+1]) - if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) { - } else { - parsedname += " " - } - } + const handleTabChange = (event, newValue) => { + setCurrTab(newValue); + }; - parsedname += character - } - - parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ") - const appUrl = isCloud ? `/apps/${data.objectID}?queryID=${data.__queryID}` : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}` - return ( - - - - { - setMouseHoverIndex(index) - /* - ReactGA.event({ - category: "app_grid_view", - action: `search_bar_click`, - label: "", - }) - */ - }} onMouseOut={() => { - setMouseHoverIndex(-1) - }} onClick={() => { - if (isCloud) { - ReactGA.event({ - category: "app_grid_view", - action: `app_${parsedname}_${data.id}_click`, - label: "", - }) - } + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [userInfo, setUserInfo] = useState([]); - //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") - console.log(searchClient) - aa('init', { - appId: searchClient.appId, - apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"] - }) + useEffect(() => { + var baseurl = globalUrl; + fetch(baseurl + "/api/v1/getinfo", { + credentials: "include", + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(response => response.json()) + .then(responseJson => { + if (responseJson.success) { + setIsLoggedIn(true); + setUserInfo(responseJson); + } + }) + .catch(error => { + console.log("Failed login check: ", error); + }); + }, []); - const timestamp = new Date().getTime() - aa('sendEvents', [ - { - eventType: 'click', - eventName: 'Product Clicked', - index: 'appsearch', - objectIDs: [data.objectID], - timestamp: timestamp, - queryID: data.__queryID, - positions: [data.__position], - userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id, - } - ]) + //Component to fetch all app from the algolia + const Hits = ({ + hits, + insights, + setIsAnyAppActivated + }) => { + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); + var counted = 0; + const [hoverEffect, setHoverEffect] = useState(-1); - }}> - - {data.name} - -
    - {index === mouseHoverIndex || showName === true ? - parsedname - : - null - } - {data.generated ? - - {data.invalid ? - - : - - } - - : - - - - } - - - - - ) - })} - - ) - } + const normalizedString = (name) => { + if (typeof name === 'string') { + return name.replace(/_/g, ' '); + } else { + return name; + } + }; - const CustomSearchBox = connectSearchBox(SearchBox) - const CustomHits = connectHits(Hits) - //const CustomHits = connectHitInsights(aa)(Hits) - const selectButtonStyle = { - minWidth: 150, - maxWidth: 150, - minHeight: 50, - } + const [allActivatedAppIds, setAllActivatedAppIds] = useState(() => { + const storedApps = isLoggedIn && localStorage.getItem('allActivatedAppIds'); + return storedApps ? JSON.parse(storedApps) : userInfo.active_apps; + }); + const [isAppActivated, setIsAppActivated] = useState(false); + const [isActivateAppSuccess, setIsActivateAppSuccess] = useState(false); - return ( -
    - {/* + //Function for activation and deactivation of app + const handleActivateButton = (event, data, type) => { + event.preventDefault(); + if (!isLoggedIn) { + toast.error("Please log in to your account to activate the app.") + return; + } + if (type === "activate") { + toast.success(`The ${normalizedString(data.name)} app is activating. Please wait...`); + } + if (type === "deactivate") { + toast.success(`The ${normalizedString(data.name)} app is deactivating. Please wait...`); + } + + const baseURL = globalUrl; + const url = `${baseURL}/api/v1/apps/${data.objectID}/${type}`; + + fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + credentials: "include", + }) + .then((response) => response.json()) + .then((responseJson) => { + if (responseJson.success === false) { + toast.error(responseJson.reason); + } else { + toast.success(`App ${type}d Successfully!`); + if (type === 'activate') { + setAllActivatedAppIds(prev => [...prev, data.objectID]); + setIsAnyAppActivated(true); + } + if (type === 'deactivate') { + const updatedIds = allActivatedAppIds.filter(id => id !== data.objectID); + setAllActivatedAppIds(updatedIds); + } + setIsActivateAppSuccess(prev => !prev); + } + }) + .catch(error => { + console.log("app error: ", error.toString()); + }); + } + + useEffect(() => { + isLoggedIn && localStorage.setItem('allActivatedAppIds', JSON.stringify(allActivatedAppIds)); + }, [allActivatedAppIds]); + + + const memoizedHits = useMemo(() => { + return hits.map((data, index) => { + let workflowDelay = 0; + const isHeader = true; + const paperStyle = { + color: "rgba(241, 241, 241, 1)", + padding: isHeader ? null : 15, + cursor: "pointer", + maxWidth: 339, + maxHeight: 96, + borderRadius: 8, + transition: 'background-color 0.3s ease', + backgroundColor: "rgba(26, 26, 26, 1)", + }; + + const appUrl = + isCloud + ? `/apps/${data.objectID}?queryID=${data.__queryID}` + : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; + + //check if appExist in userInfo.active_app or not. + return ( + + + + { + setMouseHoverIndex(index); + }} + onMouseOut={() => { + setMouseHoverIndex(-1); + }} + > + + ) : ( + + )} +
    + )} +
    +
    +
    + + + + + + ); + }); + }, [hits, mouseHoverIndex, isActivateAppSuccess, allActivatedAppIds]); + + return ( + +
    + {memoizedHits} +
    +
    + ); + }; + + var workflowDelay = -50; + + const CustomClearRefinements = connectStateResults(({ searchResults, ...rest }) => { + const hasFilters = searchResults && searchResults.nbHits !== searchResults.nbSortedHits; + return Clear All }} {...rest} disabled={!hasFilters} />; + }); + + //Component to Filter all apps base on category + const FilterAllAppsByCategory = () => { + const [isRefinementListExpanded, setIsRefinementListExpanded] = + useState(true); + + const toggleRefinementList = () => { + setIsRefinementListExpanded((prevState) => !prevState); + }; + + const categoryButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + width: "100%", + height: 30, + flexDirection: "row", + textTransform: 'none', + fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" + } + + return ( +
    + + + {isRefinementListExpanded && ( + <> + + + + )} +
    + ); + }; + + //Component to filter all apps base on Action label + const FilterByActionLabel = () => { + const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); + useState(false); + + const toogleActionLabel = () => { + setIsActionLabelExpanded((prevState) => !prevState); + }; + + const actionLabelButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + flexDirection: "row", + width: "100%", + height: 30, + textTransform: 'none', + fontWeight: 400 + } + + + return ( +
    + + + {isActionLabelExpanded && ( + <> + + + + )} +
    + ); + }; + + //component to filter all apps base on the created with like 'App Editor' or 'Python' + const FilterByCreatedWith = () => { + const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false); + + const toogleCreatedWith = () => { + setIsCreatedWithExpanded((prevState) => !prevState); + }; + + const transformRefinementListItems = items => + items.map(item => ({ + ...item, + label: item.label === 'true' ? 'App Editor' : 'Python', + })); + + const createdWithButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + flexDirection: "row", + alignItems: "center", + width: "100%", + height: 30, + textTransform: 'none', + fontWeight: 400 + } + + return ( +
    + + + {isCreatedWithExpanded && ( + <> + + + + )} +
    + ); + }; + + const FilterCreatedBy = () => { + const [isCreatedByExpanded, setIscreatedByExpanded] = useState(false); + useState(false); + + const toogleCreatedBy = () => { + setIscreatedByExpanded((prevState) => !prevState); + }; + + const createdByButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + flexDirection: "row", + alignItems: "center", + whiteSpace: "nowrap", + width: "100%", + height: 30, + textTransform: 'none', + opacity: '0.5' + } + + return ( +
    + + + {isCreatedByExpanded && ( + <> + {/* */} + + {/* */} + + + )} +
    + ); + }; + + const FilterApps = () => { + return ( +
    + + Filter By + + + + + +
    + ); + }; + + + + const boxStyle = { + color: "white", + flex: "1", + marginLeft: isHeader ? null : 10, + marginRight: isHeader ? null : 10, + paddingLeft: isHeader ? null : 30, + paddingRight: isHeader ? null : 30, + paddingBottom: isHeader ? null : 30, + display: "flex", + flexDirection: "column", + overflowX: "visible", + backgroundColor: "rgba(33, 33, 33, 1)", + borderRadius: 16, + marginTop: 24, + width: 741, + height: 741, + }; + + + //Component to display all apps. + const AllApps = ({ setIsAnyAppActivated }) => { + + return ( +
    + + +
    + ); + }; + + //Search box for the orgs and users apps + const SearchBoxForOrgsAndUsersApp = ({ searchQuery, setSearchQuery }) => { + + return ( +
    + + + + ), + endAdornment: ( + + {searchQuery.length > 0 && ( + setSearchQuery('')} + /> + )} + + + ), + }} + autoComplete="off" + color="primary" + placeholder="Find Apps" + id="shuffle_search_field" + onChange={(event) => { + setSearchQuery(event.currentTarget.value); + }} + limit={5} + /> + {/*isSearchStalled ? 'My search is stalled' : ''*/} + + ) + } + + + + const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]); + const [selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps] = useState([]); + const [isCategoreListExpanded, setIsCategoryListExpanded] = useState(true); + + const toogleCategoryList = () => { + setIsCategoryListExpanded((prevState) => !prevState); + }; + + //Component to display category List for User and Orgs app + const FilterUsersAndOrgsAppByCategory = ({ userAndOrgsApp }) => { + + //Display top 9 category from the database + + const findTopCategories = () => { + const categoryCountMap = {}; + + // Check if userAndOrgsApp is an array before iterating over it and Find top 10 Category from the apps + if (Array.isArray(userAndOrgsApp)) { + userAndOrgsApp.forEach((app) => { + const categories = app.categories; + + if (categories && categories.length > 0) { + categories.forEach((category) => { + categoryCountMap[category] = (categoryCountMap[category] || 0) + 1; + }); + } + }); + + const categoryArray = Object.keys(categoryCountMap).map((category) => ({ + category, + count: categoryCountMap[category], + })); + + categoryArray.sort((a, b) => b.count - a.count); + + const topCategories = categoryArray.slice(0, 9); + + return topCategories; + } + }; + + const topCategories = findTopCategories(); + + const handleCheckboxChange = (category) => { + if (selectedCategoryForUsersAndOgsApps.includes(category)) { + setselectedCategoryForUsersAndOgsApps(selectedCategoryForUsersAndOgsApps.filter((item) => item !== category)); + } else { + setselectedCategoryForUsersAndOgsApps([...selectedCategoryForUsersAndOgsApps, category]); + } + }; + + const handleClearFilter = () => { + setselectedCategoryForUsersAndOgsApps([]); + }; + + const categorysButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + width: "100%", + height: 30, + flexDirection: "row", + textTransform: 'none', + fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" + } + return ( +
    + + {isCategoreListExpanded && topCategories && topCategories.length > 0 && ( +
    + {topCategories.map((data, index) => ( + + + ))} + + +
    + )} +
    + ); + }; + + const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); + const toogleActionLabel = () => { + setIsActionLabelExpanded((prevState) => !prevState); + }; + + const FilterUsersAndOrgsAppByActionLabel = ({ userAndOrgsApp }) => { + + const findTopTags = () => { + const tagCountMap = {}; + + // Check if userAndOrgsApp is an array before iterating over it and Find top 10 tags from the apps + if (Array.isArray(userAndOrgsApp)) { + userAndOrgsApp.forEach((app) => { + const tags = app.tags; + + if (tags && tags.length > 0) { + tags.forEach((tag) => { + tagCountMap[tag] = (tagCountMap[tag] || 0) + 1; + }); + } + }); + } + + const tagArray = Object.keys(tagCountMap).map((tag) => ({ + tag, + count: tagCountMap[tag], + })); + + tagArray.sort((a, b) => b.count - a.count); + + const topTags = tagArray.slice(0, 9); + + return topTags; + }; + + const topTags = findTopTags(); + const [selectedCategories, setSelectedCategories] = useState([]); + + const handleCheckboxChange = (index) => { + const category = topTags[index].tag; + const updatedCheckboxStates = [...selectedTagsForUserAndOrgApps]; + + if (updatedCheckboxStates.includes(category)) { + setSelectedTagsForUserAndOrgApps(updatedCheckboxStates.filter((item) => item !== category)); + } else { + setSelectedTagsForUserAndOrgApps([...updatedCheckboxStates, category]); + } + }; + + const handleClearFilter = () => { + setSelectedTagsForUserAndOrgApps([]); + }; + + const actionLabelButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + flexDirection: "row", + width: "100%", + height: 30, + textTransform: 'none', + marginBottom: isActionLabelExpanded && 16, + fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" + } + + return ( +
    + + + {isActionLabelExpanded && topTags && topTags.length > 0 && ( + <> + {topTags.map((data, index) => ( + + + ))} + + + + )} +
    + ); + }; + + const [selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith] = useState([]); + const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false); + + const toogleCreatedWith = () => { + setIsCreatedWithExpanded((prevState) => !prevState); + }; + const FilterUsersAndOrgsAppByCreatedWith = () => { + + const AppCreatedWithOptions = ['App Editor', 'Python'] + + const handleCheckboxChange = (index) => { + const category = AppCreatedWithOptions[index]; + const updatedCheckboxStates = [...selectedOptionOfCreatedWith]; + if (updatedCheckboxStates.includes(category)) { + setSelectedOptionOfCreatedWith(updatedCheckboxStates.filter((item) => item !== category)); + } else { + setSelectedOptionOfCreatedWith([...updatedCheckboxStates, category]); + } + }; + + + const handleClearFilter = () => { + setSelectedOptionOfCreatedWith([]); + }; + + const createdWithButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: "transparent", + fontSize: 16, + display: "flex", + flexDirection: "row", + alignItems: "center", + width: "100%", + height: 30, + textTransform: 'none', + marginBottom: isCreatedWithExpanded && 16, + fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" + } + + return ( +
    + + + {isCreatedWithExpanded && ( + <> + {AppCreatedWithOptions.map((data, index) => ( + + + ))} + + + + )} +
    + ); + }; + + const FilterUsersAndOrgsAppCreatedBy = () => { + + const [isCreatedByExpanded, setIscreatedByExpanded] = useState(false); + useState(false); + + const toogleCreatedBy = () => { + setIscreatedByExpanded((prevState) => !prevState); + }; + const [isButtonDisable, setIsButtonDisable] = useState(true) + + const createdByButtonStyling = { + cursor: "pointer", + color: "white", + border: "none", + backgroundColor: isButtonDisable ? '#3c3c3c. ' : "transparent", + fontSize: 16, + display: "flex", + flexDirection: "row", + alignItems: "center", + whiteSpace: "nowrap", + width: "100%", + height: 30, + textTransform: 'none', + opacity: '0.5' + } + + return ( +
    + + + {isCreatedByExpanded && ( + <> + + + + )} +
    + ); + }; + + const FilterUserAndOrgApps = () => { + + const [userAndOrgsApp, setUserAndOrgsApp] = useState([]); + + useEffect(() => { + if (currTab === 2) { + const baseUrl = globalUrl; + const userAppsUrl = `${baseUrl}/api/v1/users/apps`; + fetch(userAppsUrl, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => response.json()) + .then((data) => { + setUserAndOrgsApp(data); + }) + .catch((err) => { + console.error("Error fetching user apps:", err); + }); + } else if (currTab === 1) { + const baseUrl = globalUrl; + const appsUrl = `${baseUrl}/api/v1/apps`; + fetch(appsUrl, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => response.json()) + .then((data) => { + setUserAndOrgsApp(data); + }) + .catch((err) => { + console.error("Error fetching apps:", err); + }); + } + }, [currTab]); + + + return ( +
    + {isLoggedIn === true && ( +
    + + Filter By + + + + + +
    + )} +
    + ) + } + const [isLoading, setIsLoading] = useState(false) + useEffect(() => { + if (currTab) { + setselectedCategoryForUsersAndOgsApps([]); + setSelectedTagsForUserAndOrgApps([]); + setSelectedOptionOfCreatedWith([]); + setIsCategoryListExpanded(true); + setIsActionLabelExpanded(false); + setIsCreatedWithExpanded(false); + } + if (currTab === 1 || currTab === 2) { + setIsLoading(true); + } + + }, [currTab]) + + //Component to fetch all apps created by user and Org + const UserAndOrgApps = () => { + + const [searchQuery, setSearchQuery] = useState(""); + const [userAndOrgAppData, setUserAndOrgAppData] = useState([]) + + const allActivatedAppIdsString = localStorage.getItem('allActivatedAppIds'); + const allActivatedAppIds = allActivatedAppIdsString ? JSON.parse(allActivatedAppIdsString) : []; + const latestActivatedAppId = allActivatedAppIds.length > 0 ? allActivatedAppIds[allActivatedAppIds.length - 1] : null; + + useEffect(() => { + if (currTab === 2 && isLoggedIn != undefined && isLoggedIn != null && isLoggedIn === true) { + const baseUrl = globalUrl; + const URL = `${baseUrl}/api/v1/users/apps`; + fetch(URL, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + return response.json(); + }) + .then((data) => { + setUserAndOrgAppData(data) + setIsLoading(false) + }) + .catch((err) => { + console.error("Error fetching user apps:", err); + }); + } + else if (currTab === 1 && isLoggedIn != undefined && isLoggedIn != null && isLoggedIn === true) { + const baseUrl = globalUrl; + const URL = `${baseUrl}/api/v1/apps`; + fetch(URL, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + return response.json(); + }) + .then((data) => { + setUserAndOrgAppData(data) + setIsLoading(false); + }) + .catch((err) => { + console.error("Error fetching user apps:", err); + }); + } + }, []) + + //Search app base on app name, category and tag + const filteredUserAppdata = Array.isArray(userAndOrgAppData) ? userAndOrgAppData.filter((app) => { + const matchesSearchQuery = ( + searchQuery === "" || + app.name.toLowerCase().includes(searchQuery.toLowerCase()) || + (app.tags && app.tags.some(tag => + tag.toLowerCase().includes(searchQuery.toLowerCase()) + )) || + (app.categories && app.categories.some((category) => + category.toLowerCase().includes(searchQuery.toLowerCase()) + )) + ); + + const matchesSelectedCategories = ( + selectedCategoryForUsersAndOgsApps.length === 0 || + (app.categories && app.categories.some(category => + selectedCategoryForUsersAndOgsApps.includes(category) + )) + ); + const matchesSelectedTags = ( + selectedTagsForUserAndOrgApps.length === 0 || + (app.tags && selectedTagsForUserAndOrgApps.some(tag => + app.tags.includes(tag) + )) + ); + + const matchesSelectedOption = ( + selectedOptionOfCreatedWith.length === 0 || + selectedOptionOfCreatedWith.includes('App Editor') && app.generated === true || + selectedOptionOfCreatedWith.includes('Python') && app.generated === false + ); + + return matchesSearchQuery && matchesSelectedCategories && matchesSelectedTags && matchesSelectedOption; + }) : []; + + + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); + var counted = 0; + + const memoizedHits = useMemo(() => { + return filteredUserAppdata.map((data, index) => { + const isMouseOverOnCloudIcon = false; + const xs = 12; + const rowHandler = 12; + const searchClient = {}; + const userdata = {}; + + const paperStyle = { + backgroundColor: "#1A1A1A", + color: "rgba(241, 241, 241, 1)", + padding: isHeader ? null : 15, + cursor: "pointer", + position: "relative", + width: 339, + height: 96, + borderRadius: 8, + }; + + var parsedname = ""; + for (var key = 0; key < data.name.length; key++) { + var character = data.name.charAt(key); + if (character === character.toUpperCase()) { + if ( + data.name.charAt(key + 1) !== undefined && + data.name.charAt(key + 1) === + data.name.charAt(key + 1).toUpperCase() + ) { + } else { + parsedname += " "; + } + } + parsedname += character; + } + + parsedname = ( + parsedname.charAt(0).toUpperCase() + parsedname.substring(1) + ).replaceAll("_", " "); + + const normalizedString = (name) => { + if (typeof name === 'string') { + return name.replace(/_/g, ' '); + } else { + return name; + } + }; + + const appUrl = + isCloud === false + ? `/apps/${data.id}` + : `https://shuffler.io/apps/${data.id}`; + + return ( + + + + { + setMouseHoverIndex(index); + }} + onMouseOut={() => { + setMouseHoverIndex(-1); + }} + > + + {data.name} +
    +
    + {normalizedString(data.name)} +
    +
    + {data.categories !== null + ? normalizedString(data.categories).join(", ") + : "NA"} +
    +
    + {data.tags && + data.tags.map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))} +
    + {/* )} */} +
    +
    +
    +
    +
    +
    + ); + }); + }, [filteredUserAppdata, latestActivatedAppId]); + + + return ( +
    + {isLoggedIn ? ( +
    + {isLoading ? : ( +
    + + +
    + {memoizedHits} +
    +
    +
    + )} +
    + ) : ( +
    + Please login to your account first to view {`${currTab === 1 ? "Organization" : "My"}`} Apps.
    + Or signup to create a new account.
    +
    + )} +
    + ); + }; + + + const AppTab = () => { + + const [isAnyAppActivated, setIsAnyAppActivated] = useState(false); + + return ( +
    +
    + + + {isAnyAppActivated && }Organization Apps
    + sx={{ + color: currTab === 1 ? "#F86743" : "inherit", + border: 'none', + height: 44, + fontSize: 16, + flex: 1, + textTransform: 'none', + fontWeight: 400, + paddingBottom: 3, + fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", + }} + > + + + + {currTab === 0 ? ( + + ) : currTab === 1 || currTab === 2 ? ( + + ) : null} +
    +
    + ); + }; + + const CustomSearchBox = connectSearchBox(SearchBox); + const CustomHits = connectHits(Hits); + return ( +
    + {/*
    */} -
    - -
    - -
    - - -
    - {showSuggestion === true ? -
    - - Can't find what you're looking for? - -
    - setFormMail(e.target.value)} - /> - setMessage(e.target.value)} - /> -
    - - {formMessage} -
    - : null - } - - - - Search by - - - Algolia logo - - -
    -
    - ) -} +
    + +
    + {currTab === 0 ? : } + +
    + {/* */} + +
    + {showSuggestion === true ? ( +
    + + Can't find what you're looking for? + +
    + setFormMail(e.target.value)} + /> + setMessage(e.target.value)} + /> +
    + + + {formMessage} + +
    + ) : null} +
    +
    + ); +}; export default AppGrid; diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index d3a65927..8602911c 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -43,7 +43,7 @@ import BillingStats from "../components/BillingStats.jsx"; import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" const Billing = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, clickedFromOrgTab } = props; //const alert = useAlert(); let navigate = useNavigate(); @@ -970,21 +970,29 @@ const Billing = (props) => { const isChildOrg = userdata.active_org.creator_org !== "" && userdata.active_org.creator_org !== undefined && userdata.active_org.creator_org !== null return ( -
    +
    {addDealModal} + {clickedFromOrgTab? +

    Billing & Licensing

    : Billing & Licensing - + } + {clickedFromOrgTab? + {isCloud ? + "Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below." + : + "Shuffle is an Open Source automation platform, and no license is required. We do however offer a Scale license with HA guarantees, along with support hours. By buying a license on https://shuffler.io, you can get access to the license immediately, and if Cloud Syncronisation is enabled, the UI in your local instance will also update." + }: {isCloud ? "Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below." : "Shuffle is an Open Source automation platform, and no license is required. We do however offer a Scale license with HA guarantees, along with support hours. By buying a license on https://shuffler.io, you can get access to the license immediately, and if Cloud Syncronisation is enabled, the UI in your local instance will also update." } - + } {userdata.support === true ? -
    +
    For sales: Create  New Cloud Contract diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index c3c32058..48466476 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -103,8 +103,8 @@ const Branding = (props) => { } return ( -
    -

    +
    +

    Branding

    diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 4e4f2fdd..22500fe2 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -115,43 +115,6 @@ const CacheView = (props) => { }); }; - // const getCacheList = (orgId) => { - // fetch(`${globalUrl}/api/v1/orgs/${orgId}/get_cache`, { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // Accept: "application/json", - // }, - // credentials: "include", - // }) - // .then((response) => { - // if (response.status !== 200) { - // console.log("Status not 200 for WORKFLOW EXECUTION :O!"); - // } - - - // return response.json(); - // }) - // .then((responseJson) => { - // if (responseJson.success !== false) { - // console.log("Found cache: ", responseJson) - // setListCache(responseJson) - // } else { - // console.log("Couldn't find the creator profile (rerun?): ", responseJson) - // // If the current user is any of the Shuffle Creators - // // AND the workflow doesn't have an owner: allow editing. - // // else: Allow suggestions? - // //console.log("User: ", userdata) - // //if (rerun !== true) { - // // getUserProfile(userdata.id, true) - // //} - // } - // }) - // .catch((error) => { - // console.log("Get userprofile error: ", error); - // }) - // } - const deleteCache = (orgId, key) => { toast("Attempting to delete Cache"); @@ -403,7 +366,7 @@ const CacheView = (props) => {
    @@ -679,7 +679,7 @@ const Files = (props) => { }} /> {priority.active === true ? -
    - ) : ( - - )} -

    - )} -
    -
    -
    - - - - - - ); - }); - }, [hits, mouseHoverIndex, isActivateAppSuccess, allActivatedAppIds]); + let workflowDelay = 0; + const isHeader = true; + const paperStyle = { + color: "rgba(241, 241, 241, 1)", + padding: isHeader ? null : 15, + cursor: "pointer", + maxWidth: 339, + maxHeight: 96, + borderRadius: 8, + transition: 'background-color 0.3s ease', + }; return ( - -
    - {memoizedHits} -
    -
    +
    + {!isLoading ? ( + +
    + {hits.map((data, index) => { + const appUrl = + isCloud + ? `/apps/${data.objectID}?queryID=${data.__queryID}` + : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; + + return ( + + + + { + setMouseHoverIndex(index); + }} + onMouseLeave={() => { + setMouseHoverIndex(-1); + }} + > + + ) : ( + + )} +
    + )} +
    +
    +
    + + + + + + ); + }) + } +
    + + ) : ( +
    + )} +
    ); }; @@ -591,11 +590,10 @@ const AppGrid = (props) => { //Component to Filter all apps base on category const FilterAllAppsByCategory = () => { - const [isRefinementListExpanded, setIsRefinementListExpanded] = - useState(true); + const [isCategoreListExpanded, setIsCategoreListExpanded] = useState(true); const toggleRefinementList = () => { - setIsRefinementListExpanded((prevState) => !prevState); + setIsCategoreListExpanded((prevState) => !prevState); }; const categoryButtonStyling = { @@ -627,27 +625,27 @@ const AppGrid = (props) => { onClick={toggleRefinementList} > Category - {isRefinementListExpanded ? ( + {isCategoreListExpanded ? ( ) : ( )} - {isRefinementListExpanded && ( - <> + +
    - - )} +
    +
    ); }; //Component to filter all apps base on Action label const FilterByActionLabel = () => { + const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); - useState(false); const toogleActionLabel = () => { setIsActionLabelExpanded((prevState) => !prevState); @@ -667,7 +665,6 @@ const AppGrid = (props) => { fontWeight: 400 } - return (
    { )} - {isActionLabelExpanded && ( - <> + +
    - - )} +
    +
    ); }; @@ -747,12 +744,12 @@ const AppGrid = (props) => { {isCreatedWithExpanded ? : } - {isCreatedWithExpanded && ( - <> + +
    - - )} +
    +
    ); }; @@ -826,7 +823,7 @@ const AppGrid = (props) => { ); }; - const FilterApps = () => { + const FilterForAllApps = () => { return (
    { }; //Search box for the orgs and users apps - const SearchBoxForOrgsAndUsersApp = ({ searchQuery, setSearchQuery }) => { + const SearchBoxForOrgAndUserApp = ({ searchQuery, setSearchQuery }) => { + + const updateUrl = (query) => { + const urlSearchParams = new URLSearchParams(window.location.search); + urlSearchParams.set("q", query); + const newUrl = `${window.location.pathname}?${urlSearchParams.toString()}`; + window.history.pushState({ path: newUrl }, "", newUrl); + }; return (
    @@ -928,7 +932,10 @@ const AppGrid = (props) => { cursor: "pointer", marginRight: 10 }} - onClick={() => setSearchQuery('')} + onClick={() => { + setSearchQuery(''); + updateUrl(''); + }} /> )} - {isCategoreListExpanded && topCategories && topCategories.length > 0 && ( -
    + +
    {topCategories.map((data, index) => ( - - + ))}
    - )} +
    ); }; - const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); - const toogleActionLabel = () => { - setIsActionLabelExpanded((prevState) => !prevState); - }; + const FilterUsersAndOrgsAppByActionLabel = ({ selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps }) => { - const FilterUsersAndOrgsAppByActionLabel = ({ userAndOrgsApp }) => { + const [isActionLabelExpanded, setIsActionLabelExpanded] = useState(false); + const toggleActionLabel = () => { + setIsActionLabelExpanded((prevState) => !prevState); + }; + //Find top 9 tags from the database const findTopTags = () => { const tagCountMap = {}; - // Check if userAndOrgsApp is an array before iterating over it and Find top 10 tags from the apps if (Array.isArray(userAndOrgsApp)) { userAndOrgsApp.forEach((app) => { const tags = app.tags; - if (tags && tags.length > 0) { tags.forEach((tag) => { tagCountMap[tag] = (tagCountMap[tag] || 0) + 1; @@ -1136,13 +1180,13 @@ const AppGrid = (props) => { tagArray.sort((a, b) => b.count - a.count); - const topTags = tagArray.slice(0, 9); + const topTags = tagArray.slice(0, 8); return topTags; }; + const topTags = findTopTags(); - const [selectedCategories, setSelectedCategories] = useState([]); const handleCheckboxChange = (index) => { const category = topTags[index].tag; @@ -1154,108 +1198,98 @@ const AppGrid = (props) => { setSelectedTagsForUserAndOrgApps([...updatedCheckboxStates, category]); } }; - const handleClearFilter = () => { setSelectedTagsForUserAndOrgApps([]); }; const actionLabelButtonStyling = { - cursor: "pointer", - color: "white", - border: "none", - backgroundColor: "transparent", + cursor: 'pointer', + color: 'white', + border: 'none', + backgroundColor: 'transparent', fontSize: 16, - display: "flex", - flexDirection: "row", - width: "100%", + display: 'flex', + width: '100%', height: 30, + flexDirection: 'row', textTransform: 'none', - marginBottom: isActionLabelExpanded && 16, - fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" - } + fontFamily: 'var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)', + marginBottom: isActionLabelExpanded && 15 + }; return (
    - - - {isActionLabelExpanded && topTags && topTags.length > 0 && ( - <> - {topTags.map((data, index) => ( + +
    + {topTags && topTags.length > 0 && topTags.map((data, index) => ( - ))} - - - )} +
    +
    ); }; - const [selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith] = useState([]); - const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false); - const toogleCreatedWith = () => { - setIsCreatedWithExpanded((prevState) => !prevState); - }; - const FilterUsersAndOrgsAppByCreatedWith = () => { + + + const FilterUsersAndOrgsAppByCreatedWith = ({ selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith }) => { + + const [isCreatedWithExpanded, setIsCreatedWithExpanded] = useState(false); + + const toogleCreatedWith = () => { + setIsCreatedWithExpanded((prevState) => !prevState); + }; const AppCreatedWithOptions = ['App Editor', 'Python'] @@ -1309,8 +1343,8 @@ const AppGrid = (props) => { {isCreatedWithExpanded ? : } - {isCreatedWithExpanded && ( - <> + +
    {AppCreatedWithOptions.map((data, index) => ( - - )} +
    +
    ); }; @@ -1370,6 +1406,7 @@ const AppGrid = (props) => { const toogleCreatedBy = () => { setIscreatedByExpanded((prevState) => !prevState); }; + const [isButtonDisable, setIsButtonDisable] = useState(true) const createdByButtonStyling = { @@ -1434,48 +1471,7 @@ const AppGrid = (props) => { ); }; - const FilterUserAndOrgApps = () => { - - const [userAndOrgsApp, setUserAndOrgsApp] = useState([]); - - useEffect(() => { - if (currTab === 2) { - const baseUrl = globalUrl; - const userAppsUrl = `${baseUrl}/api/v1/users/apps`; - fetch(userAppsUrl, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => response.json()) - .then((data) => { - setUserAndOrgsApp(data); - }) - .catch((err) => { - console.error("Error fetching user apps:", err); - }); - } else if (currTab === 1) { - const baseUrl = globalUrl; - const appsUrl = `${baseUrl}/api/v1/apps`; - fetch(appsUrl, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => response.json()) - .then((data) => { - setUserAndOrgsApp(data); - }) - .catch((err) => { - console.error("Error fetching apps:", err); - }); - } - }, [currTab]); - + const FilterForUserAndOrgApps = ({ setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith, selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith }) => { return (
    { Filter By - - - + + +
    )}
    ) } - const [isLoading, setIsLoading] = useState(false) + useEffect(() => { - if (currTab) { - setselectedCategoryForUsersAndOgsApps([]); - setSelectedTagsForUserAndOrgApps([]); - setSelectedOptionOfCreatedWith([]); - setIsCategoryListExpanded(true); - setIsActionLabelExpanded(false); - setIsCreatedWithExpanded(false); - } if (currTab === 1 || currTab === 2) { setIsLoading(true); } - + if (currTab) { + setUserAndOrgsApp([]) + } }, [currTab]) //Component to fetch all apps created by user and Org - const UserAndOrgApps = () => { - + const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith }) => { const [searchQuery, setSearchQuery] = useState(""); - const [userAndOrgAppData, setUserAndOrgAppData] = useState([]) - - const allActivatedAppIdsString = localStorage.getItem('allActivatedAppIds'); - const allActivatedAppIds = allActivatedAppIdsString ? JSON.parse(allActivatedAppIdsString) : []; - const latestActivatedAppId = allActivatedAppIds.length > 0 ? allActivatedAppIds[allActivatedAppIds.length - 1] : null; - - useEffect(() => { - if (currTab === 2 && isLoggedIn != undefined && isLoggedIn != null && isLoggedIn === true) { - const baseUrl = globalUrl; - const URL = `${baseUrl}/api/v1/users/apps`; - fetch(URL, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => { - return response.json(); - }) - .then((data) => { - setUserAndOrgAppData(data) - setIsLoading(false) - }) - .catch((err) => { - console.error("Error fetching user apps:", err); - }); - } - else if (currTab === 1 && isLoggedIn != undefined && isLoggedIn != null && isLoggedIn === true) { - const baseUrl = globalUrl; - const URL = `${baseUrl}/api/v1/apps`; - fetch(URL, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => { - return response.json(); - }) - .then((data) => { - setUserAndOrgAppData(data) - setIsLoading(false); - }) - .catch((err) => { - console.error("Error fetching user apps:", err); - }); - } - }, []) //Search app base on app name, category and tag - const filteredUserAppdata = Array.isArray(userAndOrgAppData) ? userAndOrgAppData.filter((app) => { + const filteredUserAppdata = Array.isArray(userAndOrgsApp) ? userAndOrgsApp.filter((app) => { const matchesSearchQuery = ( searchQuery === "" || app.name.toLowerCase().includes(searchQuery.toLowerCase()) || @@ -1614,214 +1553,213 @@ const AppGrid = (props) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; - const memoizedHits = useMemo(() => { - return filteredUserAppdata.map((data, index) => { - const isMouseOverOnCloudIcon = false; - const xs = 12; - const rowHandler = 12; - const searchClient = {}; - const userdata = {}; - - const paperStyle = { - backgroundColor: "#1A1A1A", - color: "rgba(241, 241, 241, 1)", - padding: isHeader ? null : 15, - cursor: "pointer", - position: "relative", - width: 339, - height: 96, - borderRadius: 8, - }; - - var parsedname = ""; - for (var key = 0; key < data.name.length; key++) { - var character = data.name.charAt(key); - if (character === character.toUpperCase()) { - if ( - data.name.charAt(key + 1) !== undefined && - data.name.charAt(key + 1) === - data.name.charAt(key + 1).toUpperCase() - ) { - } else { - parsedname += " "; - } - } - parsedname += character; - } - - parsedname = ( - parsedname.charAt(0).toUpperCase() + parsedname.substring(1) - ).replaceAll("_", " "); - - const normalizedString = (name) => { - if (typeof name === 'string') { - return name.replace(/_/g, ' '); - } else { - return name; - } - }; - - const appUrl = - isCloud === false - ? `/apps/${data.id}` - : `https://shuffler.io/apps/${data.id}`; - - return ( - - - - { - setMouseHoverIndex(index); - }} - onMouseOut={() => { - setMouseHoverIndex(-1); - }} - > - - {data.name} -
    -
    - {normalizedString(data.name)} -
    -
    - {data.categories !== null - ? normalizedString(data.categories).join(", ") - : "NA"} -
    -
    - {data.tags && - data.tags.map((tag, tagIndex) => ( - - {normalizedString(tag)} - {tagIndex < data.tags.length - 1 ? ", " : ""} - - ))} -
    - {/* )} */} -
    -
    -
    -
    -
    -
    - ); - }); - }, [filteredUserAppdata, latestActivatedAppId]); - - return (
    - {isLoggedIn ? ( -
    - {isLoading ? : ( -
    - - -
    - {memoizedHits} -
    -
    -
    - )} -
    + {isLoading ? ( + ) : (
    - Please login to your account first to view {`${currTab === 1 ? "Organization" : "My"}`} Apps.
    - Or signup to create a new account.
    + {isLoggedIn ? ( +
    +
    + + +
    + {filteredUserAppdata.map((data, index) => { + const isMouseOverOnCloudIcon = false; + const xs = 12; + const rowHandler = 12; + const searchClient = {}; + const userdata = {}; + + const paperStyle = { + backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#1A1A1A", + color: "rgba(241, 241, 241, 1)", + padding: isHeader ? null : 15, + cursor: "pointer", + position: "relative", + width: 339, + height: 96, + borderRadius: 8, + }; + + var parsedname = ""; + for (var key = 0; key < data.name.length; key++) { + var character = data.name.charAt(key); + if (character === character.toUpperCase()) { + if ( + data.name.charAt(key + 1) !== undefined && + data.name.charAt(key + 1) === + data.name.charAt(key + 1).toUpperCase() + ) { + } else { + parsedname += " "; + } + } + parsedname += character; + } + + parsedname = ( + parsedname.charAt(0).toUpperCase() + parsedname.substring(1) + ).replaceAll("_", " "); + + const normalizedString = (name) => { + if (typeof name === 'string') { + return name.replace(/_/g, ' '); + } else { + return name; + } + }; + + const appUrl = + isCloud === true + ? `/apps/${data.id}` + : `https://shuffler.io/apps/${data.id}`; + + return ( + + + + { + setMouseHoverIndex(index); + }} + onMouseOut={() => { + setMouseHoverIndex(-1); + }} + > + + {data.name} +
    +
    + {normalizedString(data.name)} +
    +
    + {data.categories !== null + ? normalizedString(data.categories).join(", ") + : "NA"} +
    +
    + {data.tags && + data.tags.map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))} +
    + {/* )} */} +
    +
    +
    +
    +
    +
    + ); + }) + } +
    +
    +
    +
    + ) : ( + Please login to your account first to view {`${currTab === 1 ? "Organization" : "My"}`} Apps.
    + Or signup to create a new account.
    + )}
    )}
    @@ -1829,7 +1767,7 @@ const AppGrid = (props) => { }; - const AppTab = () => { + const AppTab = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith }) => { const [isAnyAppActivated, setIsAnyAppActivated] = useState(false); @@ -1889,7 +1827,7 @@ const AppGrid = (props) => { {currTab === 0 ? ( ) : currTab === 1 || currTab === 2 ? ( - + ) : null}
    @@ -1898,6 +1836,40 @@ const AppGrid = (props) => { const CustomSearchBox = connectSearchBox(SearchBox); const CustomHits = connectHits(Hits); + + const DisplayAllAppsTab = () => { + const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]); + const [selectedTagsForUserAndOrgApps, setSelectedTagsForUserAndOrgApps] = useState([]); + const [selectedOptionOfCreatedWith, setSelectedOptionOfCreatedWith] = useState([]); + + return ( +
    + +
    + {currTab === 0 ? ( + + ) : ( + + )} + +
    + +
    +
    + ); + }; + return (
    { display: "flex", }} > - {/* -
    - -
    - */}
    - -
    - {currTab === 0 ? : } - -
    - {/* */} - -
    + {showSuggestion === true ? (
    {
    { const AppStats = (defaultprops) => { - const { globalUrl, selectedOrganization, userdata, isCloud, inputWorkflows, } = defaultprops; + const { globalUrl, selectedOrganization, userdata, isCloud, inputWorkflows,clickedFromOrgTab } = defaultprops; const [keys, setKeys] = useState([]) const [searches, setSearches] = useState([]); @@ -703,7 +703,7 @@ const AppStats = (defaultprops) => { textAlign: "center", padding: 40, margin: 5, - marginLeft: 90, + marginLeft: clickedFromOrgTab? null:90, backgroundColor: theme.palette.platformColor, border: "1px solid rgba(255,255,255,0.3)", maxWidth: 300, diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 22500fe2..c5c4e9c3 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -304,7 +304,7 @@ const CacheView = (props) => { }, }} required - fullWidth={true} + fullWidth autoComplete="Value" placeholder="123" id="Valuefield" @@ -349,7 +349,7 @@ const CacheView = (props) => { return ( -
    +
    {modalView}

    Shuffle Datastore

    @@ -434,6 +434,8 @@ const CacheView = (props) => { style={{ minWidth: 300, maxWidth: 300, + height:200, + overflowX: "hidden", }} primary={validate.valid ? { }} onDrop={uploadFile} > -
    +
    { logo diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 209407ea..6a1c577d 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -3075,7 +3075,7 @@ const AppCreator = (defaultprops) => { 0 && (!refreshUrl.startsWith("http") || refreshUrl.includes("//shuffler.")) ? "2px solid red" : "inherit", }} fullWidth={true} placeholder="The URL to retrieve refresh-tokens at" diff --git a/frontend/src/views/Search.jsx b/frontend/src/views/Search.jsx index 687e392f..b9b65f06 100644 --- a/frontend/src/views/Search.jsx +++ b/frontend/src/views/Search.jsx @@ -57,52 +57,52 @@ const Search = (props) => { //Stop unnecessariry re-rendering of the component to improve performace const MemoizedAppGrid = useMemo(() => , [curTab]); + />, [curTab]); -const MemoizedWorkflowGrid = useMemo(() => , [curTab]); + const MemoizedWorkflowGrid = useMemo(() => , [curTab]); -const MemoizedDocsGrid = useMemo(() => , [curTab]); + const MemoizedDocsGrid = useMemo(() => , [curTab]); -const MemoizedCreatorGrid = useMemo(() => , [curTab]); + const MemoizedCreatorGrid = useMemo(() => , [curTab]); const MemoizedDiscordChat = useMemo(() => ) -const useStyles = makeStyles({ - hideIndicator: { - display: 'none', - }, - customTab: { - justifyContent: 'center', - gap: '46px', - } -}); -const classes = useStyles(); + const useStyles = makeStyles({ + hideIndicator: { + display: 'none', + }, + customTab: { + justifyContent: 'center', + gap: '46px', + } + }); + const classes = useStyles(); if (serverside === true) { return null; @@ -177,7 +177,7 @@ const classes = useStyles(); const StyledTab = styled(Tab)(({ theme }) => ({ width: 151, height: 51, - padding: "10px 20px", + padding: "10px 20px", borderRadius: 8, fontWeight: 600, textTransform: "none", @@ -192,16 +192,16 @@ const classes = useStyles(); })); const tabSpanStyling = { - display: 'flex', - flexDirection: 'row', - alignItems: 'center' + display: 'flex', + flexDirection: 'row', + alignItems: 'center' } - const tabTextStyling = { - marginLeft: '5px', - color: 'white' + const tabTextStyling = { + marginLeft: '5px', + color: 'white' } - + // Random names for type & autoComplete. Didn't research :^) const landingpageDataBrowser = ( @@ -219,7 +219,7 @@ const classes = useStyles(); margin: isHeader ? null : "auto", marginTop: hidemargins === true ? 0 : isHeader ? null : 25, backgroundColor: "rgba(33, 33, 33, 1)", - borderRadius:8 + borderRadius: 8 }} value={curTab} indicatorColor="primary" @@ -228,28 +228,28 @@ const classes = useStyles(); aria-label="disabled tabs example" variant="scrollable" scrollButtons="auto" - classes={{indicator: classes.hideIndicator, root: classes.customTab}} + classes={{ indicator: classes.hideIndicator, root: classes.customTab }} > - + App } /> - + Workflow } @@ -257,11 +257,11 @@ const classes = useStyles(); - + Docs } @@ -273,7 +273,7 @@ const classes = useStyles(); }} label={ - + Creators } @@ -285,17 +285,17 @@ const classes = useStyles(); }} label={ - - Discord Chat + + Discord Chat } /> - {curTab === 0 && MemoizedAppGrid} - {curTab === 1 && MemoizedWorkflowGrid} - {curTab === 2 && MemoizedDocsGrid} - {curTab === 3 && MemoizedCreatorGrid} - {curTab === 4 && MemoizedDiscordChat} + {curTab === 0 && MemoizedAppGrid} + {curTab === 1 && MemoizedWorkflowGrid} + {curTab === 2 && MemoizedDocsGrid} + {curTab === 3 && MemoizedCreatorGrid} + {curTab === 4 && MemoizedDiscordChat}
    ); From b89642d7e8419bf2a62d9076447517e5d9f502cd Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 30 Apr 2024 19:55:25 +0200 Subject: [PATCH 092/142] Fixed oauth2 loading problems --- frontend/src/components/Oauth2Auth.jsx | 12 +- frontend/src/views/AngularWorkflow.jsx | 282 ++++++++++--------------- 2 files changed, 113 insertions(+), 181 deletions(-) diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 8b9d58e6..6e775652 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -471,8 +471,6 @@ const AuthenticationOauth2 = (props) => { url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`; } - console.log("URL: ", url) - // Force new consent //const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`; @@ -525,9 +523,8 @@ const AuthenticationOauth2 = (props) => { //} //while(open === true) } catch (e) { - toast( - "Failed authentication - probably bad credentials. Try again" - ); + toast("Failed authentication - probably bad credentials. Try again") + setButtonClicked(false); } @@ -807,6 +804,7 @@ const AuthenticationOauth2 = (props) => { const defaultValue = data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value + const fieldname = data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name return ( @@ -1034,6 +1032,10 @@ const AuthenticationOauth2 = (props) => { variant="contained" fullWidth onClick={() => { + toast.info("Starting authentication process", { + "autoClose": 1500, + }) + handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes); }} color="primary" diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index ecaf4e6a..3df13f42 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1080,16 +1080,19 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - toast("Error: " + responseJson.reason); + // Remove the timeout. Has to be clicked + toast.error("Error: " + responseJson.reason, { + "autoClose": false, + }) + } else { if (refresh === true) { - getAppAuthentication(true, true, true); + getAppAuthentication(true, true, true) } else { - getAppAuthentication(true, false); + getAppAuthentication(true, false) } - setAuthenticationModalOpen(false); - + setAuthenticationModalOpen(false) // Needs a refresh with the new authentication.. //toast("Successfully saved new app auth") } @@ -7203,73 +7206,47 @@ const AngularWorkflow = (defaultprops) => { toast("Error: name can't be empty"); return; } - - var mappedStartnode = ""; - const alledges = cy.edges().jsons(); + + var mappedStartnode = "" + const alledges = cy.edges().jsons() if (alledges !== undefined && alledges !== null && alledges.length > 0) { - for (let edgekey in alledges) { - const tmp = alledges[edgekey]; - console.log("TMP: ", tmp, tmp.data.source); - if (tmp.data.source === trigger.id) { - mappedStartnode = tmp.data.target; - break; - } - } + for (let edgekey in alledges) { + const tmp = alledges[edgekey] + console.log("TMP: ", tmp, tmp.data.source) + if (tmp.data.source === trigger.id) { + mappedStartnode = tmp.data.target + break + } + } } - const data = usecase; - if (data.type === "create") toast("Creating pipeline"); - else toast("stopping pipeline"); - const url = `${globalUrl}/api/v1/triggers/pipeline`; + + toast("Creating pipeline") + const data = usecase + const url = `${globalUrl}/api/v1/triggers/pipeline` fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(data), - credentials: "include", - }) + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(data), + credentials: "include", + } + ) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!"); - } - + } + return response.json(); }) .then((responseJson) => { if (!responseJson.success) { toast("Failed to set pipeline: " + responseJson.reason); } else { - if (data.type === "create") toast("Pipeline will be created!"); - else toast("Pipeline will be stopped!"); - if (!workflow.triggers[triggerindex].parameters) { - workflow.triggers[triggerindex].parameters = []; - } - - if (workflow.triggers[triggerindex].parameters.length > 0) { - workflow.triggers[triggerindex].parameters[0].name = data.name; - workflow.triggers[triggerindex].parameters[0].value = data.command; - - trigger.parameters[0].name = data.name; - trigger.parameters[0].value = data.command; - if (data.type === "stop") { - trigger.status = "stopped"; - workflow.triggers[triggerindex].status = "stopped"; - } else { - trigger.status = "running"; - workflow.triggers[triggerindex].status = "running"; - } - } else { - const newParameter = { - name: data.name, - value: data.command, - }; - trigger.parameters.push(newParameter); - if (data.type === "stop") trigger.status = "stopped"; - else trigger.status = "running"; - workflow.triggers[triggerindex] = trigger; - } - + toast("Successfully created pipeline"); + workflow.triggers[triggerindex].status = "running"; + trigger.status = "running"; setSelectedTrigger(trigger); setWorkflow(workflow); console.log("Should set the status to running and save"); @@ -7278,9 +7255,9 @@ const AngularWorkflow = (defaultprops) => { }) .catch((error) => { //toast(error.toString()); - console.log("Get pipeline error: ", error.toString()); + console.log("Get schedule error: ", error.toString()); }); - }; + } const submitSchedule = (trigger, triggerindex) => { if (trigger.name.length <= 0) { @@ -13679,6 +13656,7 @@ const AngularWorkflow = (defaultprops) => { if (data.Name.toLowerCase() === "cloud") { return null } + return ( { Run HTTP Request
    */} -
    { - if (selectedTrigger.status == "running") { - return; // Do nothing if cursor is "not-allowed" - } - const pipelineConfig = { - name: selectedTrigger.label, - type: "create", - command: "load tcp://0.0.0.0:514 | read syslog | export", - environment: selectedTrigger.environment, - workflow_id: workflow.id, - trigger_id: selectedTrigger.id, - }; - submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig); - }} - > - Start Syslog listener -
    +
    { - if (selectedTrigger.status == "running") { - return; - } - const pipelineConfig = { - name: selectedTrigger.label, - type: "create", - command: - "export --live | sigma /path/to/rules | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines", - environment: selectedTrigger.environment, - workflow_id: workflow.id, - trigger_id: selectedTrigger.id, - }; + }} + onClick={() => { + const pipelineConfig = { + "name": selectedTrigger.label, + "type": "create", + "command": "load tcp://0.0.0.0:514 | read syslog | export", + "environment": selectedTrigger.environment, + } - submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig); - }} - > - Run Sigma Rulesearch -
    + submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) + }} + > + Start Syslog listener +
    -
    { - if (selectedTrigger.status == "running") { - return; - } - const pipelineConfig = { - name: selectedTrigger.label, - type: "create", - command: - "from kafka://1.2.3.4 --topic foo | to http://api.com X-Token:Secret", - environment: selectedTrigger.environment, - workflow_id: workflow.id, - trigger_id: selectedTrigger.id, - }; +
    - Follow Kafka Queue -
    + }} + onClick={() => { + const pipelineConfig = { + "name": selectedTrigger.label, + "type": "create", + "command": "export --live | sigma /path/to/rules | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines", + "environment": selectedTrigger.environment, + } + + submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) + }} + > + Run Sigma Rulesearch +
    + +
    { + const pipelineConfig = { + "name": selectedTrigger.label, + "type": "create", + "command": "from kafka://1.2.3.4 --topic foo | to http://api.com X-Token:Secret", + "environment": selectedTrigger.environment, + } + + submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) + }} + > + Follow Kafka Queue +
    { variant="contained" disabled={selectedTrigger.status === "running"} onClick={() => { - toast("Select anyone of the parameters to start") + toast("Should start. But it doesn't") }} color="primary" > Start -
    : null} - + diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index a02f0930..ceebabdd 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -6243,7 +6243,7 @@ If you're interested, please let me know a time that works for you, or set up a letterSpacing: "1px", }} > - All Tenants + All Your Organizations
    diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3df13f42..af90c8d9 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -11890,6 +11890,7 @@ const AngularWorkflow = (defaultprops) => { borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { + setLastSaved(false) console.log("Found value: ", newValue) var parsedinput = { target: { value: newValue } } @@ -11923,10 +11924,10 @@ const AngularWorkflow = (defaultprops) => { {data.name} : null} - Choose {data.name} + Choose Subflow '{data.name}' - } placement="bottom"> + }> { borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { + setLastSaved(false) handleSubflowStartnodeSelection({ target: { value: newValue } }) }} renderOption={(props, action, state) => { @@ -12114,9 +12116,10 @@ const AngularWorkflow = (defaultprops) => { workflow.triggers[selectedTriggerIndex].parameters[1].value } onBlur={(e) => { - workflow.triggers[selectedTriggerIndex].parameters[1].value = - e.target.value; - setWorkflow(workflow); + setLastSaved(false) + + workflow.triggers[selectedTriggerIndex].parameters[1].value = e.target.value + setWorkflow(workflow) }} /> {!showDropdown ? null : @@ -13455,10 +13458,10 @@ const AngularWorkflow = (defaultprops) => { {data.name} : null} - Choose {data.name} + Choose Trigger '{data.name}' - } placement="bottom"> + }> Date: Fri, 3 May 2024 04:14:24 +0000 Subject: [PATCH 097/142] Implement lazy initialization for Tenzir node deployment --- .env | 1 - functions/onprem/orborus/orborus.go | 194 +++++++++++++++------------- 2 files changed, 105 insertions(+), 90 deletions(-) diff --git a/.env b/.env index b0c7ad25..61885b3b 100755 --- a/.env +++ b/.env @@ -101,7 +101,6 @@ SHUFFLE_OPENSEARCH_INDEX_PREFIX= SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true #Tenzir related -IS_TENZIR=false SHUFFLE_TENZIR_URL=http://localhost:5160 diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index cf921a32..acc31e96 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -103,7 +103,6 @@ var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG") var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var orborusLabel = os.Getenv("SHUFFLE_ORBORUS_LABEL") var memcached = os.Getenv("SHUFFLE_MEMCACHED") -var isTenzir = os.Getenv("IS_TENZIR") var tenzirUrl = os.Getenv("SHUFFLE_TENZIR_URL") var executionIds = []string{} @@ -112,7 +111,6 @@ var namespacemade = false // For K8s var dockercli *dockerclient.Client var containerId string var executionCount = 0 -var isTenzirReady = false func init() { var err error @@ -1498,18 +1496,6 @@ func main() { log.Printf("[INFO] Setting up Docker environment. Downloading worker and App SDK!") initializeImages() - - if isTenzir == "true" { - go func() { - if err := deployTenzirNode(); err != nil { - log.Printf("[ERROR] Failed to deploy the tenzir node, reason: %v", err) - } else { - log.Printf("[INFO] Tenzir node is deployed successfully and is available for requests!") - isTenzirReady = true - } - }() - } - workerImage := fmt.Sprintf("%s/%s/shuffle-worker:%s", baseimageregistry, baseimagename, workerVersion) if len(newWorkerImage) > 0 { workerImage = newWorkerImage @@ -1682,17 +1668,13 @@ func main() { for _, incRequest := range executionRequests.Data { // Looking for specific jobs if incRequest.Type == "PIPELINE_CREATE" || incRequest.Type == "PIPELINE_STOP" || incRequest.Type == "PIPELINE_DELETE" { - if isTenzir == "true" && isTenzirReady { - err := handlePipeline(incRequest) - if err != nil { - log.Printf("[ERROR] Failed handling pipeline: %s", err) - //update it to db ?? - } - } else { - log.Printf("[WARNING] Unable to Handle pipeline request as tenzir node is not ready") + + err := handlePipeline(incRequest) + if err != nil { + log.Printf("[ERROR] Failed handling pipeline: %s", err) } + toBeRemoved.Data = append(toBeRemoved.Data, incRequest) - } else if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" { log.Printf("[INFO] Should delete -> download new image %#v", incRequest.ExecutionArgument) @@ -2045,6 +2027,12 @@ func main() { // Read from Cache and send it to a webhook // docker run tenzir/tenzir:latest 'from http://192.168.86.44:5002/api/v1/orgs/7e9b9007-5df2-4b47-bca5-c4d267ef2943/cache/CIDR%20ranges?type=text&authorization=cec9d01f-09b2-4419-8a0a-76c6046e3fef read lines | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines' func handlePipeline(incRequest shuffle.ExecutionRequest) error { + err := deployTenzirNode() + if err != nil{ + log.Printf("[ERROR] failed to deploy the pipeline, reason: %s", err) + } + + // no need of execution arguments for state updates if incRequest.Type != "PIPELINE_STOP" && len(incRequest.ExecutionArgument) == 0 { log.Printf("[ERROR] No execution argument found for pipeline create. Skipping") @@ -2113,77 +2101,55 @@ func handlePipeline(incRequest shuffle.ExecutionRequest) error { } func deployTenzirNode() error { + if isKubernetes == "true" { + return errors.New("kubernetes not implemented") + } - if isKubernetes == "true" { - return errors.New("kubernetes not implemented") - } + ctx := context.Background() - ctx := context.Background() + imageName := "tenzir/tenzir:latest" + containerName := "tenzir-node" + containerStartOptions := container.StartOptions{} - imageName := "tenzir/tenzir" - containerName := "tenzir-node" + containerInfo, err := dockercli.ContainerInspect(ctx, containerName) + if err != nil { + if dockerclient.IsErrNotFound(err) { + pullOptions := types.ImagePullOptions{} + out, err := dockercli.ImagePull(ctx, imageName, pullOptions) + if err != nil { + log.Printf("[ERROR] Failed to pull the Tenzir image: %s", err) + return err + } + defer out.Close() - healthconfig := &container.HealthConfig{ - Test: []string{"tenzir --connection-timeout=30s --connection-retry-delay=1s 'api /ping'"}, - Interval: 30 * time.Second, - Retries: 1, - } + err = createAndStartTenzirNode(ctx, containerName, imageName, containerStartOptions) + if err != nil { + return err + } + } else { + return err + } + } else { + if !containerInfo.State.Running { + log.Printf("[DEBUG] Tenzir Node exists but is not running, starting it") + err := dockercli.ContainerStart(ctx, containerName, containerStartOptions) + if err != nil { + log.Printf("[ERROR] Failed to start Tenzir Node container: %v", err) + return err + } + log.Printf("[INFO] Tenzir Node container started successfully") + log.Printf("[INFO] Waiting for Tenzir to become available ...") + err = checkTenzirNode() + if err != nil { + return err + } + log.Printf("[INFO] Successfully deployed Tenzir Node!") + } else { + log.Printf("[DEBUG] Tenzir Node Container already running") + } + } - config := &container.Config{ - Cmd: []string{"--commands=web server --mode=dev --bind=0.0.0.0"}, - Image: imageName, - Healthcheck: healthconfig, - ExposedPorts: nat.PortSet{"5160/tcp": struct{}{}}, - Entrypoint: []string{containerName}, - } - - hostConfig := &container.HostConfig{ - PortBindings: nat.PortMap{ - "5160/tcp": []nat.PortBinding{{HostPort: "5160"}}, - }, - Mounts: []mount.Mount{ - { - Type: mount.TypeVolume, - Source: containerName, - Target: "/var/lib/tenzir/", - }, - }, - VolumeDriver: "local", - } - - // do we need to pull manually ?? - pullOptions := types.ImagePullOptions{} - out, err := dockercli.ImagePull(ctx, imageName, pullOptions) - if err != nil { - log.Printf("[ERROR] Failed to pull the tenzir image %s", err) - } - defer out.Close() - - containerStartOptions := container.StartOptions{} - _, err = dockercli.ContainerCreate(ctx, config, hostConfig, nil, nil, containerName) - if err != nil { - if strings.Contains(fmt.Sprintf("%s", err), "Conflict. The container name ") { - log.Printf("[DEBUG] Tenzir Node Container already exists, starting it") - } else { - log.Printf("[ERROR] Failed to create Tenzir container: %s", err) - return err - } - } - - err = dockercli.ContainerStart(ctx, containerName, containerStartOptions) - if err != nil { - log.Printf("[ERROR] Failed to start Tenzir Node container: %v", err) - return err - } - log.Printf("[INFO] Tenzir Node container started successfully") - - log.Printf("[INFO] Waiting for tenzir to become available ...") - err = checkTenzirNode() - if err != nil { - return err - } - - return nil + return nil } func checkTenzirNode() error { @@ -2210,6 +2176,56 @@ func checkTenzirNode() error { return fmt.Errorf("tenzir node is not available") } +func createAndStartTenzirNode(ctx context.Context, containerName, imageName string, containerStartOptions container.StartOptions) error { + healthconfig := &container.HealthConfig{ + Test: []string{"tenzir --connection-timeout=30s --connection-retry-delay=1s 'api /ping'"}, + Interval: 30 * time.Second, + Retries: 1, + } + + config := &container.Config{ + Cmd: []string{"--commands=web server --mode=dev --bind=0.0.0.0"}, + Image: imageName, + Healthcheck: healthconfig, + ExposedPorts: nat.PortSet{"5160/tcp": struct{}{}}, + Entrypoint: []string{containerName}, + } + + hostConfig := &container.HostConfig{ + PortBindings: nat.PortMap{ + "5160/tcp": []nat.PortBinding{{HostPort: "5160"}}, + }, + Mounts: []mount.Mount{ + { + Type: mount.TypeVolume, + Source: containerName, + Target: "/var/lib/tenzir/", + }, + }, + VolumeDriver: "local", + } + _, err := dockercli.ContainerCreate(ctx, config, hostConfig, nil, nil, containerName) + if err != nil { + return err + } + + err = dockercli.ContainerStart(ctx, containerName, containerStartOptions) + if err != nil { + log.Printf("[ERROR] Failed to start Tenzir Node container: %v", err) + return err + } + log.Printf("[INFO] Tenzir Node container started successfully") + + log.Printf("[INFO] Waiting for Tenzir to become available ...") + err = checkTenzirNode() + if err != nil { + return err + } + log.Printf("[INFO] Successfully deployed Tenzir Node !") + + return nil +} + func createPipeline(command, identifier string) (string, error) { toBeDeleted := false From d9c9c8c442e1332faabdefb7d6ac753605cdd4cc Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 3 May 2024 17:46:12 +0200 Subject: [PATCH 098/142] Update docker-compose.yml --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index f146a31a..a1c86dbf 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,7 +60,7 @@ services: security_opt: - seccomp:unconfined opensearch: - image: opensearchproject/opensearch:2.12.0 + image: opensearchproject/opensearch:2.11.1 hostname: shuffle-opensearch container_name: shuffle-opensearch env_file: .env From 8a3186a8c1156d06d42b6b02f6173c9c426a1532 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Mon, 6 May 2024 13:28:49 +0000 Subject: [PATCH 099/142] fresh look for docs --- frontend/src/codeeditor-index.css | 14 + frontend/src/index.css | 20 +- frontend/src/views/Docs.jsx | 1650 ++++++++++++++--------------- 3 files changed, 809 insertions(+), 875 deletions(-) diff --git a/frontend/src/codeeditor-index.css b/frontend/src/codeeditor-index.css index 6a8b757f..c87341f5 100644 --- a/frontend/src/codeeditor-index.css +++ b/frontend/src/codeeditor-index.css @@ -90,3 +90,17 @@ code { color: #fff; font-size: 14px; } + +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-thumb { + background-color: #494949; + border-radius: 5px; +} + +::-webkit-scrollbar-track { + background-color: rgb(26,26,26); +} + diff --git a/frontend/src/index.css b/frontend/src/index.css index 4188b885..9b1fc96c 100755 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -18,6 +18,11 @@ code { margin-right: -13px; } +.toc:hover { + background-color: gray; + color: white; +} + /* .cm-string{ z-index: -1; } @@ -30,4 +35,17 @@ code { .CodeMirror-selectedtext{ background-color: rgba(28, 47, 69, 0.6) !important; color: rgb(255, 255, 255) !important; -} \ No newline at end of file +} + +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-thumb { + background-color: #494949; + border-radius: 5px; +} + +::-webkit-scrollbar-track { + background-color: rgb(26,26,26); +} diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 4ae1cfbe..9835d947 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -11,104 +11,106 @@ import { useParams, useNavigate, Link } from "react-router-dom"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { - Grid, - TextField, - IconButton, - Tooltip, - Divider, - Button, - Menu, - MenuItem, - Typography, - Paper, - List, - Collapse, - ListItemButton, - ListItemText + Grid, + TextField, + IconButton, + Tooltip, + Divider, + Button, + Menu, + MenuItem, + Typography, + Paper, + List, + Collapse, + ListItemButton, + ListItemText } from "@mui/material"; import { - Link as LinkIcon, - Edit as EditIcon, - KeyboardArrowRight as KeyboardArrowRightIcon, - ExpandMore as ExpandMoreIcon, - FileCopy as FileCopyIcon + Link as LinkIcon, + Edit as EditIcon, + KeyboardArrowRight as KeyboardArrowRightIcon, + ExpandMore as ExpandMoreIcon, + FileCopy as FileCopyIcon } from "@mui/icons-material"; +import { fontGrid } from "@mui/material/styles/cssUtils.js"; const Body = { - //maxWidth: 1000, - //minWidth: 768, - maxWidth: "100%", - minWidth: "100%", - display: "flex", - height: "100%", - color: "white", - position: "relative", - //textAlign: "center", + //maxWidth: 1000, + //minWidth: 768, + maxWidth: "100%", + minWidth: "100%", + display: "flex", + height: "100%", + color: "white", + position: "relative", + //textAlign: "center", }; const dividerColor = "rgb(225, 228, 232)"; const hrefStyle = { - color: "rgba(255, 255, 255, 0.40)", - textDecoration: "none", + color: "rgba(255, 255, 255, 0.40)", + textDecoration: "none", }; + const hrefStyle2 = { - color: "#f86a3e", - textDecoration: "none", + color: "#f86a3e", + textDecoration: "none", }; const innerHrefStyle = { - color: "rgba(255, 255, 255, 0.75)", - textDecoration: "none", + color: "rgba(255, 255, 255, 0.75)", + textDecoration: "none", }; export const CopyToClipboard = (props) => { - const {text, style, onCopy} = props; - const parsedstyle = style !== undefined ? style : { - position: "absolute", - right: 0, - top: -10, - } + const { text, style, onCopy } = props; + const parsedstyle = style !== undefined ? style : { + position: "absolute", + right: 0, + top: -10, + } - return ( -
    - { - navigator.clipboard.writeText(text); - toast("Copied to clipboard") - }} - > - - -
    - ) + return ( +
    + { + navigator.clipboard.writeText(text); + toast("Copied to clipboard") + }} + > + + +
    + ) } export const OuterLink = (props) => { if (props.href.includes("http") || props.href.includes("mailto")) { - return ( - - {props.children} - - ); + return ( + + {props.children} + + ); } return ( - - {props.children} - + + {props.children} + ); - } +} export const Img = (props) => { @@ -120,850 +122,750 @@ export const CodeHandler = (props) => { const validate = validateJson(propvalue) - var newprop = propvalue - if (validate.valid === false) { - // Check if https://shuffler.io in the url - // if so, then we change it for the current url - if (propvalue.includes("https://shuffler.io")) { - newprop = propvalue.replace("https://shuffler.io", window.location.origin) - } + var newprop = propvalue + if (validate.valid === false) { + // Check if https://shuffler.io in the url + // if so, then we change it for the current url + if (propvalue.includes("https://shuffler.io")) { + newprop = propvalue.replace("https://shuffler.io", window.location.origin) + } - // Check if it contains Bearer APIKEY - // If so, replace apikey - //if (newprop.includes("Bearer APIKEY")) { - // newprop = newprop.replace("Bearer APIKEY", "Bearer API - //} - } + // Check if it contains Bearer APIKEY + // If so, replace apikey + //if (newprop.includes("Bearer APIKEY")) { + // newprop = newprop.replace("Bearer APIKEY", "Bearer API + //} + } - // Need to check if it's singletick or multi + // Need to check if it's singletick or multi console.log("PROP: ", propvalue, props) - if (props.inline === true) { - // Show it inline - return ( - - {newprop} - - ) - } + if (props.inline === true) { + // Show it inline + return ( + + {newprop} + + ) + } return ( -
    - {validate.valid === true ? - - : -
    - - {newprop} - - -
    - } -
    - ) +
    + {validate.valid === true ? + + : +
    + + {newprop} + + +
    + } +
    + ) } const Docs = (defaultprops) => { - const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; + const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; - let navigate = useNavigate(); + let navigate = useNavigate(); - // Quickfix for react router 5 -> 6 - const params = useParams(); - //var props = JSON.parse(JSON.stringify(defaultprops)) - var props = Object.assign({ selected: false }, defaultprops); - props.match = {} - props.match.params = params + // Quickfix for react router 5 -> 6 + const params = useParams(); + //var props = JSON.parse(JSON.stringify(defaultprops)) + var props = Object.assign({ selected: false }, defaultprops); + props.match = {} + props.match.params = params - useEffect(() => { - //if (params["key"] === undefined) { - // navigate("/docs/about") - // return - //} - }, []) - //console.log("PARAMS: ", params) - - const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false); - const [data, setData] = useState(""); - const [firstrequest, setFirstrequest] = useState(true); - const [list, setList] = useState([]); - const [isopen, setOpen] = useState(-1); - const [, setListLoaded] = useState(false); - const [anchorEl, setAnchorEl] = React.useState(null); - const [headingSet, setHeadingSet] = React.useState(false); - const [selectedMeta, setSelectedMeta] = React.useState({ - link: "hello", - read_time: 2, - }); - const [tocLines, setTocLines] = React.useState([]); - const [baseUrl, setBaseUrl] = React.useState( - serverside === true ? "" : window.location.href - ); - - function handleClick(event) { - setAnchorEl(event.currentTarget); - } - - function handleClose() { - setAnchorEl(null); - } - - const handleCollapse = (index) => { - setOpen(isopen === index ? -1 : index) - }; - - const SidebarPaperStyle = { - backgroundColor: theme.palette.surfaceColor, - overflowX: "hidden", - position: "relative", - paddingLeft: 15, - paddingRight: 15, - paddingTop: 15, - marginTop: 15, - minHeight: "80vh", - //height: "50vh", - }; - - const Heading = (props) => { - const element = React.createElement( - `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 } }, - props.children - ); - const [hover, setHover] = useState(false); - - var extraInfo = ""; - if (props.level === 1) { - extraInfo = ( -
    -
    - {isMobile ? null : ( - - - - - - )} - {isMobile ? null : ( -
    - )} - - {selectedMeta.read_time} minute - {selectedMeta.read_time === 1 ? "" : "s"} to read - -
    -
    - {isMobile || - selectedMeta.contributors === undefined || - selectedMeta.contributors === null ? ( - "" - ) : ( -
    - {selectedMeta.contributors.slice(0, 7).map((data, index) => { - return ( - - - {data.url} - - - ); - })} -
    - )} -
    -
    - ); - } - - if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { - if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { - extraInfo = "" - } - } - - return ( - { - setHover(true); - }} - > - {props.level !== 1 ? ( - - ) : null} - {element} - {extraInfo} - - ) - } - - const SideBar = { - minWidth: 300, - width: "20%", - left: 0, - position: "sticky", - top: 50, - minHeight: "90vh", - maxHeight: "90vh", - overflowX: "hidden", - overflowY: "auto", - zIndex: 1000, - //borderRight: "1px solid rgba(255,255,255,0.3)", - }; - - const fetchDocList = () => { - fetch(`${globalUrl}/api/v1/docs`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }) - .then((response) => response.json()) - .then((responseJson) => { - if (responseJson.success) { - setList(responseJson.list); - } else { - setList(["# Error loading documentation. Please contact us if this persists.",]); - toast("Failed loading documentation. Please reload the window") - } - setListLoaded(true); - }) - .catch((error) => { }); - }; - - const fetchDocs = (docId) => { - fetch(`${globalUrl}/api/v1/docs/${docId}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }) - .then((response) => response.json()) - .then((responseJson) => { - if (responseJson.success === false) { - //toast("Failed loading documentation. Please reload the UI") - } - - if (responseJson.success && responseJson.reason !== undefined) { - // Find tags and translate them into ![]() format - const imgRegex = / 1) { - parsedline[0] = parsedline[0].replaceAll("*", ""); - parsedline[0] = parsedline[0].replaceAll("[", ""); - parsedline[0] = parsedline[0].replaceAll("]", ""); - parsedline[0] = parsedline[0].replaceAll("(", ""); - parsedline[0] = parsedline[0].replaceAll(")", ""); - parsedline[0] = parsedline[0].trim(); - - parsedline[1] = parsedline[1].replaceAll("*", ""); - parsedline[1] = parsedline[1].replaceAll("[", ""); - parsedline[1] = parsedline[1].replaceAll("]", ""); - parsedline[1] = parsedline[1].replaceAll(")", ""); - parsedline[1] = parsedline[1].replaceAll("(", ""); - parsedline[1] = parsedline[1].trim(); - //console.log(parsedline[0], parsedline[1]) - - innerTocLines.push({ - text: parsedline[0], - link: parsedline[1], - }); - } else { - console.log("Bad line for parsing: ", line); - } - } - } - - setTocLines(innerTocLines); - } - } else { - setData("# Error\nThis page doesn't exist."); - } - }) - .catch((error) => { }); - }; - - if (firstrequest) { - setFirstrequest(false); - if (!serverside) { - if (window.innerWidth < 768) { - setMobile(true); - } - } - - if (selectedDoc !== undefined) { - setData(selectedDoc.reason); - setList(selectedDoc.list); - setListLoaded(true); - } else { - if (!serverside) { - fetchDocList(); - - //const propkey = props.match.params.key - //if (propkey === undefined) { + useEffect(() => { + //if (params["key"] === undefined) { // navigate("/docs/about") - // return null + // return //} - // - if (props.match.params.key === undefined) { - - } else { - console.log("DOCID: ", props.match.params.key) - fetchDocs(props.match.params.key) - } - } - } - } - - // Handles search-based changes that origin from outside this file - if (serverside !== true && window.location.href !== baseUrl) { - setBaseUrl(window.location.href); - fetchDocs(props.match.params.key); - } - - const parseElementScroll = () => { - const offset = 45; - var parent = document.getElementById("markdown_wrapper_outer"); - if (parent !== null) { - //console.log("IN PARENT") - var elements = parent.getElementsByTagName("h2"); - - const name = window.location.hash - .slice(1, window.location.hash.lenth) - .toLowerCase() - .split("%20") - .join(" ") - .split("_") - .join(" ") - .split("-") - .join(" ") - .split("?")[0] - - //console.log(name) - var found = false; - for (var key in elements) { - const element = elements[key]; - if (element.innerHTML === undefined) { - continue; - } - - // Fix location.. - if (element.innerHTML.toLowerCase() === name) { - //console.log(element.offsetTop) - element.scrollIntoView({ behavior: "smooth" }); - //element.scrollTo({ - // top: element.offsetTop+offset, - // behavior: "smooth" - //}) - found = true; - //element.scrollTo({ - // top: element.offsetTop-100, - // behavior: "smooth" - //}) - } - } - - // H# - if (!found) { - elements = parent.getElementsByTagName("h3"); - //console.log("NAMe: ", name) - found = false; - for (key in elements) { - const element = elements[key]; - if (element.innerHTML === undefined) { - continue; - } - - // Fix location.. - if (element.innerHTML.toLowerCase() === name) { - element.scrollIntoView({ behavior: "smooth" }); - //element.scrollTo({ - // top: element.offsetTop-offset, - // behavior: "smooth" - //}) - found = true; - //element.scrollTo({ - // top: element.offsetTop-100, - // behavior: "smooth" - //}) - } - } - } - } - //console.log(element) - - //console.log("NAME: ", name) - //console.log(document.body.innerHTML) - // parent = document.getElementById(parent); - - //var descendants = parent.getElementsByTagName(tagname); - - // this.scrollDiv.current.scrollIntoView({ behavior: 'smooth' }); - - //$(".parent").find("h2:contains('Statistics')").parent(); - }; - - if (serverside !== true && window.location.hash.length > 0) { - parseElementScroll(); - } - - const markdownStyle = { - color: "rgba(255, 255, 255, 0.90)", - overflow: "hidden", - paddingBottom: 100, - margin: "auto", - maxWidth: "100%", - minWidth: "100%", - overflow: "hidden", - fontSize: isMobile ? "1.3rem" : "1.1rem", - }; - - - - - const CustomButton = (props) => { - const { title, icon, link } = props - - const [hover, setHover] = useState(false) - - return ( - -
    { - if (link === "" || link === undefined) { - event.preventDefault() - console.log("IN CLICK!") - if (window.drift !== undefined) { - window.drift.api.startInteraction({ interactionId: 340043 }) - } else { - console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) - } - } else { - console.log("Link defined: ", link) - } - }} onMouseOver={() => { - setHover(true) - }} - onMouseOut={() => { - setHover(false); - }} - > - {icon} - - {title} - -
    -
    - ) - } - - - const DocumentationButton = (props) => { - const { item, link } = props + }, []) + //console.log("PARAMS: ", params) + const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false); + const [data, setData] = useState(""); + const [firstrequest, setFirstrequest] = useState(true); + const [list, setList] = useState([]); + const [isopen, setOpen] = useState(-1); const [hover, setHover] = useState(false); + const [, setListLoaded] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const [headingSet, setHeadingSet] = React.useState(false); + const [selectedMeta, setSelectedMeta] = React.useState({ + link: "hello", + read_time: 2, + }); + const [tocLines, setTocLines] = React.useState([]); + const [baseUrl, setBaseUrl] = React.useState( + serverside === true ? "" : window.location.href + ); - if (link === undefined || link === null) { - return null + function handleClick(event) { + setAnchorEl(event.currentTarget); } - return ( - -
    { - setHover(true) - }} - onMouseOut={() => { - setHover(false); - }} - > - - {item} - -
    - - ) - } + function handleMouseOver() { + setHover(!hover); + } - const headerStyle = { - marginTop: 25, - } + function handleClose() { + setAnchorEl(null); + } - const mainpageInfo = -
    - - Documentation - -
    - /> - link="https://discord.gg/B2CBzUm" /> -
    -
    - Tutorial - - Dive in. Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the getting started section to give it a go! - + const SidebarPaperStyle = { + backgroundColor: "rgb(26,26,26)", + backgroundImage: "none", + overflowX: "hidden", + position: "relative", + paddingLeft: 15, + paddingRight: 15, + minHeight: "80vh", + }; - Why Shuffle? - - Security first. We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you find usecases that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases. - - Get help - - Our promise is to make it easier and easier to automate your operations. In some cases however, it may be good with a helping hand. That's where Shuffle's consultancy and support services come in handy. We help you build and automate your operational processes to a level you haven't seen before with the help of our usecases. - + const Heading = (props) => { + const element = React.createElement( + `h${props.level}`, + { style: { marginTop: props.level === 1 ? 20 : 50 } }, + props.children + ); - APIs - - Learn. We're all about learning, and are continuously creating documentation and video tutorials to better understand how to get started. APIs are an extremely important part of how the internet works today, and our goal is helping every security professional learn about them. - + var extraInfo = ""; - Workflow building - - Build. Creating workflows has never been easier. Jump into things with our getting Started section and build to your hearts content. Workflows make it all come together, with an easy to use area. - + if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { + if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { + extraInfo = "" + } + } - Managing Shuffle - - Organize. Whether an organization of 1000 or 1, management tools are necessary. In Shuffle we offer full user management, MFA and single-signon options, multi-tenancy and a lot more - for free! - -
    -
    + return ( + { + setHover(true); + }} + > + {props.level !== 1 ? ( + + ) : null} + {element} + {extraInfo} + + ) + } - const markdownComponents = { - img: Img, - code: CodeHandler, - h1: Heading, - h2: Heading, - h3: Heading, - h4: Heading, - h5: Heading, - h6: Heading, - a: OuterLink, - } - // PostDataBrowser Section - const postDataBrowser = - list === undefined || list === null ? null : ( -
    -
    - - - {list.map((data, index) => { - const item = data.name; - if (item === undefined) { - return null; + const SideBar = { + width: "17%", + position: "sticky", + top: 50, + minHeight: "90vh", + maxHeight: "90vh", + overflowX: "hidden", + overflowY: "auto", + zIndex: 1000, + //borderRight: "1px solid rgba(255,255,255,0.3)", + }; + + const IndexBar = { + alignSelf: "flex-start", + position: "sticky", + top: 80, + overflow: "auto", + marginTop: 70, + } + + const fetchDocList = () => { + fetch(`${globalUrl}/api/v1/docs`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }) + .then((response) => response.json()) + .then((responseJson) => { + if (responseJson.success) { + setList(responseJson.list); + } else { + setList(["# Error loading documentation. Please contact us if this persists.",]); + toast("Failed loading documentation. Please reload the window") + } + setListLoaded(true); + }) + .catch((error) => { }); + }; + + const fetchDocs = (docId) => { + fetch(`${globalUrl}/api/v1/docs/${docId}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }) + .then((response) => response.json()) + .then((responseJson) => { + if (responseJson.success === false) { + //toast("Failed loading documentation. Please reload the UI") } - const path = "/docs/" + item; - const newname = - item.charAt(0).toUpperCase() + - item.substring(1).split("_").join(" ").split("-").join(" "); + if (responseJson.success && responseJson.reason !== undefined) { + // Find tags and translate them into ![]() format + const imgRegex = / - { - setTocLines([]); - fetchDocs(item); - handleCollapse(index); - }} - > - - {newname} - - {isopen === index ? : } - - {itemMatching && - tocLines !== null && - tocLines !== undefined && - tocLines.length > 0 ? ( - - {tocLines.map((data, index) => { + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) { + navigate("/docs") + return + } - return ( - - - {data.text} - - - ); - })} - - ) : null} - - ); - })} - - -
    -
    - {props.match.params.key === undefined ? - mainpageInfo - : -
    - - {data} - -
    - } -
    -
    - ); + if (responseJson.meta !== undefined) { + setSelectedMeta(responseJson.meta); + } - const mobileStyle = { - color: "white", - marginLeft: 25, - marginRight: 25, - paddingBottom: 50, - backgroundColor: "inherit", - display: "flex", - flexDirection: "column", - }; + //console.log("TOC list: ", responseJson.reason) + if ( + responseJson.reason !== undefined && + responseJson.reason !== null + ) { + const splitkey = responseJson.reason.split("\n"); + var innerTocLines = []; + var record = false; + for (var key in splitkey) { + const line = splitkey[key]; + //console.log("Line: ", line) + if (line.toLowerCase().includes("table of contents")) { + record = true; + continue; + } + if (record && line.length < 3) { + record = false; + } - const postDataMobile = - list === undefined || list === null ? null : ( -
    -
    - - - {list.map((data, index) => { - const item = data.name; - if (item === undefined) { - return null; - } + if (record) { + const parsedline = line.split("]("); + if (parsedline.length > 1) { + parsedline[0] = parsedline[0].replaceAll("*", ""); + parsedline[0] = parsedline[0].replaceAll("[", ""); + parsedline[0] = parsedline[0].replaceAll("]", ""); + parsedline[0] = parsedline[0].replaceAll("(", ""); + parsedline[0] = parsedline[0].replaceAll(")", ""); + parsedline[0] = parsedline[0].trim(); - const path = "/docs/" + item; - const newname = - item.charAt(0).toUpperCase() + - item.substring(1).split("_").join(" ").split("-").join(" "); - return ( - { - window.location.pathname = path; - }} - > - {newname} - - ); - })} - -
    - {props.match.params.key === undefined ? - mainpageInfo - : -
    - - {data} - -
    + parsedline[1] = parsedline[1].replaceAll("*", ""); + parsedline[1] = parsedline[1].replaceAll("[", ""); + parsedline[1] = parsedline[1].replaceAll("]", ""); + parsedline[1] = parsedline[1].replaceAll(")", ""); + parsedline[1] = parsedline[1].replaceAll("(", ""); + parsedline[1] = parsedline[1].trim(); + //console.log(parsedline[0], parsedline[1]) + + innerTocLines.push({ + text: parsedline[0], + link: parsedline[1], + }); + } else { + console.log("Bad line for parsing: ", line); + } + } + } + + setTocLines(innerTocLines); + } + } else { + setData("# Error\nThis page doesn't exist."); + } + }) + .catch((error) => { }); + }; + + if (firstrequest) { + setFirstrequest(false); + if (!serverside) { + if (window.innerWidth < 768) { + setMobile(true); + } } - - -
    + + if (selectedDoc !== undefined) { + setData(selectedDoc.reason); + setList(selectedDoc.list); + setListLoaded(true); + } else { + if (!serverside) { + fetchDocList(); + + //const propkey = props.match.params.key + //if (propkey === undefined) { + // navigate("/docs/about") + // return null + //} + // + if (props.match.params.key === undefined) { + + } else { + console.log("DOCID: ", props.match.params.key) + fetchDocs(props.match.params.key) + } + } + } + } + + // Handles search-based changes that origin from outside this file + if (serverside !== true && window.location.href !== baseUrl) { + setBaseUrl(window.location.href); + fetchDocs(props.match.params.key); + } + + const parseElementScroll = () => { + const offset = 45; + var parent = document.getElementById("markdown_wrapper_outer"); + if (parent !== null) { + //console.log("IN PARENT") + var elements = parent.getElementsByTagName("h2"); + + const name = window.location.hash + .slice(1, window.location.hash.length) + .toLowerCase() + .split("%20") + .join(" ") + .split("_") + .join(" ") + .split("-") + .join(" ") + .split("?")[0] + + //console.log(name) + var found = false; + for (var key in elements) { + const element = elements[key]; + if (element.innerHTML === undefined) { + continue; + } + + // Fix location.. + if (element.innerHTML.toLowerCase() === name) { + //console.log(element.offsetTop) + element.scrollIntoView({ behavior: "smooth" }); + //element.scrollTo({ + // top: element.offsetTop+offset, + // behavior: "smooth" + //}) + found = true; + //element.scrollTo({ + // top: element.offsetTop-100, + // behavior: "smooth" + //}) + } + } + + // H# + if (!found) { + elements = parent.getElementsByTagName("h3"); + //console.log("NAMe: ", name) + found = false; + for (key in elements) { + const element = elements[key]; + if (element.innerHTML === undefined) { + continue; + } + + // Fix location.. + if (element.innerHTML.toLowerCase() === name) { + element.scrollIntoView({ behavior: "smooth" }); + //element.scrollTo({ + // top: element.offsetTop-offset, + // behavior: "smooth" + //}) + found = true; + //element.scrollTo({ + // top: element.offsetTop-100, + // behavior: "smooth" + //}) + } + } + } + } + //console.log(element) + + //console.log("NAME: ", name) + //console.log(document.body.innerHTML) + // parent = document.getElementById(parent); + + //var descendants = parent.getElementsByTagName(tagname); + + // this.scrollDiv.current.scrollIntoView({ behavior: 'smooth' }); + + //$(".parent").find("h2:contains('Statistics')").parent(); + }; + + if (serverside !== true && window.location.hash.length > 0) { + parseElementScroll(); + } + + const markdownStyle = { + color: "rgba(255, 255, 255, 0.90)", + overflow: "hidden", + paddingBottom: 100, + margin: "auto", + maxWidth: "100%", + minWidth: "100%", + overflow: "hidden", + fontSize: isMobile ? "1.3rem" : "1.1rem", + }; + + + + + const CustomButton = (props) => { + const { title, icon, link } = props + + return ( + +
    { + if (link === "" || link === undefined) { + event.preventDefault() + console.log("IN CLICK!") + if (window.drift !== undefined) { + window.drift.api.startInteraction({ interactionId: 340043 }) + } else { + console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) + } + } else { + console.log("Link defined: ", link) + } + }} onMouseOver={() => { + setHover(true) + }} + onMouseOut={() => { + setHover(false); + }} + > + {icon} + + {title} + +
    +
    + ) + } + + + const DocumentationButton = (props) => { + const { item, link } = props + + + if (link === undefined || link === null) { + return null + } + + return ( + +
    { + setHover(true) + }} + onMouseOut={() => { + setHover(false); + }} + > + + {item} + +
    + + ) + } + + const headerStyle = { + marginTop: 25, + } + + const mainpageInfo = +
    + + Documentation + +
    + /> + link="https://discord.gg/B2CBzUm" /> +
    + +
    + Tutorial + + Dive in. Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the getting started section to give it a go! + + + Why Shuffle? + + Security first. We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you find usecases that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases. + + + Get help + + Our promise is to make it easier and easier to automate your operations. In some cases however, it may be good with a helping hand. That's where Shuffle's consultancy and support services come in handy. We help you build and automate your operational processes to a level you haven't seen before with the help of our usecases. + + + APIs + + Learn. We're all about learning, and are continuously creating documentation and video tutorials to better understand how to get started. APIs are an extremely important part of how the internet works today, and our goal is helping every security professional learn about them. + + + Workflow building + + Build. Creating workflows has never been easier. Jump into things with our getting Started section and build to your hearts content. Workflows make it all come together, with an easy to use area. + + + Managing Shuffle + + Organize. Whether an organization of 1000 or 1, management tools are necessary. In Shuffle we offer full user management, MFA and single-signon options, multi-tenancy and a lot more - for free! + +
    +
    + + const markdownComponents = { + img: Img, + code: CodeHandler, + h1: Heading, + h2: Heading, + h3: Heading, + h4: Heading, + h5: Heading, + h6: Heading, + a: OuterLink, + } + + + // PostDataBrowser Section + const postDataBrowser = + list === undefined || list === null ? null : ( +
    +
    + + + {list.map((data, index) => { + const item = data.name; + if (item === undefined) { + return null; + } + + const path = "/docs/" + item; + const newname = + item.charAt(0).toUpperCase() + + item.substring(1).split("_").join(" ").split("-").join(" "); + + const itemMatching = props.match.params.key === undefined ? false : + props.match.params.key.toLowerCase() === item.toLowerCase(); + return ( +
  • + + + {newname} + + +
  • + ); + })} +
    +
    +
    +
    + {props.match.params.key === undefined ? + mainpageInfo + : +
    + + {data} + +
    + } +
    + +
    + ); + + const mobileStyle = { + color: "white", + marginLeft: 25, + marginRight: 25, + paddingBottom: 50, + backgroundColor: "inherit", + display: "flex", + flexDirection: "column", + }; + + + const postDataMobile = + list === undefined || list === null ? null : ( +
    +
    + + {list.map((data, index) => { + const item = data.name; + if (item === undefined) { + return null; + } + + const path = "/docs/" + item; + const newname = + item.charAt(0).toUpperCase() + + item.substring(1).split("_").join(" ").split("-").join(" "); + return ( + { + window.location.pathname = path; + }} + > + {newname} + + ); + })} +
    + {props.match.params.key === undefined ? + mainpageInfo + : +
    + + {data} + +
    + } + + +
    + ); + + // Padding and zIndex etc set because of footer in cloud. + const loadedCheck = ( +
    + {postDataBrowser} + {postDataMobile} +
    ); - // Padding and zIndex etc set because of footer in cloud. - const loadedCheck = ( -
    - {postDataBrowser} - {postDataMobile} -
    - ); - - return
    {loadedCheck}
    ; + return
    {loadedCheck}
    ; }; export default Docs; From f4572c161f8ca26cb9358072c940b25167c275d1 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 6 May 2024 23:04:37 +0200 Subject: [PATCH 100/142] Added subpath rewrite script --- frontend/enable_subpathing.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 frontend/enable_subpathing.sh diff --git a/frontend/enable_subpathing.sh b/frontend/enable_subpathing.sh new file mode 100644 index 00000000..a6f76db4 --- /dev/null +++ b/frontend/enable_subpathing.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Sekerovic Dragan, oneStep2 GmbH - 2023-09-08 - quick and dirty +# purpose of the script: make shuffle reverse proxyable/run shuffle on a subpath instead of root + +if $(ls frontend/src/App.jsx.orig.* 1>/dev/null 2>&1); then + echo "shuffle src already patched for subpathing!" + echo "Aborting ..." + exit 1 +fi + +[[ "$1" != "" ]] && SUBPATH=$1 || SUBPATH="/shuffle" + +cat $0.tpl | sed "s,SUBPATH,$SUBPATH," > $0.run +chmod 700 $0.run +./$0.run +rm ./$0.run + +exit 0 From 49cfd3c838e76b2b4612fe145863d4666e522354 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 7 May 2024 10:21:28 +0000 Subject: [PATCH 101/142] feat: Add caching for container status check --- functions/onprem/orborus/orborus.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index acc31e96..ffe90213 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -2048,10 +2048,10 @@ func handlePipeline(incRequest shuffle.ExecutionRequest) error { //err := deployPipeline(image, identifier, command) pipelineId, err := createPipeline(command, identifier) if err != nil { - log.Printf("[ERROR] Failed to deploy pipeline: %s", err) + log.Printf("[ERROR] Failed to create pipeline: %s", err) return err } else { - log.Printf("[INFO] Pipeline deployed successfully with Id: %s", pipelineId) + log.Printf("[INFO] Pipeline created successfully with Id: %s", pipelineId) newErr := savePipelineData(pipelineId, identifier, "running") if newErr != nil { log.Printf("[DEBUG] failed to save the pipeline data: %s", newErr) @@ -2106,11 +2106,17 @@ func deployTenzirNode() error { } ctx := context.Background() + cacheKey := "tenzir-key" imageName := "tenzir/tenzir:latest" containerName := "tenzir-node" containerStartOptions := container.StartOptions{} + _, err := shuffle.GetCache(ctx, cacheKey) + if err == nil { + return nil + } + containerInfo, err := dockercli.ContainerInspect(ctx, containerName) if err != nil { if dockerclient.IsErrNotFound(err) { @@ -2138,17 +2144,31 @@ func deployTenzirNode() error { return err } log.Printf("[INFO] Tenzir Node container started successfully") + log.Printf("[INFO] Waiting for Tenzir to become available ...") err = checkTenzirNode() if err != nil { return err } log.Printf("[INFO] Successfully deployed Tenzir Node!") - } else { - log.Printf("[DEBUG] Tenzir Node Container already running") } } + tenzirStatus := struct { + ContainerStatus string `json:"container_status"` + }{ + ContainerStatus: "running", + } + + cacheData, err := json.Marshal(tenzirStatus) + if err != nil { + log.Printf("[WARNING] Failed marshalling execution: %s", err) + } + err = shuffle.SetCache(ctx, cacheKey, cacheData, 1) + if err != nil { + log.Printf("[WARNING] Failed updating cache for tenzir: %s", err) + } + return nil } From a4d4c34006b5a5831058fe2b1f483d33bafd6d34 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 7 May 2024 10:55:52 +0000 Subject: [PATCH 102/142] identation fix --- functions/onprem/orborus/orborus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index ffe90213..6a8e260e 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -2112,7 +2112,7 @@ func deployTenzirNode() error { containerName := "tenzir-node" containerStartOptions := container.StartOptions{} - _, err := shuffle.GetCache(ctx, cacheKey) + _, err := shuffle.GetCache(ctx, cacheKey) if err == nil { return nil } From e358824ae060a39a5d93b541bcef302f9e613d3c Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 7 May 2024 22:46:36 +0200 Subject: [PATCH 103/142] Create enable_subpathing.sh.tpl --- frontend/enable_subpathing.sh.tpl | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 frontend/enable_subpathing.sh.tpl diff --git a/frontend/enable_subpathing.sh.tpl b/frontend/enable_subpathing.sh.tpl new file mode 100644 index 00000000..04680e38 --- /dev/null +++ b/frontend/enable_subpathing.sh.tpl @@ -0,0 +1,139 @@ +#!/bin/bash + +cd frontend + +cd src +cp App.jsx App.jsx.orig.`date -I` +sed 's,startsWith("/,startsWith("SUBPATH/,g' -i App.jsx +sed 's,path="/,path="SUBPATH/,g' -i App.jsx +sed 's,location = "/,location = "SUBPATH/,g' -i App.jsx +sed 's,window.location.origin;,"SUBPATH";,' -i App.jsx + +cd views +for d in $(ls *.jsx); do cp $d $d.orig.`date -I`; done +sed 's,pathname = "/,pathname = "SUBPATH/,g' -i *.jsx +sed 's,navigate("/,navigate("SUBPATH/,g' -i *.jsx +sed 's,navigate(`/,navigate(`SUBPATH/,g' -i *.jsx +sed 's,href="/,href="SUBPATH/,g' -i *.jsx +sed 's,path = "/,path = "SUBPATH/,g' -i *.jsx +sed 's,link={"/,link={"SUBPATH/,g' -i *.jsx +sed 's,to="/,to="SUBPATH/,g' -i *.jsx +sed 's,to={"/,to={"SUBPATH/,g' -i *.jsx +sed 's,${window.location.origin},SUBPATH,g' -i *.jsx +cd .. + +cd components +for d in $(ls *.jsx); do cp $d $d.orig.`date -I`; done +sed 's,to="/,to="SUBPATH/,g' -i *.jsx +sed 's,${window.location.origin},SUBPATH,g' -i *.jsx +cd ../.. + +cd confd/templates +cp nginx.conf nginx.conf.orig.`date -I` +sed 's,location / {,rewrite ^SUBPATH(/api/v1.*)$ $1 last;\n\n\t\tlocation / {,' -i nginx.conf +cd ../.. + +docker build -t ghcr.io/shuffle/shuffle-frontend:latest . + + +cat << EOF + +If you want to reverse proxy shuffle using subpath "SUBPATH" you need to configure +your web server - in this case nginx - to do the reverse proxying by at least adding + + location SUBPATH { + proxy_pass http://snooss-proxy:3421/; + rewrite SUBPATH/(.*)$ /$1 break; + + proxy_read_timeout 300s; + + # proxy header + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # substitute html content response + proxy_set_header Accept-Encoding ""; # no compression allowed or sub_filter won't work + sub_filter 'href="/' 'href="SUBPATH/'; + sub_filter 'href:"/' 'href:"SUBPATH/'; + sub_filter '/static' 'SUBPATH/static'; + sub_filter '/images' 'SUBPATH/images'; + sub_filter_types *; + sub_filter_once off; + } + +to your nginx configuration. +EOF + + +exit 0#!/bin/bash + +cd frontend + +cd src +cp App.jsx App.jsx.orig.`date -I` +sed 's,startsWith("/,startsWith("SUBPATH/,g' -i App.jsx +sed 's,path="/,path="SUBPATH/,g' -i App.jsx +sed 's,location = "/,location = "SUBPATH/,g' -i App.jsx +sed 's,window.location.origin;,"SUBPATH";,' -i App.jsx + +cd views +for d in $(ls *.jsx); do cp $d $d.orig.`date -I`; done +sed 's,pathname = "/,pathname = "SUBPATH/,g' -i *.jsx +sed 's,navigate("/,navigate("SUBPATH/,g' -i *.jsx +sed 's,navigate(`/,navigate(`SUBPATH/,g' -i *.jsx +sed 's,href="/,href="SUBPATH/,g' -i *.jsx +sed 's,path = "/,path = "SUBPATH/,g' -i *.jsx +sed 's,link={"/,link={"SUBPATH/,g' -i *.jsx +sed 's,to="/,to="SUBPATH/,g' -i *.jsx +sed 's,to={"/,to={"SUBPATH/,g' -i *.jsx +sed 's,${window.location.origin},SUBPATH,g' -i *.jsx +cd .. + +cd components +for d in $(ls *.jsx); do cp $d $d.orig.`date -I`; done +sed 's,to="/,to="SUBPATH/,g' -i *.jsx +sed 's,${window.location.origin},SUBPATH,g' -i *.jsx +cd ../.. + +cd confd/templates +cp nginx.conf nginx.conf.orig.`date -I` +sed 's,location / {,rewrite ^SUBPATH(/api/v1.*)$ $1 last;\n\n\t\tlocation / {,' -i nginx.conf +cd ../.. + +docker build -t ghcr.io/shuffle/shuffle-frontend:latest . + + +cat << EOF + +If you want to reverse proxy shuffle using subpath "SUBPATH" you need to configure +your web server - in this case nginx - to do the reverse proxying by at least adding + + location SUBPATH { + proxy_pass http://snooss-proxy:3421/; + rewrite SUBPATH/(.*)$ /$1 break; + + proxy_read_timeout 300s; + + # proxy header + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # substitute html content response + proxy_set_header Accept-Encoding ""; # no compression allowed or sub_filter won't work + sub_filter 'href="/' 'href="SUBPATH/'; + sub_filter 'href:"/' 'href:"SUBPATH/'; + sub_filter '/static' 'SUBPATH/static'; + sub_filter '/images' 'SUBPATH/images'; + sub_filter_types *; + sub_filter_once off; + } + +to your nginx configuration. +EOF + + +exit 0 From 2278b98e81a89d99b50cc548569e52fae37878a7 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Wed, 8 May 2024 08:05:28 +0000 Subject: [PATCH 104/142] sub-contents --- frontend/src/views/Docs.jsx | 177 ++++++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 60 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 9835d947..e2b7e489 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -54,6 +54,28 @@ const hrefStyle = { textDecoration: "none", }; +const hrefStyleToc = { + color: "rgba(255, 255, 255, 0.6)", + textDecoration: "none", + fontSize: "14px", + fontWeight: 400, + padding: "4px 0", + paddingLeft: "8px", + paddingRight: "8px", + lineHeight: "20px", +}; + + +const hrefStyleToc2 = { + color: "rgba(255, 255, 255, 0.6)", + textDecoration: "none", + fontSize: "14px", + fontWeight: 400, + padding: "4px 0", + paddingLeft: "12px", + paddingRight: "12px", + lineHeight: "20px", +}; const hrefStyle2 = { color: "#f86a3e", @@ -66,6 +88,9 @@ const innerHrefStyle = { }; + + + export const CopyToClipboard = (props) => { const { text, style, onCopy } = props; const parsedstyle = style !== undefined ? style : { @@ -232,6 +257,10 @@ const Docs = (defaultprops) => { setAnchorEl(event.currentTarget); } + function handleCollapse(index) { + setOpen(isopen === index ? -1 : index) + } + function handleMouseOver() { setHover(!hover); } @@ -240,6 +269,42 @@ const Docs = (defaultprops) => { setAnchorEl(null); } + function tocvalue(markdown) { + const items = []; + let currentMainItem = null; + + const lines = markdown.split('\n'); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith('* [')) { + const matches = line.match(/^\* \[([^)]+)\]\(#([^)]+)\)/); + if (matches) { + currentMainItem = { + id: matches[2], + title: matches[1], + items: [], + }; + items.push(currentMainItem); + } + } else if (line.startsWith(' * [')) { + if (currentMainItem) { + const matches = line.match(/^ \* \[([^)]+)\]\(#([^)]+)\)/); + if (matches) { + currentMainItem.items.push({ + id: matches[2], + title: matches[1], + }); + } + } + } else if (line.startsWith('##')) { + continue + } + } + + return items + } + const SidebarPaperStyle = { backgroundColor: "rgb(26,26,26)", @@ -293,8 +358,8 @@ const Docs = (defaultprops) => { width: "17%", position: "sticky", top: 50, - minHeight: "90vh", - maxHeight: "90vh", + minHeight: "100vh", + maxHeight: "100vh", overflowX: "hidden", overflowY: "auto", zIndex: 1000, @@ -350,7 +415,6 @@ const Docs = (defaultprops) => { const tocRegex = /^## Table of contents[\s\S]*?(?=^##\s|\Z)|^\* \[[^\]]+\]\([^)]+\)\n?(?![^\n]+\]\([^)]+\))/gm; const newdata = responseJson.reason.replace(imgRegex, '![]($1)') .replace(tocRegex, ""); - console.log(newdata) setData(newdata); if (docId === undefined) { document.title = "Shuffle documentation introduction"; @@ -372,50 +436,10 @@ const Docs = (defaultprops) => { responseJson.reason !== undefined && responseJson.reason !== null ) { - const splitkey = responseJson.reason.split("\n"); - var innerTocLines = []; - var record = false; - for (var key in splitkey) { - const line = splitkey[key]; - //console.log("Line: ", line) - if (line.toLowerCase().includes("table of contents")) { - record = true; - continue; - } - - if (record && line.length < 3) { - record = false; - } - - if (record) { - const parsedline = line.split("]("); - if (parsedline.length > 1) { - parsedline[0] = parsedline[0].replaceAll("*", ""); - parsedline[0] = parsedline[0].replaceAll("[", ""); - parsedline[0] = parsedline[0].replaceAll("]", ""); - parsedline[0] = parsedline[0].replaceAll("(", ""); - parsedline[0] = parsedline[0].replaceAll(")", ""); - parsedline[0] = parsedline[0].trim(); - - parsedline[1] = parsedline[1].replaceAll("*", ""); - parsedline[1] = parsedline[1].replaceAll("[", ""); - parsedline[1] = parsedline[1].replaceAll("]", ""); - parsedline[1] = parsedline[1].replaceAll(")", ""); - parsedline[1] = parsedline[1].replaceAll("(", ""); - parsedline[1] = parsedline[1].trim(); - //console.log(parsedline[0], parsedline[1]) - - innerTocLines.push({ - text: parsedline[0], - link: parsedline[1], - }); - } else { - console.log("Bad line for parsing: ", line); - } - } - } - - setTocLines(innerTocLines); + const values = tocvalue(responseJson.reason.match(tocRegex) + .join() + .toString()); + setTocLines(values); } } else { setData("# Error\nThis page doesn't exist."); @@ -758,19 +782,52 @@ const Docs = (defaultprops) => {
    }
    - -
    +
    +

    Table Of Content

    + +
    + +
    ); const mobileStyle = { From 4661dc2c753b6f63cdc8a7548228a4f4c0f0f928 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 8 May 2024 13:30:48 +0200 Subject: [PATCH 105/142] Fixed some loading problems for images & changed Workflow UI slightly --- frontend/src/components/AppFramework.jsx | 68 +- frontend/src/components/AppGrid.jsx | 971 ++++++++++-------- frontend/src/components/CacheView.jsx | 2 +- frontend/src/components/ConfigureWorkflow.jsx | 12 +- frontend/src/components/NewHeader.jsx | 753 +++++++------- frontend/src/components/OrgHeader.jsx | 16 +- frontend/src/components/ParsedAction.jsx | 168 ++- frontend/src/components/Priorities.jsx | 95 +- .../src/components/WorkflowTemplatePopup.jsx | 45 +- frontend/src/defaultCytoscapeStyle.jsx | 41 +- frontend/src/theme.jsx | 7 +- frontend/src/views/Admin.jsx | 54 +- frontend/src/views/AngularWorkflow.jsx | 566 ++++++---- frontend/src/views/AppCreator.jsx | 8 +- frontend/src/views/Apps.jsx | 13 +- frontend/src/views/Search.jsx | 15 +- frontend/src/views/Workflows.jsx | 40 +- 17 files changed, 1669 insertions(+), 1205 deletions(-) diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index d119e708..0d31ce9d 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -35,7 +35,7 @@ import edgehandles from "cytoscape-edgehandles"; import cytoscape from "cytoscape"; import { toast } from 'react-toastify'; -cytoscape.use(edgehandles); +cytoscape.use(edgehandles) export const findSpecificApp = (framework, inputcategory) => { // Get the frameworkinfo for the org and fill in @@ -59,7 +59,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "EDR :default", - large_image: parsedDatatypeImages["EDR & AV"], + large_image: parsedDatatypeImages()["EDR & AV"], count: 0, description: "", id: "", @@ -71,7 +71,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "COMMS :default", - large_image: parsedDatatypeImages["COMMS"], + large_image: parsedDatatypeImages()["COMMS"], count: 0, description: "", id: "", @@ -83,7 +83,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "COMMS :default", - large_image: parsedDatatypeImages["COMMS"], + large_image: parsedDatatypeImages()["COMMS"], count: 0, description: "", id: "", @@ -95,7 +95,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "ASSETS :default", - large_image: parsedDatatypeImages["ASSETS"], + large_image: parsedDatatypeImages()["ASSETS"], count: 0, description: "", id: "", @@ -107,7 +107,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "CASES :default", - large_image: parsedDatatypeImages["CASES"], + large_image: parsedDatatypeImages()["CASES"], count: 0, description: "", id: "", @@ -119,7 +119,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "IAM :default", - large_image: parsedDatatypeImages["IAM"], + large_image: parsedDatatypeImages()["IAM"], count: 0, description: "", id: "", @@ -131,7 +131,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "Network :default", - large_image: parsedDatatypeImages["NETWORK"], + large_image: parsedDatatypeImages()["NETWORK"], count: 0, description: "", id: "", @@ -143,7 +143,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "INTEL :default", - large_image: parsedDatatypeImages["INTEL"], + large_image: parsedDatatypeImages()["INTEL"], count: 0, description: "", id: "", @@ -155,7 +155,7 @@ export const findSpecificApp = (framework, inputcategory) => { return { name: "SIEM :default", - large_image: parsedDatatypeImages["SIEM"], + large_image: parsedDatatypeImages()["SIEM"], count: 0, description: "", id: "", @@ -168,24 +168,30 @@ export const findSpecificApp = (framework, inputcategory) => { } const svgSize = "40px" -export const parsedDatatypeImages = { - "SIEM": encodeURI(`data:image/svg+xml;utf-8,`), +export const parsedDatatypeImages = () => { + const isWorkflow = window.location.pathname.includes("/workflows/") + const svgSize = isWorkflow ? "24px" : "40px" + const colorfill = isWorkflow ? "rgb(240,240,240)" : "rgb(248,90,62)" - "CASES": encodeURI(`data:image/svg+xml;utf-8,`), + return { + "SIEM": encodeURI(`data:image/svg+xml;utf-8,`), - "EDR & AV": encodeURI(`data:image/svg+xml;utf-8,`), + "CASES": encodeURI(`data:image/svg+xml;utf-8,`), - "INTEL": encodeURI(`data:image/svg+xml;utf-8,`), + "EDR & AV": encodeURI(`data:image/svg+xml;utf-8,`), - "COMMS": encodeURI(`data:image/svg+xml;utf-8,`), + "INTEL": encodeURI(`data:image/svg+xml;utf-8,`), - "NETWORK": encodeURI(`data:image/svg+xml;utf-8,`), + "COMMS": encodeURI(`data:image/svg+xml;utf-8,`), + + "NETWORK": encodeURI(`data:image/svg+xml;utf-8,`), - "INTEL": encodeURI(`data:image/svg+xml;utf-8,`), + "INTEL": encodeURI(`data:image/svg+xml;utf-8,`), - "ASSETS": encodeURI(`data:image/svg+xml;utf-8,`), + "ASSETS": encodeURI(`data:image/svg+xml;utf-8,`), - "IAM": encodeURI(`data:image/svg+xml;utf-8,`), + "IAM": encodeURI(`data:image/svg+xml;utf-8,`), + } } export const usecases = { @@ -1506,7 +1512,7 @@ const AppFramework = (props) => { margin_y: casescheck ? `${19*scale}px` : `0px`, width: casescheck ? iconSize : defaultSize, height: casescheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image, + large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages()["CASES"] : parsedFrameworkData.Cases.large_image, label: securityFramework[0].text.toUpperCase(), id: securityFramework[0].text.toUpperCase(), @@ -1535,7 +1541,7 @@ const AppFramework = (props) => { margin_y: iamcheck ? `${19*scale}px` : `0px`, width: iamcheck ? iconSize : defaultSize, height: iamcheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image, + large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages()["IAM"] : parsedFrameworkData.IAM.large_image, label: securityFramework[3].text.toUpperCase(), id: securityFramework[3].text.toUpperCase(), @@ -1563,7 +1569,7 @@ const AppFramework = (props) => { margin_y: assetscheck ? `${19*scale}px` : `0px`, width: assetscheck ? iconSize : defaultSize, height: assetscheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image, + large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages()["ASSETS"] : parsedFrameworkData.Assets.large_image, label: securityFramework[2].text.toUpperCase(), id: securityFramework[2].text.toUpperCase(), @@ -1591,7 +1597,7 @@ const AppFramework = (props) => { margin_y: intelcheck ? `${19*scale}px` : `0px`, width: intelcheck ? iconSize : defaultSize, height: intelcheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image, + large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages()["INTEL"] : parsedFrameworkData.Intel.large_image, label: securityFramework[4].text.toUpperCase(), id: securityFramework[4].text.toUpperCase(), @@ -1619,7 +1625,7 @@ const AppFramework = (props) => { margin_y: commscheck ? `${19*scale}px` : `0px`, width: commscheck ? iconSize : defaultSize, height: commscheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image, + large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages()["COMMS"] : parsedFrameworkData.Comms.large_image, label: securityFramework[5].text.toUpperCase(), id: securityFramework[5].text.toUpperCase(), @@ -1647,7 +1653,7 @@ const AppFramework = (props) => { margin_y: edrcheck ? `${19*scale}px` : `0px`, width: edrcheck ? iconSize : defaultSize, height: edrcheck ? iconSize : defaultSize, - large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image, + large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages()["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image, label: securityFramework[7].text.toUpperCase(), id: securityFramework[7].text.toUpperCase(), @@ -1675,7 +1681,7 @@ const AppFramework = (props) => { margin_y: networkcheck ? `${19*scale}px` : `0px`, width: networkcheck ? iconSize : defaultSize, height: networkcheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image, + large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages()["NETWORK"] : parsedFrameworkData.Network.large_image, label: securityFramework[6].text.toUpperCase(), id: securityFramework[6].text.toUpperCase(), animate: false, @@ -1702,7 +1708,7 @@ const AppFramework = (props) => { margin_y: siemcheck ? `${19*scale}px` : `0px`, width: siemcheck ? iconSize : defaultSize, height: siemcheck ? iconSize : defaultSize, - large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image, + large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages()["SIEM"] : parsedFrameworkData.SIEM.large_image, label: securityFramework[1].text.toUpperCase(), id: securityFramework[1].text.toUpperCase(), animate: false, @@ -1838,8 +1844,8 @@ const AppFramework = (props) => { const UsecaseHandler = (props) => { const { data, index, diff } = props - const leftImage = data.left_image !== undefined ? parsedDatatypeImages[data.left_image.toUpperCase()] : undefined - const rightImage = data.right_image !== undefined ? parsedDatatypeImages[data.right_image.toUpperCase()] : undefined + const leftImage = data.left_image !== undefined ? parsedDatatypeImages()[data.left_image.toUpperCase()] : undefined + const rightImage = data.right_image !== undefined ? parsedDatatypeImages()[data.right_image.toUpperCase()] : undefined if (leftImage === undefined) { console.log("LEFT MISSING: ", leftImage) @@ -2220,7 +2226,7 @@ const AppFramework = (props) => { if (foundelement !== undefined && foundelement !== null) { //console.log("element: ", foundelement) //console.log("DISC: ", discoveryData) - foundelement.data("large_image", parsedDatatypeImages[discoveryData.id.toUpperCase()]) + foundelement.data("large_image", parsedDatatypeImages()[discoveryData.id.toUpperCase()]) foundelement.data("text_margin_y", "14px") foundelement.data("margin_x", "32px") foundelement.data("margin_y", "19x") diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index a9581a27..dfd13210 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -35,6 +35,7 @@ import { } from "react-instantsearch-dom"; import aa from "search-insights"; +import { useLocation } from 'react-router-dom'; import "./FilterCSS.css"; @@ -91,7 +92,6 @@ const AppGrid = (props) => { window.title = "Shuffle | Apps | Find and integrate any app"; const noImage = "/public/no_image.png"; - const submitContact = (email, message) => { const data = { firstname: "", @@ -131,10 +131,9 @@ const AppGrid = (props) => { }); }; - const SearchBox = ({ currentRefinement, refine, isSearchStalled }) => { + const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, setSearchQuery }) => { var defaultSearch = ""; - var [searchQuery, setSearchQuery] = useState(""); //useEffect(() => { if ( @@ -241,11 +240,31 @@ const AppGrid = (props) => { }; const [currTab, setCurrTab] = useState(0); + const location = useLocation(); - const handleTabChange = (event, newValue) => { - setCurrTab(newValue); + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const tabParam = queryParams.get('tab'); + if (tabParam === 'org_apps') { + setCurrTab(1); + } else if (tabParam === 'my_apps') { + setCurrTab(2); + } else { + setCurrTab(0); + } + }, [location.search]); + + const handleTabChange = (event, newTab) => { + setCurrTab(newTab); + const newQueryParam = newTab === 0 ? 'all_apps' : newTab === 1 ? 'org_apps' : 'my_apps'; + const queryParams = new URLSearchParams(location.search); + queryParams.set('tab', newQueryParam); + queryParams.delete('q'); + window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); }; + + const [isLoggedIn, setIsLoggedIn] = useState(false); const [isLoading, setIsLoading] = useState(true) @@ -253,7 +272,8 @@ const AppGrid = (props) => { const Hits = ({ hits, insights, - setIsAnyAppActivated + setIsAnyAppActivated, + searchQuery }) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; @@ -282,9 +302,9 @@ const AppGrid = (props) => { .then(response => response.json()) .then(responseJson => { if (responseJson.success) { - setIsLoggedIn(true); setUserdata(responseJson); setAllActivatedAppIds(responseJson.active_apps) + setIsLoggedIn(true); } else { setIsLoggedIn(false); } @@ -355,225 +375,241 @@ const AppGrid = (props) => { transition: 'background-color 0.3s ease', }; + const [showNoAppFound, setShowNoAppFound] = useState(false); + + //show some delay to show the "App Not Found." so it doesn't not show while changing tab. + useEffect(() => { + const timer = setTimeout(() => { + setShowNoAppFound(true); + }, 1000); + return () => clearTimeout(timer); + }, []); + return (
    {!isLoading ? ( - -
    - {hits.map((data, index) => { - const appUrl = - isCloud - ? `/apps/${data.objectID}?queryID=${data.__queryID}` - : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; +
    + {hits.length === 0 && searchQuery.length >= 0 && showNoAppFound ? ( + No App Found + ) : ( + +
    + {hits.map((data, index) => { + const appUrl = + isCloud + ? `/apps/${data.objectID}?queryID=${data.__queryID}` + : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; - return ( - - - - { - setMouseHoverIndex(index); - }} - onMouseLeave={() => { - setMouseHoverIndex(-1); - }} - > - - ) : ( - +
    + {mouseHoverIndex === index && isCloud && ( +
    + {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( + + ) : ( + + )} +
    )}
    - )} +
    -
    -
    - - - - - - ); - }) - } -
    - + + + + + + ); + }) + } +
    + + )} +
    ) : (
    )} @@ -631,7 +667,6 @@ const AppGrid = (props) => { )} -
    @@ -868,6 +903,7 @@ const AppGrid = (props) => { //Component to display all apps. const AllApps = ({ setIsAnyAppActivated }) => { + var [searchQuery, setSearchQuery] = useState(""); return (
    { height: "100%", }} > - +
    ); @@ -889,13 +926,6 @@ const AppGrid = (props) => { //Search box for the orgs and users apps const SearchBoxForOrgAndUserApp = ({ searchQuery, setSearchQuery }) => { - const updateUrl = (query) => { - const urlSearchParams = new URLSearchParams(window.location.search); - urlSearchParams.set("q", query); - const newUrl = `${window.location.pathname}?${urlSearchParams.toString()}`; - window.history.pushState({ path: newUrl }, "", newUrl); - }; - return ( { }} onClick={() => { setSearchQuery(''); - updateUrl(''); }} /> )} @@ -964,7 +993,6 @@ const AppGrid = (props) => { id="shuffle_search_field" onChange={(event) => { setSearchQuery(event.currentTarget.value); - updateUrl(event.currentTarget.value); }} limit={5} /> @@ -974,7 +1002,8 @@ const AppGrid = (props) => { } - const [userAndOrgsApp, setUserAndOrgsApp] = useState([]); + const [userApps, setUserApps] = useState([]); + const [orgApps, setOrgApps] = useState([]); useEffect(() => { if (currTab === 2) { @@ -989,8 +1018,8 @@ const AppGrid = (props) => { }) .then((response) => response.json()) .then((data) => { - setUserAndOrgsApp(data); - setIsLoading(false) + setUserApps(data); + setIsLoading(false); }) .catch((err) => { console.error("Error fetching user apps:", err); @@ -1007,7 +1036,7 @@ const AppGrid = (props) => { }) .then((response) => response.json()) .then((data) => { - setUserAndOrgsApp(data); + setOrgApps(data); setIsLoading(false) }) .catch((err) => { @@ -1015,7 +1044,6 @@ const AppGrid = (props) => { }); } }, [currTab]); - //Component to display category List for User and Orgs app const FilterUsersAndOrgsAppByCategory = ({ selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps }) => { @@ -1025,14 +1053,23 @@ const AppGrid = (props) => { setIsCategoryListExpanded((prevState) => !prevState); }; - //Display top 9 category from the database + const [appsToFilter, setAppsToFilter] = useState([]); + useEffect(() => { + if (currTab === 1) { + setAppsToFilter(orgApps) + } + if (currTab === 2) { + setAppsToFilter(userApps) + } + }) + //Display top 9 category from the database const findTopCategories = () => { const categoryCountMap = {}; // Check if userAndOrgsApp is an array before iterating over it and Find top 10 Category from the apps - if (Array.isArray(userAndOrgsApp)) { - userAndOrgsApp.forEach((app) => { + if (Array.isArray(appsToFilter)) { + appsToFilter.forEach((app) => { const categories = app.categories; if (categories && categories.length > 0) { @@ -1104,49 +1141,53 @@ const AppGrid = (props) => { )} - -
    - {topCategories.map((data, index) => ( - - ))} +
    + {!isLoading && ( + +
    + {topCategories.map((data, index) => ( + + ))} - -
    -
    + +
    + + )} +
    ); }; @@ -1157,13 +1198,22 @@ const AppGrid = (props) => { const toggleActionLabel = () => { setIsActionLabelExpanded((prevState) => !prevState); }; + const [appsToFilter, setAppsToFilter] = useState([]); + useEffect(() => { + if (currTab === 1) { + setAppsToFilter(orgApps) + } + if (currTab === 2) { + setAppsToFilter(userApps) + } + }) //Find top 9 tags from the database const findTopTags = () => { const tagCountMap = {}; - if (Array.isArray(userAndOrgsApp)) { - userAndOrgsApp.forEach((app) => { + if (Array.isArray(appsToFilter)) { + appsToFilter.forEach((app) => { const tags = app.tags; if (tags && tags.length > 0) { tags.forEach((tag) => { @@ -1505,17 +1555,30 @@ const AppGrid = (props) => { if (currTab === 1 || currTab === 2) { setIsLoading(true); } - if (currTab) { - setUserAndOrgsApp([]) - } }, [currTab]) //Component to fetch all apps created by user and Org - const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith }) => { + const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => { const [searchQuery, setSearchQuery] = useState(""); + const [appsToShow, setAppsToShow] = useState([]); + useEffect(() => { + if (currTab === 1) { + setAppsToShow(orgApps) + } + if (currTab === 2) { + setAppsToShow(userApps) + } + }, [currTab]) + + useEffect(() => { + setselectedCategoryForUsersAndOgsApps([]); + setSelectedTagsForUserAndOrgApps([]); + setSelectedOptionOfCreatedWith([]); + }, [currTab]) + //Search app base on app name, category and tag - const filteredUserAppdata = Array.isArray(userAndOrgsApp) ? userAndOrgsApp.filter((app) => { + const filteredUserAppdata = Array.isArray(appsToShow) ? appsToShow.filter((app) => { const matchesSearchQuery = ( searchQuery === "" || app.name.toLowerCase().includes(searchQuery.toLowerCase()) || @@ -1552,6 +1615,15 @@ const AppGrid = (props) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; + const [showNoAppFound, setShowNoAppFound] = useState(false); + + //show some delay to show the "App Not Found." so it doesn't not show while changing tab. + useEffect(() => { + const timer = setTimeout(() => { + setShowNoAppFound(true); + }, 1000); + return () => clearTimeout(timer); + }, []); return (
    @@ -1560,201 +1632,208 @@ const AppGrid = (props) => { ) : (
    {isLoggedIn ? ( -
    -
    - - -
    - {filteredUserAppdata.map((data, index) => { - const isMouseOverOnCloudIcon = false; - const xs = 12; - const rowHandler = 12; - const searchClient = {}; - const userdata = {}; +
    + + {((filteredUserAppdata.length === 0 && showNoAppFound) && isLoggedIn && !isLoading) ? ( + No App Found + ) : ( +
    +
    + +
    + {filteredUserAppdata.map((data, index) => { + const isMouseOverOnCloudIcon = false; + const xs = 12; + const rowHandler = 12; + const searchClient = {}; + const userdata = {}; - const paperStyle = { - backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#1A1A1A", - color: "rgba(241, 241, 241, 1)", - padding: isHeader ? null : 15, - cursor: "pointer", - position: "relative", - width: 339, - height: 96, - borderRadius: 8, - }; + const paperStyle = { + backgroundColor: mouseHoverIndex === index ? "rgba(26, 26, 26, 1)" : "#1A1A1A", + color: "rgba(241, 241, 241, 1)", + padding: isHeader ? null : 15, + cursor: "pointer", + position: "relative", + width: 339, + height: 96, + borderRadius: 8, + }; - var parsedname = ""; - for (var key = 0; key < data.name.length; key++) { - var character = data.name.charAt(key); - if (character === character.toUpperCase()) { - if ( - data.name.charAt(key + 1) !== undefined && - data.name.charAt(key + 1) === - data.name.charAt(key + 1).toUpperCase() - ) { - } else { - parsedname += " "; + var parsedname = ""; + for (var key = 0; key < data.name.length; key++) { + var character = data.name.charAt(key); + if (character === character.toUpperCase()) { + if ( + data.name.charAt(key + 1) !== undefined && + data.name.charAt(key + 1) === + data.name.charAt(key + 1).toUpperCase() + ) { + } else { + parsedname += " "; + } + } + parsedname += character; } - } - parsedname += character; - } - parsedname = ( - parsedname.charAt(0).toUpperCase() + parsedname.substring(1) - ).replaceAll("_", " "); + parsedname = ( + parsedname.charAt(0).toUpperCase() + parsedname.substring(1) + ).replaceAll("_", " "); - const normalizedString = (name) => { - if (typeof name === 'string') { - return name.replace(/_/g, ' '); - } else { - return name; - } - }; + const normalizedString = (name) => { + if (typeof name === 'string') { + return name.replace(/_/g, ' '); + } else { + return name; + } + }; - const appUrl = - isCloud === true - ? `/apps/${data.id}` - : `https://shuffler.io/apps/${data.id}`; + const appUrl = + isCloud === true + ? `/apps/${data.id}` + : `https://shuffler.io/apps/${data.id}`; - return ( - - - - { - setMouseHoverIndex(index); - }} - onMouseOut={() => { - setMouseHoverIndex(-1); - }} - > - + - {data.name} { + setMouseHoverIndex(index); }} - /> -
    { + setMouseHoverIndex(-1); }} > -
    - {normalizedString(data.name)} -
    -
    - {data.categories !== null - ? normalizedString(data.categories).join(", ") - : "NA"} -
    -
    - {data.tags && - data.tags.map((tag, tagIndex) => ( - - {normalizedString(tag)} - {tagIndex < data.tags.length - 1 ? ", " : ""} - - ))} -
    - {/* )} */} -
    - - -
    -
    -
    - ); - }) - } + {data.name} + +
    +
    + {normalizedString(data.name)} +
    +
    + {data.categories !== null + ? normalizedString(data.categories).join(", ") + : "NA"} +
    +
    + {data.tags && + data.tags.map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))} +
    + {/* )} */} +
    + + + + + + ); + }) + } +
    +
    - -
    +
    + )}
    ) : ( Please login to your account first to view {`${currTab === 1 ? "Organization" : "My"}`} Apps.
    @@ -1767,8 +1846,7 @@ const AppGrid = (props) => { }; - const AppTab = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith }) => { - + const AppTab = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => { const [isAnyAppActivated, setIsAnyAppActivated] = useState(false); return ( @@ -1827,7 +1905,7 @@ const AppGrid = (props) => { {currTab === 0 ? ( ) : currTab === 1 || currTab === 2 ? ( - + ) : null}
    @@ -1862,6 +1940,9 @@ const AppGrid = (props) => { selectedCategoryForUsersAndOgsApps={selectedCategoryForUsersAndOgsApps} selectedTagsForUserAndOrgApps={selectedTagsForUserAndOrgApps} selectedOptionOfCreatedWith={selectedOptionOfCreatedWith} + setselectedCategoryForUsersAndOgsApps={setselectedCategoryForUsersAndOgsApps} + setSelectedTagsForUserAndOrgApps={setSelectedTagsForUserAndOrgApps} + setSelectedOptionOfCreatedWith={setSelectedOptionOfCreatedWith} />
    diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index c5c4e9c3..434b604a 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -434,7 +434,7 @@ const CacheView = (props) => { style={{ minWidth: 300, maxWidth: 300, - height:200, + // height:200, overflowX: "hidden", }} primary={validate.valid ? diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 5e9605f2..1c850813 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -219,6 +219,11 @@ const ConfigureWorkflow = (props) => { action.app_name = action.app_name.slice(0, -4) } + if (action.app_name === "Integration Framework") { + console.log("Skipping integration framework: ", action) + continue + } + // 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))))) // @@ -227,8 +232,6 @@ const ConfigureWorkflow = (props) => { const newappname = action.app_name.toLowerCase().replaceAll(" ", "_") const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname) - - if (app === undefined || app === null) { const subapp = apps.find(app => app.name === action.app_name) @@ -532,7 +535,7 @@ const ConfigureWorkflow = (props) => { , @@ -917,6 +920,7 @@ const ConfigureWorkflow = (props) => { parsedName = parsedName.substring(0, parsedName.length - 4); } + return ( {action.must_authenticate ? @@ -1061,7 +1065,7 @@ const ConfigureWorkflow = (props) => { src={action.large_image} /> - Activate + Activate {action.app_name.replaceAll("_", " ")} : diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index e830467e..edf0ae24 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -18,11 +18,11 @@ import { MenuItem, Select, Button, + ButtonGroup, Grid, IconButton, Divider, LinearProgress, - AppBar, } from "@mui/material"; @@ -50,13 +50,14 @@ const Header = (props) => { globalUrl, setNotifications, notifications, - isLoaded, + isLoaded, isLoggedIn, removeCookie, homePage, userdata, - isMobile, + isMobile, serverside, + setModalOpen, } = props; const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); @@ -80,7 +81,7 @@ const Header = (props) => { setAnchorElAvatar(null); }; // Should be based on some path - const logoCheck = !homePage ? null : null + const logoCheck = !homePage ? null : null const hrefStyle = { color: hoverOutColor, @@ -88,25 +89,25 @@ const Header = (props) => { }; const menuText = { - textTransform: "none", - color: "#FFF", - textAlign: "center", - fontSize: 16, - fontStyle: "normal", - fontWeight: 400, - lineHeight: "normal", + textTransform: "none", + color: "#FFF", + textAlign: "center", + fontSize: 16, + fontStyle: "normal", + fontWeight: 400, + lineHeight: "normal", } const isCloud = serverside === true || typeof window === "undefined" ? true : window.location.host === "localhost:3002" || - window.location.host === "shuffler.io" || - window.location.host === "localhost:5002"; + window.location.host === "shuffler.io" || + window.location.host === "localhost:5002"; const clearNotifications = () => { // Don't really care about the logout - + toast("Clearing notifications") fetch(`${globalUrl}/api/v1/notifications/clear`, { credentials: "include", @@ -235,12 +236,12 @@ const Header = (props) => { setLoginHoverColor(hoverOutColor); }; - const notificationWidth = 335 - const imagesize = 22; - const boxColor = "#86c142"; + const notificationWidth = 335 + const imagesize = 22; + const boxColor = "#86c142"; - const NotificationItem = (props) => { - const {data} = props + const NotificationItem = (props) => { + const { data } = props var image = ""; var orgName = ""; @@ -279,7 +280,7 @@ const Header = (props) => { alt={foundOrg.name} src={foundOrg.image} style={imageStyle} - onClick={() => {}} + onClick={() => { }} /> ); @@ -297,31 +298,31 @@ const Header = (props) => { borderBottom: "1px solid rgba(255,255,255,0.4)", }} > - {data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ? - - - {data.title} ({data.amount}) - - - : - - {data.title} - - } + {data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ? + + + {data.title} ({data.amount}) + + + : + + {data.title} + + } - {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.title} - : - null - } - - {data.description} - + {data.image !== undefined && data.image !== null && data.image.length > 0 ? + {data.title} + : + null + } + + {data.description} +
    {data.read === false ? ( - ) : null} + + {notifications.length > 1 ? ( + + ) : null} + +
    - Notifications generated made by Shuffle to help you discover issues or - improvements. - Learn more + Notifications generated made by Shuffle to help you discover issues or + improvements. + Learn more {notifications.map((data, index) => { - if (data.read) { - return null - } + if (data.read) { + return null + } - return ; + return ; })} @@ -456,7 +467,7 @@ const Header = (props) => { return response.json(); }) .then(function (responseJson) { - console.log("In here?") + console.log("In here?") if (responseJson.success === true) { if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { console.log("Region Change: ", responseJson.region_url); @@ -470,11 +481,11 @@ const Header = (props) => { toast("Successfully changed active organization - refreshing!"); } else { - if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { - toast(responseJson.reason); - } else { - toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); - } + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + toast(responseJson.reason); + } else { + toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); + } } }) .catch((error) => { @@ -497,7 +508,7 @@ const Header = (props) => { style={{}} aria-controls="simple-menu" aria-haspopup="true" - onClick={(event) => {}} + onClick={(event) => { }} > Discord Community Join { // Should be based on some path const parsedAvatar = userdata.avatar !== undefined && - userdata.avatar !== null && - userdata.avatar.length > 0 + userdata.avatar !== null && + userdata.avatar.length > 0 ? userdata.avatar : ""; @@ -551,7 +562,7 @@ const Header = (props) => { handleClose(); }} > - Organisation + Organisation @@ -560,10 +571,10 @@ const Header = (props) => { handleClose(); }} > - Account + Account - + { @@ -602,7 +613,7 @@ const Header = (props) => { Creator page - + { @@ -613,11 +624,11 @@ const Header = (props) => { >  Logout - + - - Version: 1.4.0 - + + Version: 1.4.0 + ); @@ -628,7 +639,7 @@ const Header = (props) => { marginBottom: "auto", marginRight: 10, }; - + // Handle top bar or something const defaultTop = -2 const loginTextBrowser = !isLoggedIn ? ( @@ -641,163 +652,163 @@ const Header = (props) => { textAlign: "center", }} > - + + + + { + if (isCloud) { + ReactGA.event({ + category: "header", + action: "home_click", + label: "", + }); + } + }} + > + shuffle logo + + + + + + + + + + + {isCloud ? ( - - { - if (isCloud) { - ReactGA.event({ - category: "header", - action: "home_click", - label: "", - }); - } - }} - > - shuffle logo - - - - - - - + - {isCloud ? ( - - - - - - ) : null} - - - - - - - -
    - - - - - - - - - + + + + + + +
    + + + + + + + + + - +
    ) : (
    @@ -830,91 +841,91 @@ const Header = (props) => { style={{ color: HomeHoverColor, cursor: "pointer" }} > - + logo
    - -
    - {/* + +
    + {/* */} - - Workflows - -
    - - - - -
    - {/* + + Workflows + +
    + +
    + + +
    + {/* */} - - Apps - -
    - -
    - {/* + + Apps + +
    + +
    + {/*
    Dashboard
    */} - - -
    - {/* + + +
    + {/* */} - - Docs - -
    - -
    + + Docs + +
    + +
    - +
    -
    +
    { marginRight: 10, marginLeft: data.creator_org !== undefined && - data.creator_org !== null && - data.creator_org.length > 0 + data.creator_org !== null && + data.creator_org.length > 0 ? data.id === userdata.active_org.id ? 0 : 20 @@ -1021,8 +1032,8 @@ const Header = (props) => { const parsedTitle = data.creator_org !== undefined && - data.creator_org !== null && - data.creator_org.length > 0 + data.creator_org !== null && + data.creator_org.length > 0 ? `Suborg of ${data.creator_org}` : ""; @@ -1056,11 +1067,11 @@ const Header = (props) => { regiontag = namesplit[namesplit.length - 1]; - if (regiontag === "california") { - regiontag = "us" - } else if (regiontag === "frankfurt") { - regiontag = "fr" - } + if (regiontag === "california") { + regiontag = "us" + } else if (regiontag === "frankfurt") { + regiontag = "fr" + } } } @@ -1134,61 +1145,62 @@ const Header = (props) => { {/* Show on cloud, if not suborg and if not customer/pov/internal */} { - userdata.licensed !== undefined && - userdata.licensed !== null && - userdata.licensed === false ? - - - - - - : null} + + + + : null} {userdata === undefined || - userdata.app_execution_limit === undefined || - userdata.app_execution_usage === undefined || - userdata.app_execution_usage < 1000 ? null : ( + userdata.app_execution_limit === undefined || + userdata.app_execution_usage === undefined || + userdata.app_execution_usage < 1000 ? null : ( @@ -1207,14 +1219,14 @@ const Header = (props) => { border: userdata.app_execution_usage / userdata.app_execution_limit >= - 0.9 + 0.9 ? "2px solid #f86a3e" : null, }} onClick={() => { console.log( userdata.appe_execution_usage / - userdata.app_execution_limit + userdata.app_execution_limit ); if (window.drift !== undefined) { window.drift.api.startInteraction({ @@ -1398,29 +1410,36 @@ const Header = (props) => { ); // - // - /* - !isLoggedIn ? -
    - - {loginTextBrowser} - -
    - : - */ - return !isMobile ? - - - {loginTextBrowser} - - - : - {loginTextMobile} + // + /* + !isLoggedIn ? +
    + + {loginTextBrowser} + +
    + : + */ + return !isMobile ? +
    + +
    + {loginTextBrowser} +
    +
    +
    + : + {loginTextMobile} }; export default Header; diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index f9eceb64..5f7fbbbd 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -24,9 +24,7 @@ const useStyles = makeStyles({ }, }); -const defaultImage = - "data:image/png;base64,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"; - +const defaultImage = theme.palette.defaultImage const OrgHeader = (props) => { const { userdata, @@ -124,10 +122,9 @@ const OrgHeader = (props) => { ); var imageData = file.length > 0 ? file : fileBase64; - imageData = - imageData === undefined || imageData.length === 0 - ? defaultImage - : imageData; + imageData = imageData === undefined || imageData.length === 0 ? defaultImage : imageData + + const imageInfo = ( { minWidth: 174, minHeight: 174, objectFit: "contain", + borderRadius: theme.shape.borderRadius, }} /> ); @@ -166,10 +164,6 @@ const OrgHeader = (props) => { ? null : "1px solid #f85a3e", cursor: "pointer", - backgroundColor: - imageData !== undefined && imageData.length > 0 - ? null - : theme.palette.inputColor, maxWidth: 174, maxHeight: 174, borderRadius: theme.shape.borderRadius, diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 4fd1edbf..13e67d7a 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -8,6 +8,7 @@ import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths } from "../views/Apps.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx"; import { NestedMenuItem } from "mui-nested-menu"; +import { parsedDatatypeImages } from "../components/AppFramework.jsx"; //import { useAlert import { @@ -163,6 +164,8 @@ const ParsedAction = (props) => { expansionModalOpen, setExpansionModalOpen, + + listCache, apps, setEditorData, @@ -431,20 +434,59 @@ const ParsedAction = (props) => { }) } + /* actionlist.push({ type: "Shuffle DB", name: "Shuffle DB", value: "$shuffle_cache", highlight: "shuffle_cache", autocomplete: "shuffle_cache", - example: "", + example: { + "what": "", + "unique gmail ids new": "", + }, }) + */ - if ( - workflow.workflow_variables !== null && - workflow.workflow_variables !== undefined && - workflow.workflow_variables.length > 0 - ) { + var cachekey = { + type: "Shuffle DB", + name: "Shuffle DB", + value: "$shuffle_cache", + highlight: "shuffle_cache", + autocomplete: "shuffle_cache", + example: "", + } + + if (listCache !== undefined && listCache !== null && listCache.keys !== undefined && listCache.keys !== null && listCache.keys.length > 0) { + cachekey.example = {} + + for (var i in listCache.keys) { + const item = listCache.keys[i] + if (item.key === undefined || item.key === null || item.key.length === 0) { + continue + } + + var itemvalue = item.value === undefined || item.value === null ? "" : item.value + try{ + if (itemvalue.length > 10000) { + itemvalue = "" + } + + } catch (e) { + itemvalue = "" + } + + var itemkey = item.key.split(" ").join("_") + cachekey.example[itemkey] = { + "value": itemvalue, + } + } + } else { + } + + actionlist.push(cachekey) + + if (workflow.workflow_variables !== null && workflow.workflow_variables !== undefined && workflow.workflow_variables.length > 0) { for (let [key,keyval] in Object.entries(workflow.workflow_variables)) { const item = workflow.workflow_variables[key]; actionlist.push({ @@ -1083,26 +1125,104 @@ const ParsedAction = (props) => { // FIXME: Issue #40 - selectedActionParameters not reset - if ( - Object.getOwnPropertyNames(selectedAction).length > 0 && - selectedActionParameters.length > 0 - ) { + if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0) { + + var wrapperapp = { + "id": "", + "name": "noapp", + "large_image": "", + } + + var actionname = selectedAction.name.toLowerCase() + if (actionname === "email" || actionname === "communication") { + actionname = "comms" + } + + if (isIntegration) { + + // Check if actionname uppercase is in the parsedDatatypeImages() dictionary + if (parsedDatatypeImages()[actionname.toUpperCase()] !== undefined) { + var newimage = parsedDatatypeImages()[actionname.toUpperCase()] + //newimage = + wrapperapp.large_image = newimage + } else { + console.log("Couldn't find actionname: ", actionname) + } + } + var authWritten = false; + var noAppSelected = false + const paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name") + if (paramIndex === -1 || selectedAction.parameters[paramIndex].value === "" || selectedAction.parameters[paramIndex].value === "noapp") { + // Check the actual value and if it's the same + noAppSelected = true + } return (
    {isIntegration ? apps !== undefined && apps !== null && apps.length > 0 ? -
    +
    +
    { + + selectedAction.example = "noapp" + selectedAction.large_image = newimage + if (cy !== undefined && cy !== null) { + const foundnode = cy.getElementById(selectedAction.id) + if (foundnode !== undefined && foundnode !== null) { + foundnode.data("large_image", newimage) + } + } + + const iconInfo = GetIconInfo(selectedAction) + if (iconInfo !== undefined && iconInfo !== null) { + selectedAction.fillGradient = iconInfo.fillGradient + + selectedAction.iconBackground = iconInfo.iconBackgroundColor + selectedAction.fillstyle = "linear-gradient" + } + + if (paramIndex === -1) { + console.log("Couldn't find app_name parameter") + selectedAction.parameters.push({ + name: "app_name", + value: wrapperapp.name, + autocompleted: false, + }) + } else { + selectedAction.parameters[paramIndex].value = wrapperapp.name + } + + setSelectedAction(selectedAction) + setUpdate(Math.random()) + + }}> + +
    + +
    +
    +
    {apps.map((app, appIndex) => { if (app.categories === undefined || app.categories === null || app.categories.length === 0) { return null } var found = false - var actionname = selectedAction.name.toLowerCase() - if (actionname === "email") { - actionname = "communication" - } for (var key in app.categories) { @@ -1129,16 +1249,14 @@ const ParsedAction = (props) => { return (
    { + selectedAction.example = "" selectedAction.large_image = app.large_image - - /* - if (cy !== undefined) { - const foundnode = cy.getElementById(selectedAction.id) - if (foundnode !== undefined && foundnode !== null) { - foundnode.data("large_image", app.large_image) - } - } - */ + if (cy !== undefined && cy !== null) { + const foundnode = cy.getElementById(selectedAction.id) + if (foundnode !== undefined && foundnode !== null) { + foundnode.data("large_image", app.large_image) + } + } if (paramIndex === -1) { console.log("Couldn't find app_name parameter") @@ -1155,7 +1273,7 @@ const ParsedAction = (props) => { setUpdate(Math.random()) }}> - + { 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(""); let navigate = useNavigate(); useEffect(() => { getFramework() + + // Check "workflow" and "execution_id" in URL + const urlParams = new URLSearchParams(window.location.search) + const workflow = urlParams.get("workflow") + const execution_id = urlParams.get("execution_id") + + if (execution_id !== null) { + setSelectedExecutionId(execution_id) + + //toast.info("Execution-related notifications are highlighted.") + } + + if (workflow !== null) { + setSelectedWorkflow(workflow) + + toast.info("Workflow-related notifications are highlighted.") + } }, []) if (userdata === undefined || userdata === null) { @@ -157,6 +176,10 @@ const Priorities = (props) => { var image = ""; var orgName = ""; 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) + if (userdata.orgs !== undefined) { const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]); if (foundOrg !== undefined && foundOrg !== null) { @@ -207,6 +230,8 @@ const Priorities = (props) => { padding: 30, borderBottom: "1px solid rgba(255,255,255,0.4)", marginBottom: 20, + border: highlighted ? "2px solid #f85a3e" : null, + borderRadius: theme.palette.borderRadius, }} >
    @@ -313,7 +338,42 @@ const Priorities = (props) => { return (
    -

    Suggestions

    +

    Notifications

    + + Notifications help you find potential problems with your workflows and apps.  + + Learn more + + +
    + { + setShowRead(!showRead); + }} + />  Show read + {notifications === null || notifications === undefined || notifications.length === 0 ? null : +
    + {notifications.map((notification, index) => { + if (showRead === false && notification.read === true) { + return null + } + + return ( + + ) + })} +
    + } + + {clickedFromOrgTab? null : } + +

    Suggestions

    Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
    These range from simple configurations in Shuffle to Usecases you may have missed.  { ) }) } - {clickedFromOrgTab?null:} -

    Notifications

    - - Notifications help you find potential problems with your workflows and apps.  -
    - Learn more - -
    -
    - { - setShowRead(!showRead); - }} - />  Show read - {notifications === null || notifications === undefined || notifications.length === 0 ? null : -
    - {notifications.map((notification, index) => { - if (showRead === false && notification.read === true) { - return null - } - - return ( - - ) - })} -
    - }
    ) diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 6df11b9b..13a6735f 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -42,7 +42,7 @@ const WorkflowTemplatePopup = (props) => { const [appAuthentication, setAppAuthentication] = React.useState(undefined); const [missingSource, setMissingSource] = React.useState(undefined) const [missingDestination, setMissingDestination] = React.useState(undefined); - const [configurationFinished, setConfigurationFinished] = React.useState(false); + const [configurationFinished, setConfigurationFinished] = React.useState(false) const [appSetupDone, setAppSetupDone] = React.useState(false) const [requestSent, setRequestSent] = React.useState(false) @@ -75,6 +75,16 @@ const WorkflowTemplatePopup = (props) => { } }, [modalOpen, missingSource, missingDestination]) + useEffect(() => { + //console.log("IN USEEFFECT FOR CONFIG: ", configurationFinished) + if (configurationFinished === true && workflow.id !== undefined && workflow.id !== null && workflow.id !== "") { + toast.success("Generation Successful. Redirecting to the workflow..") + setTimeout(() => { + navigate("/workflows/" + workflow.id) + }, 2000) + } + }, [configurationFinished, workflow]) + const imagestyleWrapper = { height: 40, @@ -305,13 +315,13 @@ const WorkflowTemplatePopup = (props) => { if (srcapp.includes(":default") || dstapp.includes(":default")) { toast("You need to select both a source and destination app before generating this workflow.") - if (srcapp.includes(":default")) { + if (srcapp !== undefined && srcapp !== null && srcapp.includes(":default")) { setMissingSource({ "type": srcapp.split(":")[0], }) } - if (dstapp.includes(":default")) { + if (dstapp !== undefined && dstapp !== null && dstapp.includes(":default")) { setMissingDestination({ "type": dstapp.split(":")[0], }) @@ -490,7 +500,7 @@ const WorkflowTemplatePopup = (props) => {
    Generating the Workflow... - +
    :
    @@ -572,18 +582,7 @@ const WorkflowTemplatePopup = (props) => { setConfigurationFinished={setConfigurationFinished} /> - - {/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id !== "" ? -
    - -
    - : null*/} - - {errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ? + {/*errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ? { window.open("/workflows/" + workflow.id, "_blank") }} > - {/* */} Workflow Successfully Generated! - : null} - - {/*errorMessage === "" ? - : null*/} + ) diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index ca66ef3c..76840b9c 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -2,7 +2,16 @@ const data = [ { selector: "node", css: { - label: "data(label)", + label: function(element) { + var elementname = element.data("label") + if (elementname === null || elementname === undefined) { + return "" + } + + elementname = elementname.replace("_", " ", -1) + elementname = elementname.charAt(0).toUpperCase() + elementname.slice(1) + return elementname + }, "text-valign": "center", "font-family": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif", "font-weight": "lighter", @@ -79,6 +88,36 @@ const data = [ "text-wrap": "wrap", }, }, + { + selector: `node[app_name="Integration Framework"]`, + css: { + width: "60px", + height: "60px", + "z-index": 5000, + + //'border-width': 3, + //'border-color': 'transparent', + //'border-style': 'solid', + //'border-gradient': 'linear-gradient(to right, #FF0000, #FF7F00, #FFFF00, #00FF00, #0000FF, #4B0082, #8A2BE2)' + }, + }, + { + selector: `node[example="noapp"]`, + css: { + // Make background image padding on the left side 20px + "background-width": "75%", + "background-height": "75%", + "background-position-x": "17px", + "background-position-y": "17px", + + "background-color": "data(iconBackground)", + "background-fill": "data(fillstyle)", + "background-gradient-direction": "to-bottom-right", + "background-gradient-stop-colors": "data(fillGradient)", + // Change transparency of background + "background-opacity": "0.3", + }, + }, { selector: `node[app_name="Shuffle Tools"]`, css: { diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 99649e48..b183a1e4 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -24,12 +24,12 @@ const theme = createTheme(adaptV4Theme({ platformColor: "#1c1c1d", backgroundColor: "#1a1a1a", green: "#5cc879", - borderRadius: 5, + borderRadius: 10, defaultBorder: "1px solid rgba(255,255,255,0.3)", jsonTheme: "brewer", reactJsonStyle: { padding: 5, - width: "100%", + width: "98%", borderRadius: 5, border: "1px solid rgba(255,255,255,0.7)", overflowX: "auto", @@ -53,8 +53,7 @@ const theme = createTheme(adaptV4Theme({ boxShadow: 1, fontSize: 11, }, - defaultImage: - "data:image/png;base64,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", + defaultImage: "/images/no_image.png", }, typography: { fontFamily: `"Roboto", "Helvetica", "Arial", sans-serif`, diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index ceebabdd..a6c35e7f 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -6262,23 +6262,15 @@ If you're interested, please let me know a time that works for you, or set up a /> - - {userdata.orgs !== undefined && @@ -6317,33 +6309,29 @@ If you're interested, please let me know a time that works for you, or set up a /> - - { - setCloudSyncModalOpen(true); - setSelectedOrganization(data); - console.log("INVERT CLOUD SYNC"); - }} - /> - style={{ minWidth: 150, maxWidth: 150 }} - /> - - ); + style={{ minWidth: 200, maxWidth: 200 }} + primary={ + + } + /> + + ) })} ) : null} diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index af90c8d9..3f25507b 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -73,6 +73,7 @@ import { import { Folder as FolderIcon, + VerifiedUser as VerifiedUserIcon, Insights as InsightsIcon, LibraryBooks as LibraryBooksIcon, OpenInNew as OpenInNewIcon, @@ -118,6 +119,7 @@ import { QueryStats as QueryStatsIcon, AutoAwesome as AutoAwesomeIcon, Add as AddIcon, + ErrorOutline as ErrorOutlineIcon, } from "@mui/icons-material"; //import * as cytoscape from "cytoscape"; @@ -139,6 +141,7 @@ import PaperComponent from "../components/PaperComponent.jsx" import ExtraApps from "../components/ExtraApps.jsx" import EditWorkflow from "../components/EditWorkflow.jsx" // import AppStats from "../components/AppStats.jsx"; +const noImage = "/public/no_image.png"; cytoscape.use(edgehandles); @@ -154,14 +157,14 @@ export const triggers = [ is_valid: true, label: "Webhook", environment: "onprem", - description: "Custom HTTP input", + description: "Custom HTTP input trigger", long_description: "Execute a workflow with an unauthicated POST request", + id: "", }, { name: "Schedule", type: "TRIGGER", status: "uninitialized", - description: "Specify time", trigger_type: "SCHEDULE", errors: null, large_image: @@ -169,9 +172,11 @@ export const triggers = [ label: "Schedule", is_valid: true, environment: "onprem", + description: "Schedule time trigger", long_description: "Create a schedule based on cron", + id: "", }, - { + /*{ name: "Office365", type: "TRIGGER", status: "uninitialized", @@ -184,6 +189,7 @@ export const triggers = [ large_image: "data:image/jpg;base64,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", long_description: "Execute a workflow when you get an email", + id: "", }, { name: "Gmail", @@ -198,7 +204,8 @@ export const triggers = [ large_image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAzFodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QTIyMjgyMEYwMDJDMTFFQkJBOEE5OUJBM0MzMTA2RDIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QTIyMjgyMTAwMDJDMTFFQkJBOEE5OUJBM0MzMTA2RDIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozQTMwMDQxRTAwMEUxMUVCQkE4QTk5QkEzQzMxMDZEMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBMjIyODIwRTAwMkMxMUVCQkE4QTk5QkEzQzMxMDZEMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAwICAwMCAwMDAwMEBwUEBAQECQYHBQcKCQsL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", long_description: "Execute a workflow when you get an email", - }, + id: "", + },*/ { name: "Shuffle Workflow", type: "TRIGGER", @@ -210,27 +217,29 @@ export const triggers = [ is_valid: true, label: "Subflow", environment: "onprem", - description: "Control a workflow", + description: "Run a Subflow trigger", long_description: "Execute another workflow from this workflow", + id: "", }, { name: "User Input", type: "TRIGGER", status: "running", large_image: "/images/workflows/UserInput2.svg", - description: "Wait for user input", + description: "Wait for user input trigger", trigger_type: "USERINPUT", is_valid: true, errors: null, label: "User input", environment: "cloud", long_description: "Take user input to continue execution", + id: "", }, { name: "Pipelines by Tenzir", type: "TRIGGER", status: "uninitialized", - description: "BETA: Support only", + description: "BETA: Support only for Tenzir pipelines - trigger", trigger_type: "PIPELINE", errors: null, is_valid: true, @@ -238,6 +247,7 @@ export const triggers = [ environment: "onprem", large_image: "/images/workflows/tenzir2.png", long_description: "Controls a pipeline to run things", + id: "", }, ]; @@ -423,7 +433,7 @@ const AngularWorkflow = (defaultprops) => { const [subworkflow, setSubworkflow] = React.useState({}); const [subworkflowStartnode, setSubworkflowStartnode] = React.useState(""); const [leftViewOpen, setLeftViewOpen] = React.useState(isMobile ? false : true); - const [leftBarSize, setLeftBarSize] = React.useState(isMobile ? 0 : 350); + const [leftBarSize, setLeftBarSize] = React.useState(isMobile ? 0 : 325) const [creatorProfile, setCreatorProfile] = React.useState({}); const [usecases, setUsecases] = React.useState([]); const [files, setFiles] = React.useState({ @@ -500,7 +510,7 @@ const AngularWorkflow = (defaultprops) => { const [apps, setApps] = React.useState([]); const [filteredApps, setFilteredApps] = React.useState([]); - const [prioritizedApps, setPrioritizedApps] = React.useState([]); + const [prioritizedApps, setPrioritizedApps] = React.useState([]) const [environments, setEnvironments] = React.useState([]); const [established, setEstablished] = React.useState(false); @@ -512,7 +522,10 @@ const AngularWorkflow = (defaultprops) => { const [selectedAction, setSelectedAction] = React.useState({}); const [selectedActionEnvironment, setSelectedActionEnvironment] = React.useState({}); - const [streamDisabled, setStreamDisabled] = React.useState(false); + // Disabled streaming for now + const [streamDisabled, setStreamDisabled] = React.useState(true) + + const [executionRequest, setExecutionRequest] = React.useState({}); const [executionRunning, setExecutionRunning] = React.useState(false); @@ -750,7 +763,6 @@ const AngularWorkflow = (defaultprops) => { useEffect(() => { // Current variable + future state controlled // This is so that the loop can stop itself as well - console.log("In useeffect for loopRunning: ", loopRunning, loopRunning2) if (loopRunning && loopRunning2) { const intervalId = setInterval(() => { if (!loopRunning) { @@ -2180,9 +2192,9 @@ const AngularWorkflow = (defaultprops) => { }] setAppsLoaded(true) - setApps(pretend_apps) setFilteredApps(pretend_apps) - setPrioritizedApps(pretend_apps); + setApps(Array.prototype.concat.apply(pretend_apps, triggers)) + setPrioritizedApps(pretend_apps) if (isLoggedIn) { toast("Something went wrong while loading apps. Please refresh the window to try again.") @@ -2207,9 +2219,9 @@ const AngularWorkflow = (defaultprops) => { }] setAppsLoaded(true) - setApps(pretend_apps) setFilteredApps(pretend_apps) - setPrioritizedApps(pretend_apps); + setApps(Array.prototype.concat.apply(pretend_apps, triggers)) + setPrioritizedApps(pretend_apps) return } @@ -2223,8 +2235,8 @@ const AngularWorkflow = (defaultprops) => { setToolsApp(foundTools) } - setApps(responseJson); // Set localstorage for the apps in the "apps" key + setApps(Array.prototype.concat.apply(responseJson, triggers)) if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) { try { localStorage.setItem("apps", JSON.stringify(responseJson)) @@ -2237,11 +2249,11 @@ const AngularWorkflow = (defaultprops) => { handledPrioritizedApps = [].concat(integrationApps, handledPrioritizedApps) if (isCloud) { - setFilteredApps(responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase()))); + setFilteredApps(responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase()))) setPrioritizedApps(handledPrioritizedApps) } else { - var tmpFiltered = responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase())) + const tmpFiltered = responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase())) setFilteredApps(tmpFiltered) setPrioritizedApps(handledPrioritizedApps) } @@ -2924,8 +2936,8 @@ const AngularWorkflow = (defaultprops) => { } } - // Read text from stream - //return response.text(); + // Read text from stream + //return response.text(); return response.json(); }) .then((responseJson) => { @@ -2950,6 +2962,10 @@ const AngularWorkflow = (defaultprops) => { if (responseJson.triggers === undefined || responseJson.triggers === null) { responseJson.triggers = []; } + + if (responseJson.org_id !== undefined && responseJson.org_id !== null) { + listOrgCache(responseJson.org_id) + } // Wait for this to finish fetchRecommendations(responseJson) @@ -3477,6 +3493,7 @@ const AngularWorkflow = (defaultprops) => { ((nodedata.app_name !== "Shuffle Tools" && nodedata.app_name !== "Testing" && nodedata.app_name !== "Shuffle Workflow" && + nodedata.app_name !== "Integration Framework" && nodedata.app_name !== "User Input") || nodedata.isStartNode) ) { @@ -4424,9 +4441,6 @@ const AngularWorkflow = (defaultprops) => { } } - - console.log("TRIGGER: ", data) - setTimeout(() => { setSelectedTriggerIndex(trigger_index); setSelectedTrigger(data) @@ -6534,7 +6548,7 @@ const AngularWorkflow = (defaultprops) => { const node = {}; if (!action.isStartNode && action.app_name === "Shuffle Tools") { - const iconInfo = GetIconInfo(action); + const iconInfo = GetIconInfo(action) const svg_pin = ``; const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); action.large_image = svgpin_Url; @@ -6549,7 +6563,15 @@ const AngularWorkflow = (defaultprops) => { } else { action.iconBackground = iconInfo.iconBackgroundColor; } - } + } else if (action.app_name === "Integration Framework") { + const iconInfo = GetIconInfo(action) + console.log("FOUND INTEGRATION: iconInfo: ", iconInfo) + if (iconInfo !== undefined && iconInfo !== null) { + action.fillGradient = iconInfo.fillGradient + action.iconBackground = iconInfo.iconBackgroundColor + action.fillstyle = "linear-gradient" + } + } node.position = action.position; node.data = action; @@ -6581,9 +6603,11 @@ const AngularWorkflow = (defaultprops) => { const decoratorNodes = inputworkflow.actions.map((action) => { if (!action.isStartNode) { if (action.app_name === "Testing") { - return null; + return null } else if (action.app_name === "Shuffle Tools") { - return null; + return null + } else if (action.app_name === "Integration Framework") { + return null } } @@ -6727,19 +6751,16 @@ const AngularWorkflow = (defaultprops) => { return edge; }); - if ( - inputworkflow.visual_branches !== undefined && - inputworkflow.visual_branches !== null && - inputworkflow.visual_branches.length > 0 - ) { + console.log("VISUAL BRANCHES: ", inputworkflow.visual_branches) + if (inputworkflow.visual_branches !== undefined && inputworkflow.visual_branches !== null && inputworkflow.visual_branches.length > 0) { const visualedges = inputworkflow.visual_branches.map((branch, index) => { const edge = {}; if (inputworkflow.branches[index] === undefined) { - return {}; + return {} } - var conditions = inputworkflow.branches[index].conditions; + var conditions = inputworkflow.branches[index].conditions if (conditions === undefined || conditions === null) { conditions = []; } @@ -6752,12 +6773,12 @@ const AngularWorkflow = (defaultprops) => { target: branch.destination_id, label: label, decorator: true, - }; + } - return edge; + return edge }); - edges = edges.concat(visualedges); + edges = edges.concat(visualedges) } @@ -6996,7 +7017,7 @@ const AngularWorkflow = (defaultprops) => { setFirstrequest(false); getWorkflow(props.match.params.key, {}); getRevisionHistory(props.match.params.key) - getApps(); + getApps() fetchUsecases() const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; @@ -7129,6 +7150,10 @@ const AngularWorkflow = (defaultprops) => { } }); + cy.on('grab', 'edge', (e) => { + console.log("Edge grabbed: ", e.target.data()) + }) + cy.on("select", "node", (e) => { onNodeSelect(e, appAuthentication); }); @@ -7337,8 +7362,8 @@ const AngularWorkflow = (defaultprops) => { const paperAppStyle = { borderRadius: theme.palette.borderRadius, - minHeight: isMobile ? 50 : 100, - maxHeight: isMobile ? 50 : 100, + minHeight: isMobile ? 50 : 70, + maxHeight: isMobile ? 50 : 70, minWidth: isMobile ? 50 : "100%", maxWidth: isMobile ? 50 : "100%", marginTop: "5px", @@ -7665,7 +7690,7 @@ const AngularWorkflow = (defaultprops) => { > {thisview}
    - + { {isMobile ? null : Triggers} } - style={tabStyle} + style={{ + maxWidth: isMobile ? leftBarSize : leftBarSize / 3, + minWidth: isMobile ? leftBarSize : leftBarSize / 3, + flex: 1, + textTransform: "none", + padding: 0, + }} /> { />
    - ); - }; + ) + } const TriggersView = () => { const triggersViewStyle = { - marginLeft: "10px", - marginRight: "10px", + marginLeft: 10, + marginRight: 10, display: "flex", flexDirection: "column", - }; + } // Predefined hurr - return (
    @@ -7740,8 +7770,8 @@ const AngularWorkflow = (defaultprops) => { return null } - const imagesize = isMobile ? 40 : trigger.large_image.includes("svg") ? 80 : 80 - var imageline = trigger.large_image.length === 0 ? + const imagesize = isMobile ? 40 : trigger.large_image.includes("svg") ? 50 : 50 + var imageline = trigger.large_image.length === 0 ? : { style={{ display: "flex", flexDirection: "column", - marginLeft: "20px", + marginLeft: 20, overflow: "hidden", }} > -

    +

    {trigger.name}

    @@ -8025,6 +8055,12 @@ const AngularWorkflow = (defaultprops) => { const handleAppDrag = (e, app) => { const cycontainer = cy.container(); + + if (app.type === "TRIGGER") { + handleTriggerDrag(e, app) + return + } + //console.log("e: ", e) //console.log("Offset: ", cycontainer) @@ -8045,25 +8081,17 @@ const AngularWorkflow = (defaultprops) => { return; } - currentnode[0].renderedPosition("x", e.pageX - cycontainer.offsetLeft); - currentnode[0].renderedPosition("y", e.pageY - cycontainer.offsetTop); + 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"); + console.log("workflow is public - not adding") return; } - 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; + 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(); @@ -8209,7 +8237,10 @@ const AngularWorkflow = (defaultprops) => { const [visibleApps, setVisibleApps] = React.useState( Array.prototype.concat.apply( prioritizedApps, - filteredApps.filter((innerapp) => !internalIds.includes(innerapp.id.toLowerCase())) + Array.prototype.concat.apply( + filteredApps.filter((innerapp) => !internalIds.includes(innerapp.id.toLowerCase())), + triggers + ) ) ) @@ -8220,18 +8251,18 @@ const AngularWorkflow = (defaultprops) => { const app = props.app; const [hover, setHover] = React.useState(false); - if (app.id === "" || app.name === "") { + if (app.type !== "TRIGGER" && (app.id === "" || app.name === "")) { return null } - const maxlen = 24; - var newAppname = app.name; - newAppname = newAppname.charAt(0).toUpperCase() + newAppname.substring(1); + const maxlen = 35 + var newAppname = app.name + newAppname = newAppname.charAt(0).toUpperCase() + newAppname.substring(1) if (newAppname.length > maxlen) { - newAppname = newAppname.slice(0, maxlen) + ".."; + newAppname = newAppname.slice(0, maxlen) + ".." } - newAppname = newAppname.replaceAll("_", " "); + newAppname = newAppname.replaceAll("_", " ") if (app.large_image === undefined || app.large_image === null || app.large_image === "") { app.large_image = theme.palette.defaultImage @@ -8239,14 +8270,14 @@ const AngularWorkflow = (defaultprops) => { const image = app.large_image !== undefined && app.large_image !== null && app.large_image !== "" ? app.large_image : theme.palette.defaultImage - const newAppStyle = JSON.parse(JSON.stringify(paperAppStyle)); + const newAppStyle = JSON.parse(JSON.stringify(paperAppStyle)) const pixelSize = !hover ? "2px" : "4px"; //newAppStyle.borderLeft = app.is_valid && app.actions !== null && app.actions !== undefined && app.actions.length > 0 && !(app.activated && app.generated) newAppStyle.borderLeft = app.is_valid && app.actions !== null && app.actions !== undefined && app.actions.length > 0 ? `${pixelSize} solid ${green}` : `${pixelSize} solid ${yellow}`; - if (app.id == highlightedApp) { + if (app.id == highlightedApp && app.id !== "") { //console.log("Found correct appid to highlight: ", app.id) newAppStyle.border = "3px solid " + green @@ -8372,7 +8403,7 @@ const AngularWorkflow = (defaultprops) => { > { userDrag: "none", userSelect: "none", borderRadius: theme.palette.borderRadius, - height: isMobile ? 40 : 80, - width: isMobile ? 40 : 80, + height: isMobile ? 40 : 55, + width: isMobile ? 40 : 55, }} /> @@ -8403,29 +8434,16 @@ const AngularWorkflow = (defaultprops) => { 20 ? -1 : 12, + fontSize: 19, + }} > {newAppname} - - - Version: {app.app_version} - - - - - {app.description} - - } @@ -8435,32 +8453,39 @@ const AngularWorkflow = (defaultprops) => { }; const runSearch = (value) => { + value = value.trim().toLowerCase() if (value.length > 0) { - var newApps = allApps.filter( + // Dedup based on name + const preppedApps = Array.prototype.concat.apply(allApps, triggers).filter((app, index, self) => { + return index === self.findIndex((t) => ( + t.name === app.name + )) + }) + + var newApps = preppedApps.filter( (app) => - app.name.toLowerCase().includes(value.trim().toLowerCase()) + app.name.toLowerCase().includes(value) || - app.description.toLowerCase().includes(value.trim().toLowerCase()) + app.description.toLowerCase().includes(value) ) // Extend search if (newApps.length === 0) { - const searchvalue = value.trim().toLowerCase(); newApps = allApps.filter((app) => { - if (app.actions !== undefined && app.actions !== null) { - for (let actionkey in app.actions) { - const inneraction = app.actions[actionkey]; - if (inneraction.name.toLowerCase().includes(searchvalue)) { - return true; - } - } - } + if (app.actions !== undefined && app.actions !== null) { + for (let actionkey in app.actions) { + const inneraction = app.actions[actionkey] + if (inneraction.name.toLowerCase().includes(value)) { + return true; + } + } + } return false; - }); + }) } - setVisibleApps(newApps); + setVisibleApps(newApps) } else { setVisibleApps( prioritizedApps.concat( @@ -8468,7 +8493,7 @@ const AngularWorkflow = (defaultprops) => { (innerapp) => !internalIds.includes(innerapp.id.toLowerCase()) ) ) - ); + ) } }; @@ -8755,16 +8780,20 @@ const AngularWorkflow = (defaultprops) => {
    {visibleApps.map((app, index) => { if (app.invalid) { - return null; + return null } + if (app.trigger_type === "PIPELINE" && userdata.support !== true) { + return null + } + if (app.id === "integration" && userdata.support !== true) { return null } var extraMessage = "" if (index == 2) { - extraMessage =
    + extraMessage =
    } delay += 75 @@ -9744,11 +9773,11 @@ const AngularWorkflow = (defaultprops) => { - Please provide an execution argument + Provide a runtime argument - At least one node in this workflow requires an execution argument. Please select one below, or provide a custom one in the text field next to the run button. + At least one node in this workflow requires a runtime argument ($exec). Please select one below, or provide a custom one in the text field next to the run button. {/*
    @@ -11709,13 +11738,78 @@ const AngularWorkflow = (defaultprops) => { } + const subflowtypes = [ + { + name: "Any", + }, + { + name: "Enrich", + } + ] return (
    -

    - {selectedTrigger.app_name} -

    - +

    + {selectedTrigger.app_name} +

    + + + + +
    { }, }} filterOptions={(options, { inputValue }) => { - console.log("Option contains?: ", inputValue, options) const lowercaseValue = inputValue.toLowerCase() options = options.filter(x => x.name.replaceAll("_", " ").toLowerCase().includes(lowercaseValue) || x.description.toLowerCase().includes(lowercaseValue)) @@ -12533,6 +12626,15 @@ const AngularWorkflow = (defaultprops) => { parsedvalue.actions = [] parsedvalue.authentication = {} selectedTrigger.app_association = parsedvalue + selectedTrigger.large_image = app.large_image + + if (cy !== undefined && cy !== null) { + const foundnode = cy.getElementById(selectedTrigger.id) + if (foundnode !== undefined && foundnode !== null) { + foundnode.data("large_image", app.large_image) + } + } + setUpdate(Math.random()); } }} @@ -14618,10 +14720,22 @@ const AngularWorkflow = (defaultprops) => { document.removeEventListener('keydown', handleKeyDown); } }, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]) + + if (isMobile) { + return null + } + return (
    { > Explore runs - {/* */}
    ) } @@ -15074,6 +15187,7 @@ const AngularWorkflow = (defaultprops) => { setLastSaved={setLastSaved} lastSaved={lastSaved} aiSubmit={aiSubmit} + listCache={listCache} apps={apps} expansionModalOpen={codeEditorModalOpen} @@ -15155,6 +15269,7 @@ const AngularWorkflow = (defaultprops) => { }; const unPublishWorkflow = (data) => { + data.id = props.match.params.key if (!isCloud) { toast("Function only supported on cloud") return @@ -15167,7 +15282,7 @@ const AngularWorkflow = (defaultprops) => { // This ALWAYS talks to Shuffle cloud data = JSON.parse(JSON.stringify(data)); - const url = `${globalUrl}/api/v1/workflows/${data.id}/unpublish`; + const url = `${globalUrl}/api/v1/workflows/${props.match.params.key}/unpublish`; fetch(url, { method: "POST", headers: { @@ -15214,12 +15329,19 @@ const AngularWorkflow = (defaultprops) => { const leftView = workflow.public === true ?
    - - {workflow.name} - + + + + {workflow.name} + + {workflow.validated === true ? + + + + : null} + This workflow is public and { saveWorkflow(workflow) @@ -15445,8 +15567,7 @@ const AngularWorkflow = (defaultprops) => { getSettings(); getFiles() - // For loading datastore - // listOrgCache(workflow.org_id) + // For loading datastore setUpdate(Math.random()); }} @@ -15470,6 +15591,29 @@ const AngularWorkflow = (defaultprops) => { > Unpublish Workflow + + {userdata.support === true ? + + + Manual Verification: {workflow.validated === undefined || workflow.validated === null || workflow.validated === false ? "Not valided" : "Validated"} + +
    + + Validate Workflow: + + { + workflow.validated = event.target.checked + workflow.user_editing = true + //setUserediting(true) + + saveWorkflow(workflow) + }} + /> +
    +
    + : null}
    : null} @@ -15966,7 +16110,10 @@ const AngularWorkflow = (defaultprops) => { const newitem = removeParam("execution_id", cursearch); navigate(curpath + newitem) }} - style={{ resize: "both", overflow: "auto", }} + style={{ + resize: "both", + overflow: "auto", + }} hideBackdrop={false} variant="temporary" BackdropProps={{ @@ -15978,8 +16125,8 @@ const AngularWorkflow = (defaultprops) => { style: { resize: "both", overflow: "auto", - minWidth: isMobile ? "100%" : 420, - maxWidth: isMobile ? "100%" : 420, + minWidth: isMobile ? "100%" : 490, + maxWidth: isMobile ? "100%" : 490, color: "white", fontSize: 18, borderLeft: theme.palette.defaultBorder, @@ -16033,7 +16180,7 @@ const AngularWorkflow = (defaultprops) => {
    + + {data.execution_source !== "default" && foundnotifications > 0 ? + + { + e.preventDefault() + e.stopPropagation() + window.open(`/admin?admin_tab=priorities&workflow=${data.workflow.id}&execution_id=${data.execution_id}`, "_blank") + }} + /> + + : null} + {lastExecution === data.execution_id ? ( {
    ) : ( -
    +
    { if (data.action.app_name === "Shuffle Tools" && data.action.id !== undefined && cy !== undefined) { const nodedata = cy.getElementById(data.action.id).data(); - if (nodedata !== undefined && nodedata !== null && nodedata.fillstyle === "linear-gradient") { + //if (nodedata !== undefined && nodedata !== null && nodedata.fillstyle === "linear-gradient") { + if (nodedata !== undefined && nodedata !== null) { var imgStyle = { marginRight: 20, width: imgsize, @@ -16696,7 +16865,7 @@ const AngularWorkflow = (defaultprops) => { actionimg = ( {data.action.app_name} { ) : null}
    -
    - - Status  - - - {data.status} - - {similarActionsView} -
    + + { data.status !== "SUCCESS" ? +
    + + Status  + + + {data.status} + + {similarActionsView} +
    + : null} + {validate.valid ? ( { app. - Want help help making or using this app?{" "} + Want to help the making of, or imrpvoe this app?{" "}
    { />
    ) : ( -
    +
    + + + Loading Workflow & Apps... + +
    ); // Awful way of handling scroll diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 6a1c577d..04e0d6be 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -2813,7 +2813,7 @@ const AppCreator = (defaultprops) => { paddingLeft: "10px", color: "white", height: 50, - borderRadius: 5, + borderRadius: theme.shape.borderRadius, }} inputProps={{ name: "age", @@ -3195,7 +3195,7 @@ const AppCreator = (defaultprops) => { }} value={parameterLocation} style={{ - borderRadius: 5, + borderRadius: theme.shape.borderRadius, backgroundColor: inputColor, paddingLeft: 10, color: "white", @@ -3883,7 +3883,7 @@ const AppCreator = (defaultprops) => { { style={{ backgroundColor: bgColor, color: "white", - borderRadius: 5, + borderRadius: theme.shape.borderRadius, minWidth: 80, marginRight: 10, marginTop: 2, diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index ae4c9b93..a1515105 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -418,7 +418,7 @@ const Apps = (props) => { minWidth: "100%", maxWidth: 612.5, marginBottom: 5, - borderRadius: 5, + borderRadius: theme.palette.borderRadius, color: "white", backgroundColor: surfaceColor, cursor: "pointer", @@ -600,8 +600,8 @@ const Apps = (props) => { // dropdown with copy etc I guess const AppPaper = (props) => { - const { app } = props - const data = app + const { app } = props + const data = app if (data.name === "" && data.id === "") { return null; @@ -631,6 +631,7 @@ const Apps = (props) => { data.large_image === undefined || data.large_image.length === 0 ? ( {data.title} { minWidth: viewWidth, maxWidth: viewWidth, color: "white", - borderRadius: 5, + borderRadius: theme.palette.borderRadius, backgroundColor: surfaceColor, //display: "flex", marginBottom: 10, @@ -2132,7 +2133,7 @@ const Apps = (props) => {
    { minWidth: viewWidth, maxWidth: viewWidth, color: "white", - borderRadius: 5, + borderRadius: theme.palette.borderRadius, //display: "flex", marginBottom: 10, overflow: "hidden", diff --git a/frontend/src/views/Search.jsx b/frontend/src/views/Search.jsx index b9b65f06..8994dbed 100644 --- a/frontend/src/views/Search.jsx +++ b/frontend/src/views/Search.jsx @@ -98,9 +98,11 @@ const Search = (props) => { display: 'none', }, customTab: { - justifyContent: 'center', - gap: '46px', - } + justifyContent: "center", + "& .MuiButtonBase-root": { + color: "white", + }, + }, }); const classes = useStyles(); @@ -128,7 +130,6 @@ const Search = (props) => { display: "flex", flexDirection: "column", overflowX: "hidden", - width: "100%", minHeight: 400, }; @@ -150,6 +151,8 @@ const Search = (props) => { document.title = "Shuffle - search - documentation"; } else if (newValue === 3) { document.title = "Shuffle - search - creators"; + } else if (newValue === 4) { + document.title = "Shuffle - search - Discord Chat"; } else { document.title = "Shuffle - search"; } @@ -202,14 +205,12 @@ const Search = (props) => { color: 'white' } - // Random names for type & autoComplete. Didn't research :^) const landingpageDataBrowser = (
    @@ -286,7 +287,7 @@ const Search = (props) => { label={ - Discord Chat + Discord Chat } /> diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 707249e7..bd655685 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -129,6 +129,7 @@ export const GetIconInfo = (action) => { // Finds the icon based on the action. Should be verbs. const iconList = [ { key: "cases", values: ["cases"] }, + { key: "communication", values: ["communication", "comms", "email",] }, { key: "cache_add", values: ["set_cache"] }, { key: "cache_get", values: ["get_cache"] }, { key: "filter", values: ["filter"] }, @@ -200,18 +201,20 @@ export const GetIconInfo = (action) => { { key: "close", values: ["close", "stop", "cancel", "block"] }, ]; - var selectedKey = ""; - if (action.name === undefined || action.name === null) { + var selectedKey = "" + if (action.app_name == "Integration Framework") { + selectedKey = "magic" + }else if (action.name === undefined || action.name === null) { } else { - const actionname = action.name.toLowerCase(); + const actionname = action.name.toLowerCase() for (var key in iconList) { //console.log(iconList[key], actionname) const found = iconList[key].values.find((value) => actionname.includes(value) - ); + ) if (found !== null && found !== undefined) { - selectedKey = iconList[key].key; - break; + selectedKey = iconList[key].key + break } } } @@ -223,8 +226,22 @@ export const GetIconInfo = (action) => { const defaultColor = "#f76b1c"; const defaultGradient = ["#fad961", "#f76b1c"]; const parsedIcons = { + magic: { + icon: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.9959.9959 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12z", + iconColor: "white", + iconBackgroundColor: "red", + originalIcon: "", + fillGradient: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#8A2BE2"], + }, + communication: { + icon: "M9.89516 7.71433H8.60945V5.1429H9.89516V7.71433ZM9.89516 10.2858H8.60945V9.00004H9.89516V10.2858ZM14.3952 2.57147H4.10944C3.76845 2.57147 3.44143 2.70693 3.20031 2.94805C2.95919 3.18917 2.82373 3.51619 2.82373 3.85719V15.4286L5.39516 12.8572H14.3952C14.7362 12.8572 15.0632 12.7217 15.3043 12.4806C15.5454 12.2395 15.6809 11.9125 15.6809 11.5715V3.85719C15.6809 3.14361 15.1023 2.57147 14.3952 2.57147Z", + iconColor: "white", + iconBackgroundColor: "#8acc3f", + originalIcon: "", + fillGradient: ["#8acc3f", "#459622"], + }, cases: { - icon: "M11 3C6.58 3 3 4.79 3 7C3 9.21 6.58 11 11 11C15.42 11 19 9.21 19 7C19 4.79 15.42 3 11 3ZM3 9V12C3 14.21 6.58 16 11 16C15.42 16 19 14.21 19 12V9C19 11.21 15.42 13 11 13C6.58 13 3 11.21 3 9ZM3 14V17C3 19.21 6.58 21 11 21C12.41 21 13.79 20.81 15 20.46V17.46C13.79 17.81 12.41 18 11 18C6.58 18 3 16.21 3 14ZM20 14V17H17V19H20V22H22V19H25V17H22V14", + icon: "M15.6408 8.39233H18.0922V10.0287H15.6408V8.39233ZM0.115234 8.39233H2.56663V10.0287H0.115234V8.39233ZM9.92083 0.21051V2.66506H8.28656V0.21051H9.92083ZM3.31839 2.25596L5.05889 4.00687L3.89856 5.16051L2.15807 3.42596L3.31839 2.25596ZM13.1485 3.99869L14.8808 2.25596L16.0493 3.42596L14.3088 5.16051L13.1485 3.99869ZM9.10369 4.30142C10.404 4.30142 11.651 4.81863 12.5705 5.73926C13.4899 6.65989 14.0065 7.90854 14.0065 9.21051C14.0065 11.0269 13.0178 12.6141 11.5551 13.4651V14.9378C11.5551 15.1548 11.469 15.3629 11.3158 15.5163C11.1625 15.6698 10.9547 15.756 10.738 15.756H7.46943C7.25271 15.756 7.04487 15.6698 6.89163 15.5163C6.73839 15.3629 6.6523 15.1548 6.6523 14.9378V13.4651C5.18963 12.6141 4.2009 11.0269 4.2009 9.21051C4.2009 7.90854 4.71744 6.65989 5.63689 5.73926C6.55635 4.81863 7.80339 4.30142 9.10369 4.30142ZM10.738 16.5741V17.3923C10.738 17.6093 10.6519 17.8174 10.4986 17.9709C10.3454 18.1243 10.1375 18.2105 9.92083 18.2105H8.28656C8.06984 18.2105 7.862 18.1243 7.70876 17.9709C7.55552 17.8174 7.46943 17.6093 7.46943 17.3923V16.5741H10.738ZM8.28656 14.1196H9.92083V12.3769C11.3345 12.0169 12.3722 10.7323 12.3722 9.21051C12.3722 8.34253 12.0279 7.5101 11.4149 6.89634C10.8019 6.28259 9.97056 5.93778 9.10369 5.93778C8.23683 5.93778 7.40546 6.28259 6.79249 6.89634C6.17953 7.5101 5.83516 8.34253 5.83516 9.21051C5.83516 10.7323 6.87292 12.0169 8.28656 12.3769V14.1196Z", iconColor: "white", iconBackgroundColor: "#8acc3f", originalIcon: "", @@ -1318,10 +1335,10 @@ const Workflows = (props) => { width: "100%", color: "white", padding: "12px 12px 0px 15px", - borderRadius: 5, display: "flex", boxSizing: "border-box", position: "relative", + borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.surfaceColor, }; @@ -3423,9 +3440,14 @@ const Workflows = (props) => { } if (data.app_name.toLowerCase() === "shuffle tools") { - data.large_image = theme.palette.defaultImage; + //data.large_image = theme.palette.defaultImage } + if (data.app_name.toLowerCase() === "integration framework") { + console.log("Skipping: ", data.app_name) + return null + } + const returnData = Date: Wed, 8 May 2024 17:21:12 +0000 Subject: [PATCH 106/142] link --- frontend/src/views/Docs.jsx | 183 ++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index e2b7e489..a79e26c4 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -301,11 +301,9 @@ const Docs = (defaultprops) => { continue } } - return items } - const SidebarPaperStyle = { backgroundColor: "rgb(26,26,26)", backgroundImage: "none", @@ -316,8 +314,12 @@ const Docs = (defaultprops) => { minHeight: "80vh", }; - const Heading = (props) => { + var id = props.children[0].toLowerCase().toString() + if (props.level <= 3) { + id = props.children[0].toLowerCase().toString().replaceAll(" ", "-"); + } + const element = React.createElement( `h${props.level}`, { style: { marginTop: props.level === 1 ? 20 : 50 } }, @@ -337,6 +339,7 @@ const Docs = (defaultprops) => { onMouseOver={() => { setHover(true); }} + id={id} > {props.level !== 1 ? ( { fetchDocs(props.match.params.key); } - const parseElementScroll = () => { - const offset = 45; - var parent = document.getElementById("markdown_wrapper_outer"); - if (parent !== null) { - //console.log("IN PARENT") - var elements = parent.getElementsByTagName("h2"); + // const parseElementScroll = () => { + // const offset = 45; + // var parent = document.getElementById("markdown_wrapper_outer"); + // if (parent !== null) { + // //console.log("IN PARENT") + // var elements = parent.getElementsByTagName("h2"); + // + // const name = window.location.hash + // .slice(1, window.location.hash.length) + // .toLowerCase() + // .split("%20") + // .join(" ") + // .split("_") + // .join(" ") + // .split("-") + // .join(" ") + // .split("?")[0] + // + // //console.log(name) + // var found = false; + // for (var key in elements) { + // const element = elements[key]; + // if (element.innerHTML === undefined) { + // continue; + // } + // + // // Fix location.. + // if (element.innerHTML.toLowerCase() === name) { + // //console.log(element.offsetTop) + // element.scrollIntoView({ behavior: "smooth" }); + // //element.scrollTo({ + // // top: element.offsetTop+offset, + // // behavior: "smooth" + // //}) + // found = true; + // //element.scrollTo({ + // // top: element.offsetTop-100, + // // behavior: "smooth" + // //}) + // } + // } + // + // // H# + // if (!found) { + // elements = parent.getElementsByTagName("h3"); + // //console.log("NAMe: ", name) + // found = false; + // for (key in elements) { + // const element = elements[key]; + // if (element.innerHTML === undefined) { + // continue; + // } + // + // // Fix location.. + // if (element.innerHTML.toLowerCase() === name) { + // element.scrollIntoView({ behavior: "smooth" }); + // //element.scrollTo({ + // // top: element.offsetTop-offset, + // // behavior: "smooth" + // //}) + // found = true; + // //element.scrollTo({ + // // top: element.offsetTop-100, + // // behavior: "smooth" + // //}) + // } + // } + // } + // } + // //console.log(element) + // + // //console.log("NAME: ", name) + // //console.log(document.body.innerHTML) + // // parent = document.getElementById(parent); + // + // //var descendants = parent.getElementsByTagName(tagname); + // + // // this.scrollDiv.current.scrollIntoView({ behavior: 'smooth' }); + // + // //$(".parent").find("h2:contains('Statistics')").parent(); + // }; - const name = window.location.hash - .slice(1, window.location.hash.length) - .toLowerCase() - .split("%20") - .join(" ") - .split("_") - .join(" ") - .split("-") - .join(" ") - .split("?")[0] - - //console.log(name) - var found = false; - for (var key in elements) { - const element = elements[key]; - if (element.innerHTML === undefined) { - continue; - } - - // Fix location.. - if (element.innerHTML.toLowerCase() === name) { - //console.log(element.offsetTop) - element.scrollIntoView({ behavior: "smooth" }); - //element.scrollTo({ - // top: element.offsetTop+offset, - // behavior: "smooth" - //}) - found = true; - //element.scrollTo({ - // top: element.offsetTop-100, - // behavior: "smooth" - //}) - } - } - - // H# - if (!found) { - elements = parent.getElementsByTagName("h3"); - //console.log("NAMe: ", name) - found = false; - for (key in elements) { - const element = elements[key]; - if (element.innerHTML === undefined) { - continue; - } - - // Fix location.. - if (element.innerHTML.toLowerCase() === name) { - element.scrollIntoView({ behavior: "smooth" }); - //element.scrollTo({ - // top: element.offsetTop-offset, - // behavior: "smooth" - //}) - found = true; - //element.scrollTo({ - // top: element.offsetTop-100, - // behavior: "smooth" - //}) - } - } - } - } - //console.log(element) - - //console.log("NAME: ", name) - //console.log(document.body.innerHTML) - // parent = document.getElementById(parent); - - //var descendants = parent.getElementsByTagName(tagname); - - // this.scrollDiv.current.scrollIntoView({ behavior: 'smooth' }); - - //$(".parent").find("h2:contains('Statistics')").parent(); - }; - - if (serverside !== true && window.location.hash.length > 0) { - parseElementScroll(); - } + // if (serverside !== true && window.location.hash.length > 0) { + // parseElementScroll(); + // } const markdownStyle = { color: "rgba(255, 255, 255, 0.90)", @@ -789,13 +792,12 @@ const Docs = (defaultprops) => { return (
    ( handleCollapse(index) )} + href={`#${data.id}`} > {data.title} {data.items.length > 0 ? ( @@ -809,10 +811,9 @@ const Docs = (defaultprops) => { {data.items.map((d, i) => { return ( {d.title} From 0708a51482066d1297d7e8b4fab94b711efd4f47 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 8 May 2024 23:05:32 +0200 Subject: [PATCH 107/142] Fixed an unecessary string replacement in App SDK --- backend/app_sdk/app_base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index d5194909..88b9a217 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -3428,11 +3428,11 @@ class AppBase: pass actionname = action["name"] + + # Loops in general goes in here to be parsed out as one->multi if len(actualitem) > 0: multiexecution = True - # Loop WITHOUT JSON variables go here. - # Loop WITH variables go in else. handled = False # Has a loop without a variable used inside @@ -3486,6 +3486,7 @@ class AppBase: if replacement.startswith("\"") and replacement.endswith("\""): replacement = replacement[1:len(replacement)-1] + #except json.decoder.JSONDecodeError as e: #self.logger.info("REPLACING %s with %s" % (key, replacement)) @@ -3515,7 +3516,7 @@ class AppBase: self.logger.info("(2) JSON ERROR IN FILE HANDLING: %s" % e) if not isfile: - tmpitem = tmpitem.replace("\\\\", "\\", -1) + #tmpitem = tmpitem.replace("\\\\", "\\", -1) resultarray.append(tmpitem) # With this parameter ready, add it to... a greater list of parameters. Rofl From 42f472996934543426f76e09c44475efc4719187 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Fri, 10 May 2024 09:37:40 +0000 Subject: [PATCH 108/142] scoll view for toc --- frontend/src/views/Docs.jsx | 233 +++++++++++++++++++++++++----------- 1 file changed, 166 insertions(+), 67 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index a79e26c4..9ad90b28 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; import { toast } from 'react-toastify'; import Markdown from 'react-markdown' @@ -35,6 +35,7 @@ import { FileCopy as FileCopyIcon } from "@mui/icons-material"; import { fontGrid } from "@mui/material/styles/cssUtils.js"; +import { active } from "d3"; const Body = { //maxWidth: 1000, @@ -54,17 +55,6 @@ const hrefStyle = { textDecoration: "none", }; -const hrefStyleToc = { - color: "rgba(255, 255, 255, 0.6)", - textDecoration: "none", - fontSize: "14px", - fontWeight: 400, - padding: "4px 0", - paddingLeft: "8px", - paddingRight: "8px", - lineHeight: "20px", -}; - const hrefStyleToc2 = { color: "rgba(255, 255, 255, 0.6)", @@ -87,8 +77,38 @@ const innerHrefStyle = { textDecoration: "none", }; +// Emma Goto +const useIntersectionObserver = (setActiveId) => { + const headingElementsRef = useRef({}) + const callback = (headings) => { + headingElementsRef.current = headings.reduce((map, headingElemet) => { + if (headingElemet.target.id != undefined || headingElemet.target.id != "") { + map[headingElemet.target.id] = headingElemet + return map + } + }, headingElementsRef.current) + const visibleHeadings = []; + Object.keys(headingElementsRef.current).forEach((key) => { + const headingElemet = headingElementsRef.current[key]; + if (headingElemet.isIntersecting) visibleHeadings.push(headingElemet) + }) + if (visibleHeadings.length > 0) { + setActiveId(visibleHeadings[0].target.id) + } + } + + const observer = new IntersectionObserver(callback, { + rootMargin: "10%", + }); + + const headingElements = Array.from(document.querySelectorAll("h2")) + if (headingElements.length != 0) { + headingElements.forEach((element) => observer.observe(element)); + return () => observer.disconnect() + } +} export const CopyToClipboard = (props) => { @@ -227,22 +247,18 @@ const Docs = (defaultprops) => { props.match = {} props.match.params = params - useEffect(() => { - //if (params["key"] === undefined) { - // navigate("/docs/about") - // return - //} - }, []) //console.log("PARAMS: ", params) const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false); const [data, setData] = useState(""); const [firstrequest, setFirstrequest] = useState(true); const [list, setList] = useState([]); + const [activeId, setActiveId] = useState(); const [isopen, setOpen] = useState(-1); const [hover, setHover] = useState(false); const [, setListLoaded] = useState(false); const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorElToc, setAnchorElToc] = React.useState(null); const [headingSet, setHeadingSet] = React.useState(false); const [selectedMeta, setSelectedMeta] = React.useState({ link: "hello", @@ -253,10 +269,23 @@ const Docs = (defaultprops) => { serverside === true ? "" : window.location.href ); + useEffect(() => { + //if (params["key"] === undefined) { + // navigate("/docs/about") + // return + //} + }, []) + + useIntersectionObserver(setActiveId); + function handleClick(event) { setAnchorEl(event.currentTarget); } + function handleClickToc(event) { + setAnchorElToc(event.currentTarget); + } + function handleCollapse(index) { setOpen(isopen === index ? -1 : index) } @@ -269,6 +298,21 @@ const Docs = (defaultprops) => { setAnchorEl(null); } + function handleCloseToc() { + setAnchorElToc(null); + } + + function scrollToHash() { + const hash = window.location.hash.replace("#", "") + if (hash) { + const element = document.getElementById(hash) + if (element) { + element.scrollIntoView({ behavior: 'smooth' }) + } + } + + } + function tocvalue(markdown) { const items = []; let currentMainItem = null; @@ -322,7 +366,7 @@ const Docs = (defaultprops) => { const element = React.createElement( `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 } }, + { style: { marginTop: props.level === 1 ? 20 : 50, scrollPaddingTop: "50px" }, id: `${id}` }, props.children ); @@ -361,8 +405,8 @@ const Docs = (defaultprops) => { width: "17%", position: "sticky", top: 50, - minHeight: "100vh", - maxHeight: "100vh", + minHeight: "93vh", + maxHeight: "93vh", overflowX: "hidden", overflowY: "auto", zIndex: 1000, @@ -399,6 +443,8 @@ const Docs = (defaultprops) => { }; const fetchDocs = (docId) => { + setActiveId("") + setTocLines([]) fetch(`${globalUrl}/api/v1/docs/${docId}`, { method: "GET", headers: { @@ -571,9 +617,9 @@ const Docs = (defaultprops) => { // //$(".parent").find("h2:contains('Statistics')").parent(); // }; - // if (serverside !== true && window.location.hash.length > 0) { - // parseElementScroll(); - // } + if (serverside !== true && window.location.hash.length > 0) { + scrollToHash() + } const markdownStyle = { color: "rgba(255, 255, 255, 0.90)", @@ -786,14 +832,27 @@ const Docs = (defaultprops) => { }
    -

    Table Of Content

    + {tocLines.length > 0 ? + ( +

    Table Of Content

    + + ) : null} -
    +
    ); @@ -841,7 +902,6 @@ const Docs = (defaultprops) => { flexDirection: "column", }; - const postDataMobile = list === undefined || list === null ? null : (
    @@ -856,28 +916,67 @@ const Docs = (defaultprops) => { >
    More docs
    - {list.map((data, index) => { - const item = data.name; - if (item === undefined) { - return null; - } + + {list.map((data, index) => { + const item = data.name; + if (item === undefined) { + return null; + } - const path = "/docs/" + item; - const newname = - item.charAt(0).toUpperCase() + - item.substring(1).split("_").join(" ").split("-").join(" "); - return ( - { - window.location.pathname = path; - }} - > - {newname} - - ); - })} + const path = "/docs/" + item; + const newname = + item.charAt(0).toUpperCase() + + item.substring(1).split("_").join(" ").split("-").join(" "); + return ( + { + window.location.pathname = path; + }} + > + {newname} + + ); + })} + + +
    + {tocLines.map((data, index) => { + return ( + + {data.title} + + ) + })} +
    {props.match.params.key === undefined ? mainpageInfo From 199058652d1d8a773dc3683de142765f0d8443f8 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Fri, 10 May 2024 10:47:57 +0000 Subject: [PATCH 109/142] edit button (scoll view stopped working) --- frontend/src/views/Docs.jsx | 101 +++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 9ad90b28..e6d8523d 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -95,12 +95,12 @@ const useIntersectionObserver = (setActiveId) => { }) if (visibleHeadings.length > 0) { + console.log(visibleHeadings) setActiveId(visibleHeadings[0].target.id) } } const observer = new IntersectionObserver(callback, { - rootMargin: "10%", }); const headingElements = Array.from(document.querySelectorAll("h2")) @@ -277,6 +277,7 @@ const Docs = (defaultprops) => { }, []) useIntersectionObserver(setActiveId); + console.log(activeId) function handleClick(event) { setAnchorEl(event.currentTarget); @@ -307,7 +308,7 @@ const Docs = (defaultprops) => { if (hash) { const element = document.getElementById(hash) if (element) { - element.scrollIntoView({ behavior: 'smooth' }) + element.scrollIntoView({ behavior: "instant" }) } } @@ -366,18 +367,98 @@ const Docs = (defaultprops) => { const element = React.createElement( `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50, scrollPaddingTop: "50px" }, id: `${id}` }, + { style: { marginTop: props.level === 1 ? 20 : 50 }, id: `${id}` }, props.children ); + const [hover, setHover] = useState(false); + var extraInfo = ""; + if (props.level === 1) { + extraInfo = ( +
    +
    + {mobile ? null : ( + + + + + + )} + {mobile ? null : ( +
    + )} + + {selectedMeta.read_time} minute + {selectedMeta.read_time === 1 ? "" : "s"} to read + +
    +
    + {mobile || + selectedMeta.contributors === undefined || + selectedMeta.contributors === null ? ( + "" + ) : ( +
    + {selectedMeta.contributors.slice(0, 7).map((data, index) => { + return ( + + + {data.url} + + + ); + })} +
    + )} +
    +
    + ); + } if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { extraInfo = "" } } - return ( { @@ -443,8 +524,6 @@ const Docs = (defaultprops) => { }; const fetchDocs = (docId) => { - setActiveId("") - setTocLines([]) fetch(`${globalUrl}/api/v1/docs/${docId}`, { method: "GET", headers: { @@ -632,9 +711,6 @@ const Docs = (defaultprops) => { fontSize: isMobile ? "1.3rem" : "1.1rem", }; - - - const CustomButton = (props) => { const { title, icon, link } = props @@ -783,7 +859,6 @@ const Docs = (defaultprops) => { if (item === undefined) { return null; } - const path = "/docs/" + item; const newname = item.charAt(0).toUpperCase() + @@ -843,6 +918,7 @@ const Docs = (defaultprops) => {
    { paddingRight: "8px", lineHeight: "20px", }} - onClick={() => ( + onClick={(e) => { handleCollapse(index) - )} - href={`#${data.id}`} + }} > {data.title} {data.items.length > 0 ? ( From 3765ff461a86d35808c356a508a5a936faa0ed0b Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Fri, 10 May 2024 12:07:08 +0000 Subject: [PATCH 110/142] each heading embedded by link --- frontend/src/views/Docs.jsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index e6d8523d..d7c6b35f 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -367,8 +367,8 @@ const Docs = (defaultprops) => { const element = React.createElement( `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 }, id: `${id}` }, - props.children + { id: `${id}` }, + props.children, ); const [hover, setHover] = useState(false); @@ -475,7 +475,18 @@ const Docs = (defaultprops) => { }} /> ) : null} - {element} +
    + {element} + +
    + {extraInfo} ) From 0328703821b84723d3453309fb6e50f18c33b952 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 10 May 2024 15:38:13 +0200 Subject: [PATCH 111/142] Pushing docs back --- frontend/src/views/Docs.jsx | 136 ++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 52 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index d7c6b35f..2a6b92d1 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -77,39 +77,6 @@ const innerHrefStyle = { textDecoration: "none", }; -// Emma Goto -const useIntersectionObserver = (setActiveId) => { - const headingElementsRef = useRef({}) - const callback = (headings) => { - headingElementsRef.current = headings.reduce((map, headingElemet) => { - if (headingElemet.target.id != undefined || headingElemet.target.id != "") { - map[headingElemet.target.id] = headingElemet - return map - } - }, headingElementsRef.current) - - const visibleHeadings = []; - Object.keys(headingElementsRef.current).forEach((key) => { - const headingElemet = headingElementsRef.current[key]; - if (headingElemet.isIntersecting) visibleHeadings.push(headingElemet) - }) - - if (visibleHeadings.length > 0) { - console.log(visibleHeadings) - setActiveId(visibleHeadings[0].target.id) - } - } - - const observer = new IntersectionObserver(callback, { - }); - - const headingElements = Array.from(document.querySelectorAll("h2")) - if (headingElements.length != 0) { - headingElements.forEach((element) => observer.observe(element)); - return () => observer.disconnect() - } -} - export const CopyToClipboard = (props) => { const { text, style, onCopy } = props; @@ -183,7 +150,6 @@ export const CodeHandler = (props) => { } // Need to check if it's singletick or multi - console.log("PROP: ", propvalue, props) if (props.inline === true) { // Show it inline return ( @@ -253,7 +219,7 @@ const Docs = (defaultprops) => { const [data, setData] = useState(""); const [firstrequest, setFirstrequest] = useState(true); const [list, setList] = useState([]); - const [activeId, setActiveId] = useState(); + const [activeId, setActiveId] = useState() const [isopen, setOpen] = useState(-1); const [hover, setHover] = useState(false); const [, setListLoaded] = useState(false); @@ -268,6 +234,9 @@ const Docs = (defaultprops) => { const [baseUrl, setBaseUrl] = React.useState( serverside === true ? "" : window.location.href ); + const [hashRendered, setHashRendered] = React.useState(false) + + const headingElementsRef = useRef({}) useEffect(() => { //if (params["key"] === undefined) { @@ -276,9 +245,6 @@ const Docs = (defaultprops) => { //} }, []) - useIntersectionObserver(setActiveId); - console.log(activeId) - function handleClick(event) { setAnchorEl(event.currentTarget); } @@ -296,25 +262,83 @@ const Docs = (defaultprops) => { } function handleClose() { - setAnchorEl(null); + setAnchorEl(null) } function handleCloseToc() { - setAnchorElToc(null); + setAnchorElToc(null) } - function scrollToHash() { + + // Emma Goto + const activeIdsetter = (id) => { + setActiveId(id) + } + + const intersectionObserver = () => { + const callback = (headings) => { + console.log("Headings: ", headings) + + headingElementsRef.current = headings.reduce((map, headingElement) => { + if (map === undefined) { + return {} + } + + if (headingElement === undefined || headingElement.target === undefined || headingElement.target.id === undefined || headingElement.target.id === null || headingElement.target.id === "") { + return map + } + + if (headingElement.target.id != undefined || headingElement.target.id != "") { + map[headingElement.target.id] = headingElement + return map + } + }, headingElementsRef.current) + + const visibleHeadings = [] + Object.keys(headingElementsRef.current).forEach((key) => { + const headingElemet = headingElementsRef.current[key] + if (headingElemet.isIntersecting) { + visibleHeadings.push(headingElemet) + } + }) + + if (visibleHeadings.length > 0) { + //setActiveId(visibleHeadings[0].target.id) + activeIdsetter(visibleHeadings[0].target.id) + } + } + + const observer = new IntersectionObserver(callback, { + }) + + const headingElements = Array.from(document.querySelectorAll("h2")) + if (headingElements.length != 0) { + headingElements.forEach((element) => observer.observe(element)) + return () => observer.disconnect() + } + } + + console.log("ACTIVEID: ", activeId) + + if (hashRendered) { + setTimeout(() => { + intersectionObserver() + }, 2000) + } + + const scrollToHash = () => { const hash = window.location.hash.replace("#", "") if (hash) { const element = document.getElementById(hash) if (element) { - element.scrollIntoView({ behavior: "instant" }) + element.scrollIntoView({ + behavior: "instant", + }) } } - } - function tocvalue(markdown) { + const tocvalue = (markdown) => { const items = []; let currentMainItem = null; @@ -360,6 +384,7 @@ const Docs = (defaultprops) => { }; const Heading = (props) => { + const [hover, setHover] = useState(false); var id = props.children[0].toLowerCase().toString() if (props.level <= 3) { id = props.children[0].toLowerCase().toString().replaceAll(" ", "-"); @@ -369,9 +394,14 @@ const Docs = (defaultprops) => { `h${props.level}`, { id: `${id}` }, props.children, - ); + ) - const [hover, setHover] = useState(false); + if (serverside !== true && window.location.hash.length > 0 && props.level === 1 && !hashRendered) { + setTimeout(() => { + setHashRendered(true) + scrollToHash() + }, 500) + } var extraInfo = ""; if (props.level === 1) { @@ -383,6 +413,7 @@ const Docs = (defaultprops) => { borderRadius: theme.palette.borderRadius, marginBottom: 30, display: "flex", + scrollPaddingTop: 20, }} >
    @@ -465,6 +496,9 @@ const Docs = (defaultprops) => { setHover(true); }} id={id} + style={{ + scrollPaddingTop: 20, + }} > {props.level !== 1 ? ( { marginBlock: "0.85em", alignItems: "center" }}> {element} - + }}> + +
    {extraInfo} @@ -707,10 +743,6 @@ const Docs = (defaultprops) => { // //$(".parent").find("h2:contains('Statistics')").parent(); // }; - if (serverside !== true && window.location.hash.length > 0) { - scrollToHash() - } - const markdownStyle = { color: "rgba(255, 255, 255, 0.90)", overflow: "hidden", From 51e07f72718eef48b1daef352560f758e50b65c8 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 10 May 2024 15:38:21 +0200 Subject: [PATCH 112/142] Added usecases --- frontend/src/views/Usecases.jsx | 1745 +++++++++++++++++++++++++++++++ 1 file changed, 1745 insertions(+) create mode 100644 frontend/src/views/Usecases.jsx diff --git a/frontend/src/views/Usecases.jsx b/frontend/src/views/Usecases.jsx new file mode 100644 index 00000000..34fe84c5 --- /dev/null +++ b/frontend/src/views/Usecases.jsx @@ -0,0 +1,1745 @@ +import React, { useState, useEffect } from "react" +import { useInterval } from "react-powerhooks" +import AppFramework from "../components/AppFramework.jsx" +import { makeStyles, } from "@mui/styles" +import classNames from "classnames" +import theme from '../theme.jsx' +import { useNavigate, Link, useParams } from "react-router-dom" +import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx" +import { ToastContainer, toast } from "react-toastify" +import { parsedDatatypeImages } from "../components/AppFramework.jsx" +import { findSpecificApp } from "../components/AppFramework.jsx" + +import { + Autocomplete, + Tooltip, + TextField, + IconButton, + Button, + Typography, + Grid, + Paper, + Chip, + Checkbox, +} from "@mui/material"; + +import { + Close as CloseIcon, + DoneAll as DoneAllIcon, + Description as DescriptionIcon, + PlayArrow as PlayArrowIcon, + Edit as EditIcon, + CheckBox as CheckBoxIcon, + CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon, + OpenInNew as OpenInNewIcon, +} from "@mui/icons-material"; + +import WorkflowPaper from "../components/WorkflowPaper.jsx" +import { removeParam } from "../views/AngularWorkflow.jsx" + +// core components +//import { +// chartExample1, +// chartExample2, +// chartExample3, +// chartExample4, +//} from "../charts.js"; + +import { + RadialBarChart, + RadialAreaChart, + RadialAxis, + StackedBarSeries, + TooltipArea, + ChartTooltip, + TooltipTemplate, + RadialAreaSeries, + RadialPointSeries, + RadialArea, + RadialLine, + TreeMap, + TreeMapSeries, + TreeMapLabel, + TreeMapRect, +} from 'reaviz'; + +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important", + }, + root: { + "& .MuiAutocomplete-listbox": { + border: "2px solid #f85a3e", + color: "white", + fontSize: 18, + "& li:nth-child(even)": { + backgroundColor: "#CCC", + }, + "& li:nth-child(odd)": { + backgroundColor: "#FFF", + }, + }, + }, + inputRoot: { + color: "white", + "&:hover .MuiOutlinedInput-notchedOutline": { + borderColor: "#f86a3e", + }, + }, +}); + + + +const UsecaseListComponent = (props) => { + const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, getFramework, setFrameworkData, } = props + + + const [expandedIndex, setExpandedIndex] = useState(-1); + const [expandedItem, setExpandedItem] = useState(-1); + const [inputUsecase, setInputUsecase] = useState({}); + + const [prevSubcase, setPrevSubcase] = useState({}) + + const [editing, setEditing] = useState(false); + const [description, setDescription] = useState(""); + const [video, setVideo] = useState(""); + const [blogpost, setBlogpost] = useState(""); + const [workflowOutline, setWorkflowOutline] = useState(""); + + const [selectedWorkflows, setSelectedWorkflows] = useState([]) + const [firstLoad, setFirstLoad] = useState(true) + const [apps, setApps] = useState([]) + + + const classes = useStyles(); + let navigate = useNavigate(); + + const [mitreTags, setMitreTags] = useState([]); + + + const parseUsecase = (subcase) => { + const srcdata = findSpecificApp(frameworkData, subcase.type) + const dstdata = findSpecificApp(frameworkData, subcase.last) + + if (srcdata !== undefined && srcdata !== null) { + subcase.srcimg = srcdata.large_image + subcase.srcapp = srcdata.name + } + + if (dstdata !== undefined && dstdata !== null) { + subcase.dstimg = dstdata.large_image + subcase.dstapp = dstdata.name + } + + return subcase + } + + useEffect(() => { + //console.log("Frameworkdata changed. Use to set inputUsecase: ", frameworkData, prevSubcase) + if (frameworkData === undefined || prevSubcase === undefined) { + return + } + + var parsedUsecase = inputUsecase + const subcase = parseUsecase(prevSubcase) + + parsedUsecase.srcimg = subcase.srcimg + parsedUsecase.srcapp = subcase.srcapp + parsedUsecase.dstimg = subcase.dstimg + parsedUsecase.dstapp = subcase.dstapp + + setInputUsecase(parsedUsecase) + }, [frameworkData]) + + const loadApps = () => { + fetch(`${globalUrl}/api/v1/apps`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + return response.json(); + }) + .then((responseJson) => { + if (responseJson === null) { + console.log("null-response from server") + const pretend_apps = [{ + "name": "TBD", + "app_name": "TBD", + "app_version": "TBD", + "description": "TBD", + "version": "TBD", + "large_image": "", + }] + + setApps(pretend_apps) + return + } + + if (responseJson.success === false) { + console.log("error loading apps: ", responseJson) + return + } + + setApps(responseJson); + }) + .catch((error) => { + console.log("App loading error: " + error.toString()); + }) + } + + useEffect(() => { + loadApps() + }, []) + + if (keys === undefined || keys === null || keys.length === 0) { + return null + } + + + + // Timeout 50ms to delay it slightly + const getUsecase = (subcase, index, subindex) => { + subcase = parseUsecase(subcase) + setPrevSubcase(subcase) + + fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + return response.json(); + }) + .then((responseJson) => { + var parsedUsecase = responseJson + + if (responseJson.success === false) { + parsedUsecase = subcase + } else { + parsedUsecase = responseJson + + parsedUsecase.srcimg = subcase.srcimg + parsedUsecase.srcapp = subcase.srcapp + parsedUsecase.dstimg = subcase.dstimg + parsedUsecase.dstapp = subcase.dstapp + } + + // Look for the type of app and fill in img1, srcapp... + setInputUsecase(parsedUsecase) + setExpandedIndex(index) + setExpandedItem(subindex) + + setTimeout(() => { + const found = document.getElementById("selected_box"); + if (found !== undefined && found !== null) { + //console.log("FOUND!!") + + //found.scrollTo({ + // top: 100, + // behavior: "smooth", + //}) + } else { + //console.log("NOT FOUND!!") + } + + setFirstLoad(true) + setSelectedWorkflows([]) + }, 100) + }) + .catch((error) => { + //toast(error.toString()); + setInputUsecase({}) + setExpandedIndex(index) + setExpandedItem(subindex) + + setFirstLoad(true) + setSelectedWorkflows([]) + }) + } + + const setUsecaseItem = (inputUsecase) => { + var parsedUsecase = inputUsecase + + if (blogpost !== inputUsecase.blogpost) { + inputUsecase.blogpost = blogpost + parsedUsecase.blogpost = blogpost + } + + if (video !== inputUsecase.video) { + inputUsecase.video = video + parsedUsecase.video = video + } + + if (description !== inputUsecase.description) { + inputUsecase.description = description + parsedUsecase.description = description + } + + if (mitreTags !== inputUsecase.mitre) { + inputUsecase.mitre = mitreTags + parsedUsecase.mitre = mitreTags + } + + if (workflowOutline !== inputUsecase.workflow_outline) { + inputUsecase.workflow_outline = workflowOutline + parsedUsecase.workflow_outline = workflowOutline + } + + fetch(globalUrl + "/api/v1/workflows/usecases", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(parsedUsecase), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + //toast("Failed updating: " + responseJson.reason) + } else { + //toast("Failed to update framework for your org.") + } + } else { + //toast("Updated usecase.") + } + }) + .catch((error) => { + //toast(error.toString()); + //setFrameworkLoaded(true) + }) + } + + const setWorkflow = (workflowdata) => { + const new_url = `${globalUrl}/api/v1/workflows/${workflowdata.id}` + + fetch(new_url, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(workflowdata), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for workflows :O!"); + return; + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Error updating workflow: ", responseJson.reason) + } else { + toast("Error updating workflow.") + } + + return + } + + return responseJson; + }) + .catch((error) => { + toast("Problem setting workflow: ", error.toString()); + }); + }; + + return ( +
    + + Shuffle usecases + + + Usecases in Shuffle are divided into {keys.length} type{keys.length === 1 ? "" : "s"}. + + {keys.map((usecase, index) => { + return ( +
    + + {usecase.name} + + + {usecase.list.map((subcase, subindex) => { + const selectedItem = subindex === expandedItem && index === expandedIndex + + if (subcase.matches === undefined || subcase.matches === null) { + subcase.matches = [] + } else { + if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) { + setFirstLoad(false) + setSelectedWorkflows(subcase.matches) + + } + } + + if (selectedItem && subcase.name !== undefined && inputUsecase.name !== undefined) { + if (subcase.name.toLowerCase().replaceAll(" ", "_") === inputUsecase.name.toLowerCase().replaceAll(" ", "_")) { + if (inputUsecase.description !== undefined && inputUsecase.description !== null) { + subcase.description = inputUsecase.description + } + + if (inputUsecase.blogpost !== undefined && inputUsecase.blogpost !== null) { + subcase.blogpost = inputUsecase.blogpost + } + + if (inputUsecase.video !== undefined && inputUsecase.video !== null) { + subcase.video = inputUsecase.video + } + + if (inputUsecase.extra_buttons !== undefined && inputUsecase.extra_buttons !== null) { + subcase.extra_buttons = inputUsecase.extra_buttons + } + + if (inputUsecase.workflow_outline !== undefined && inputUsecase.workflow_outline !== null) { + subcase.workflow_outline = inputUsecase.workflow_outline + } + } + } + + const finished = subcase.matches.length > 0 + const backgroundColor = theme.palette.surfaceColor + const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}` + + const fixedName = subcase.name.toLowerCase().replace("_", " ") + + return ( + { + if (fixedName === "increase authentication") { + getUsecase(subcase, index, subindex) + return + } + + //setSelectedWorkflows([]) + if (selectedItem) { + } else { + getUsecase(subcase, index, subindex) + navigate(`/usecases?selected_object=${fixedName}`) + } + }}> + { + }}> + {!selectedItem ? +
    + + {subcase.name} + + {finished ? + + { + }} + > + + + + : null} + {subcase.blogpost !== null && subcase.blogpost !== undefined && subcase.blogpost.length > 0 ? + + + { + }} + > + + + + + : null} + {subcase.video !== null && subcase.video !== undefined && subcase.video.length > 0 ? + + + { + }} + > + + + + + : null} +
    + : +
    + + {subcase.name} + +
    + {isLoggedIn === true ? + + { + setEditing(true) + if (subcase.description !== undefined && subcase.description !== null) { + setDescription(subcase.description) + } + + if (subcase.blogpost !== undefined && subcase.blogpost !== null) { + setBlogpost(subcase.blogpost) + } + + if (subcase.video !== undefined && subcase.video !== null) { + setVideo(subcase.video) + } + + if (subcase.mitre !== undefined && subcase.mitre !== null) { + setMitreTags(subcase.mitre) + } + + if (subcase.workflow_outline !== undefined && subcase.workflow_outline !== null) { + setWorkflowOutline(subcase.workflow_outline) + } else { + setWorkflowOutline("") + } + }} + > + + + + : null} + {subcase.blogpost !== null && subcase.blogpost !== undefined && subcase.blogpost.length > 0 ? + + + { + }} + > + + + + + : null} + {subcase.video !== null && subcase.video !== undefined && subcase.video.length > 0 ? + + + { + }} + > + + + + + : null} + + { + setExpandedItem(-1) + setExpandedIndex(-1) + setEditing(false) + setInputUsecase({}) + }} + > + + + +
    +
    + {editing ? +
    + { + setDescription(event.target.value) + }} + id="descriptionEditng" + /> + { + setBlogpost(event.target.value) + }} + id="blogpostEditing" + /> + { + setVideo(event.target.value) + }} + id="videoEditing" + /> + { + setWorkflowOutline(event.target.value) + }} + id="workflowOutline" + tabIndex="-1" + /> + +
    + + +
    +
    + : +
    + + {subcase.description} + + + {workflows !== undefined && workflows !== null && workflows.length > 0 ? + + Select relevant workflows + + : + + + Find workflows related to this usecase: + + + + + + + + + } + + {workflows !== undefined && workflows !== null && workflows.length > 0 ? + option.id === value.id} + getOptionLabel={(option) => { + + 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; + }} + fullWidth + style={{ + backgroundColor: theme.palette.inputColor, + height: 50, + borderRadius: theme.palette.borderRadius, + }} + onChange={(event, newValue) => { + //handleWorkflowSelectionUpdate({ target: { value: newValue} }) + //setSelectedWorkflows= + //var newvalue = [] + //for (var key in newValue) { + // if (newValue[key].id !== undefined) { + // newvalue.push(newValue[key].id) + // } + //} + + // Doing this way as you may want to remove some too + for (var key in workflows) { + if (!newValue.find(data => data.id === workflows[key].id)) { + // Check if it has the one in it + if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) { + const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name) + if (filtered !== undefined && filtered !== null) { + workflows[key]["usecase_ids"] = filtered + + setWorkflow(workflows[key]) + } + } + + continue + } + + if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) { + workflows[key]["usecase_ids"] = [subcase.name] + setWorkflow(workflows[key]) + + } else if (!workflows[key]["usecase_ids"].includes(subcase.name)) { + workflows[key]["usecase_ids"].push(subcase.name) + setWorkflow(workflows[key]) + + } + } + + setWorkflows(workflows) + setSelectedWorkflows(newValue) + //setUpdate(Math.random()) + }} + renderOption={(props, data, state) => { + var newname = data.name + if (newname === undefined || newname === null) { + newname = "placeholder" + } + + if (newname.length > 2) { + newname = newname.charAt(0).toUpperCase() + newname.substring(1) + } + return ( +
  • + + {data.image !== undefined && data.image !== null && data.image.length > 0 ? + {newname} + : null} + + Choose {newname} + + + } placement="bottom"> + + } + checkedIcon={} + style={{ marginRight: 8 }} + checked={selectedWorkflows.find(wf => wf.id === data.id) !== undefined} + /> + {newname} + + +
  • + ) + }} + renderInput={(params) => { + return ( + + ); + }} + /> + : null} + + {}} + > + Try it out: + + {frameworkData !== undefined && frameworkData !== null && Object.keys(frameworkData).length > 0 ? + + : null} + + {/* + + + {subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ? +
    + {}}> + Examples + +
    + {subcase.extra_buttons.map((subdata, index) => { + var highlight = false + var baseTypeInfo = subcase.type !== undefined ? subcase.type : "communication" + if (frameworkData !== undefined && frameworkData !== null) { + if (frameworkData[baseTypeInfo] !== undefined && frameworkData[baseTypeInfo] !== null && subdata.app !== undefined && subdata.app !== null) { + if (frameworkData[baseTypeInfo].name !== undefined && frameworkData[baseTypeInfo].name.toLowerCase().replaceAll("_", " ") === subdata.app.toLowerCase().replaceAll("_", " ")) { + highlight = true + } + } + } + + var marginTop = 6 + if (subdata.name.includes(" ") && subdata.name.length > 10) { + marginTop = 0 + } + + return ( + +
    + + + {subdata.name} + +
    +
    + ) + })} +
    +
    + : null} + + */} +
    + } +
    + +
    +
    +
    + } +
    +
    + ) + })} +
    +
    + ) + })} +
    + ) +} + +const TreeChart = ({keys}) => { + const [hovered, setHovered] = useState(""); + + return ( +
    { + console.log("Click: ", hovered) + }}> + { + return info.color + }} + label={ + + } + rect={ + { + console.log("Click: ", event) + }} + /> + } + /> + } + /> +
    + ) + //axis={} +} + + +const RadialChart = ({keys, setSelectedCategory}) => { + const [hovered, setHovered] = useState(""); + + return ( +
    { + console.log("Click: ", hovered) + if (setSelectedCategory !== undefined) { + setSelectedCategory(hovered) + } + }}> + } + series={ + { + return '#f86a3e' + }} + animated={false} + id="workflow_series_id" + style={{cursor: "pointer",}} + line={ + { + console.log("INFO: ", data, color) + return ( + null + ) + }} + /> + } + tooltip={ + { + if (hovered !== event.value.x) { + setHovered(event.value.x) + } + }} + tooltip={ + { + return ( +
    + + {data.x} + +
    + ) + /* + + ) + */ + } + } + /> + } + /> + + } + /> + } + /> +
    + ) + //axis={} +} + +// This is the start of a dashboard that can be used. +// What data do we fill in here? Idk +const Dashboard = (props) => { + const { globalUrl, isLoggedIn, userdata } = props; + //const alert = useAlert(); + const [bigChartData, setBgChartData] = useState("data1"); + const [dayAmount, setDayAmount] = useState(7); + const [firstRequest, setFirstRequest] = useState(true); + const [stats, setStats] = useState({}); + const [changeme, setChangeme] = useState(""); + const [statsRan, setStatsRan] = useState(false); + const [keys, setKeys] = useState([]) + const [treeKeys, setTreeKeys] = useState([]) + + const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState(""); + const [selectedUsecases, setSelectedUsecases] = useState([]); + const [usecases, setUsecases] = useState([]); + const [workflows, setWorkflows] = useState([]); + const [frameworkData, setFrameworkData] = useState(undefined); + + let navigate = useNavigate(); + const isCloud = + window.location.host === "localhost:3002" || + window.location.host === "shuffler.io"; + + + useEffect(() => { + if (selectedUsecaseCategory.length === 0) { + setSelectedUsecases(usecases) + } else { + const foundUsecase = usecases.find(data => data.name === selectedUsecaseCategory) + if (foundUsecase !== undefined && foundUsecase !== null) { + setSelectedUsecases([foundUsecase]) + } + } + }, [selectedUsecaseCategory]) + + const checkSelectedParams = () => { + const urlSearchParams = new URLSearchParams(window.location.search) + const params = Object.fromEntries(urlSearchParams.entries()) + + const curpath = typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname; + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + + const foundQuery = params["selected"] + if (foundQuery !== null && foundQuery !== undefined) { + setSelectedUsecaseCategory(foundQuery) + + const newitem = removeParam("selected", cursearch); + navigate(curpath + newitem) + } + + /* + const baseItem = document.getElementById("increase authentication") + if (baseItem !== undefined && baseItem !== null) { + baseItem.click() + + // Find close window button -> go to top + const foundButton = document.getElementById("close_selection") + if (foundButton !== undefined && foundButton !== null) { + foundButton.click() + } + + // Scroll back to top + window.scrollTo(0, 0) + } + */ + + const foundQuery2 = params["selected_object"] + if (foundQuery2 !== null && foundQuery2 !== undefined) { + // Take a random object, quickly click it, then go to this one + // Something is weird with loading apps without it + + const queryName = foundQuery2.toLowerCase().replaceAll("_", " ") + // Waiting a bit for it to render + setTimeout(() => { + const foundItem = document.getElementById(queryName) + if (foundItem !== undefined && foundItem !== null) { + foundItem.click() + // Scroll to it + + setTimeout(() => { + foundItem.scrollIntoView({ + behavior: "smooth", + block: "center", + inline: "center" + }) + }, 100) + } else { + //console.log("Couldn't find item with name ", queryName) + } + }, 1000); + } + + } + + useEffect(() => { + if (usecases.length > 0) { + //console.log(usecases) + checkSelectedParams() + } + }, [usecases]) + + const getFramework = () => { + fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + const preparedData = { + "siem": findSpecificApp({}, "SIEM"), + "communication": findSpecificApp({}, "COMMUNICATION"), + "assets": findSpecificApp({}, "ASSETS"), + "cases": findSpecificApp({}, "CASES"), + "network": findSpecificApp({}, "NETWORK"), + "intel": findSpecificApp({}, "INTEL"), + "edr": findSpecificApp({}, "EDR"), + "iam": findSpecificApp({}, "IAM"), + "email": findSpecificApp({}, "EMAIL"), + } + + console.log("Got error for framework! ", preparedData) + setFrameworkData(preparedData) + } else { + setFrameworkData(responseJson) + } + }) + .catch((error) => { + toast(error.toString()); + }) + } + + + const getAvailableWorkflows = () => { + fetch(globalUrl + "/api/v1/workflows", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + fetchUsecases() + console.log("Status not 200 for workflows :O!: ", response.status); + return; + } + + return response.json(); + }) + .then((responseJson) => { + fetchUsecases(responseJson) + + if (responseJson !== undefined) { + setWorkflows(responseJson); + } + }) + .catch((error) => { + fetchUsecases() + //toast(error.toString()); + }); + } + + + document.title = "Shuffle - usecases"; + var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; + var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; + + const handleKeysetting = (categorydata) => { + var allCategories = [] + var treeCategories = [] + for (key in categorydata) { + const category = categorydata[key] + allCategories.push({"key": category.name, "data": category.list.length, "color": category.color}) + treeCategories.push({"key": category.name, "data": 100, "color": category.color,}) + for (var subkey in category.list) { + treeCategories.push({"key": category.list[subkey].name, "data": 20, "color": category.color}) + } + } + + setKeys(allCategories) + setTreeKeys(treeCategories) + } + + const fetchUsecases = (workflows) => { + fetch(globalUrl + "/api/v1/workflows/usecases", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for usecases"); + } + + return response.json(); + }) + .then((responseJson) => { + // Matching workflows with usecases + if (responseJson.success === false) { + return + } + + if (workflows !== undefined && workflows !== null && workflows.length > 0) { + var categorydata = responseJson + var newcategories = [] + for (var key in categorydata) { + var category = categorydata[key] + category.matches = [] + + for (var subcategorykey in category.list) { + var subcategory = category.list[subcategorykey] + subcategory.matches = [] + + for (var workflowkey in workflows) { + const workflow = workflows[workflowkey] + + if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) { + for (var usecasekey in workflow.usecase_ids) { + if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) { + + category.matches.push({ + "workflow": workflow.id, + "category": subcategory.name, + }) + + subcategory.matches.push(workflow) + break + } + } + } + + if (subcategory.matches.length > 0) { + break + } + } + } + + newcategories.push(category) + } + + console.log("CATEGORIES: ", newcategories) + + if (newcategories !== undefined && newcategories !== null && newcategories.length > 0) { + handleKeysetting(newcategories) + setUsecases(newcategories) + setSelectedUsecases(newcategories) + } else { + handleKeysetting(responseJson) + setUsecases(responseJson) + setSelectedUsecases(responseJson) + } + } + }) + .catch((error) => { + //toast("ERROR: " + error.toString()); + console.log("ERROR: " + error.toString()); + }); + }; + + useEffect(() => { + getAvailableWorkflows() + getFramework() + }, []); + + const fetchdata = (stats_id) => { + fetch(globalUrl + "/api/v1/stats/" + stats_id, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for " + stats_id); + } + + return response.json(); + }) + .then((responseJson) => { + stats[stats_id] = responseJson; + setStats(stats); + // Used to force updates + setChangeme(stats_id); + }) + .catch((error) => { + //toast("ERROR: " + error.toString()); + console.log("ERROR: " + error.toString()); + }); + }; + + let chart1_2_options = { + maintainAspectRatio: false, + legend: { + display: false, + }, + tooltips: { + backgroundColor: "#f5f5f5", + titleFontColor: "#333", + bodyFontColor: "#666", + bodySpacing: 4, + xPadding: 12, + mode: "nearest", + intersect: 0, + position: "nearest", + }, + responsive: true, + scales: { + yAxes: [ + { + barPercentage: 1.6, + gridLines: { + drawBorder: false, + color: "rgba(29,140,248,0.0)", + zeroLineColor: "transparent", + }, + ticks: { + suggestedMin: 60, + suggestedMax: 125, + padding: 20, + fontColor: "#9a9a9a", + }, + }, + ], + xAxes: [ + { + barPercentage: 1.6, + gridLines: { + drawBorder: false, + color: "rgba(29,140,248,0.1)", + zeroLineColor: "transparent", + }, + ticks: { + padding: 20, + fontColor: "#9a9a9a", + }, + }, + ], + }, + }; + + const dayGraph = { + data: (canvas) => { + let ctx = canvas.getContext("2d"); + + let gradientStroke = ctx.createLinearGradient(0, 230, 0, 50); + + gradientStroke.addColorStop(1, "rgba(29,140,248,0.2)"); + gradientStroke.addColorStop(0.4, "rgba(29,140,248,0.0)"); + gradientStroke.addColorStop(0, "rgba(29,140,248,0)"); //blue colors + + return { + labels: dayGraphLabels, + datasets: [ + { + label: "My First dataset", + fill: true, + backgroundColor: gradientStroke, + borderColor: "#1f8ef1", + borderWidth: 2, + borderDash: [], + borderDashOffset: 0.0, + pointBackgroundColor: "#1f8ef1", + pointBorderColor: "rgba(255,255,255,0)", + pointHoverBackgroundColor: "#1f8ef1", + pointBorderWidth: 20, + pointHoverRadius: 4, + pointHoverBorderWidth: 15, + pointRadius: 4, + data: dayGraphData, + }, + ], + }; + }, + options: chart1_2_options, + }; + + // All these are currently tracked. + const variables = [ + "backend_executions", + "workflow_executions", + "workflow_executions_aborted", + "workflow_executions_success", + "total_apps_created", + "total_apps_loaded", + "openapi_apps_created", + "total_apps_deleted", + "total_webhooks_ran", + "total_workflows", + "total_workflow_actions", + "total_workflow_triggers", + ]; + + const runUpdate = () => { + for (var key in variables) { + fetchdata(variables[key]); + } + }; + + // Refresh every 60 seconds + const autoUpdate = 60000; + const { start, stop } = useInterval({ + duration: autoUpdate, + startImmediate: false, + callback: () => { + runUpdate(); + }, + }); + + if (firstRequest) { + setFirstRequest(false); + //start(); + //runUpdate(); + } else if (!statsRan) { + // FIXME: Run this under runUpdate schedule? + // 1. Fix labels in dayGraphy.data + // 2. Add data to the daygraph + + // Every time there's an update :) + + // This should probably be done in the backend.. bleh + if ( + stats["workflow_executions"] !== undefined && + stats["workflow_executions"] !== null && + stats["workflow_executions"].data !== undefined + ) { + setStatsRan(true); + //console.log("NEW DATA?: ", stats) + console.log("SET WORKFLOW: ", stats["workflow_executions"]); + //var curday = startDate.getDate() + + // Index = what day are we on + + // 0 = today + var newDayGraphLabels = []; + var newDayGraphData = []; + for (var i = dayAmount; i > 0; i--) { + var enddate = new Date(); + enddate.setDate(-i); + enddate.setHours(23, 59, 59, 999); + + var startdate = new Date(); + startdate.setDate(-i); + startdate.setHours(0, 0, 0, 0); + + var endtime = enddate.getTime() / 1000; + var starttime = startdate.getTime() / 1000; + + console.log( + "START: ", + starttime, + "END: ", + endtime, + "Data: ", + stats["workflow_executions"] + ); + for (var key in stats["workflow_executions"].data) { + const item = stats["workflow_executions"]["data"][key]; + console.log("ITEM: ", item.timestamp, endtime); + console.log(endtime - starttime); + if ( + endtime - starttime > endtime - item.timestamp && + endtime.timestamp >= 0 + ) { + console.log("HIT? "); + } + console.log(item.timestamp - endtime); + //console.log(item.timestamp-endtime) + break; + if (item.timestamp > endtime && item.timestamp < starttime) { + if (newDayGraphData[i - 1] === undefined) { + newDayGraphData[i - 1] = 1; + } else { + newDayGraphData[i - 1] += 1; + } + + //break + } + } + + newDayGraphLabels.push(i); + } + + console.log(newDayGraphLabels); + console.log(newDayGraphData); + } + } + + const newdata = + Object.getOwnPropertyNames(stats).length > 0 ? ( +
    + Autoupdate every {autoUpdate / 1000} seconds + {variables.map((data) => { + if (stats[data] === undefined || stats[data] === null) { + return null; + } + + if (stats[data].total === undefined) { + return null; + } + + return ( +
    + {data}: {stats[data].total} +
    + ); + })} +
    + ) : null + + console.log("USECASES: ", usecases) + + const data = +
    +
    + {keys.length > 0 ? + + : null} +
    + + {usecases !== null && usecases !== undefined && usecases.length > 0 ? +
    + {usecases.map((usecase, index) => { + return ( + { + console.log("Clicked: ", usecase.name) + if (selectedUsecaseCategory === usecase.name) { + setSelectedUsecaseCategory("") + } else { + setSelectedUsecaseCategory(usecase.name) + } + //addFilter(usecase.name.slice(3,usecase.name.length)) + }} + variant="outlined" + color="primary" + /> + ) + })} +
    + : null} + + + + {treeKeys.length > 0 ? + + : null} + + {newdata} +
    + + const dataWrapper = +
    + {data} +
    + + + return dataWrapper +}; + +export default Dashboard; From 7a1529463d429d9971935e0fddf70490fd47394c Mon Sep 17 00:00:00 2001 From: Frikky Date: Sat, 11 May 2024 00:36:14 +0200 Subject: [PATCH 113/142] UI fixes across the board :) --- frontend/src/components/AppGrid.jsx | 4 +- frontend/src/components/EditWorkflow.jsx | 108 +++++++++- frontend/src/components/NewHeader.jsx | 163 +++++++++++---- frontend/src/components/Oauth2Auth.jsx | 2 +- frontend/src/components/ParsedAction.jsx | 17 +- frontend/src/components/SearchData.jsx | 17 +- frontend/src/components/Searchfield.jsx | 2 +- .../src/components/ShuffleCodeEditor1.jsx | 2 +- frontend/src/defaultCytoscapeStyle.jsx | 2 +- frontend/src/views/Admin.jsx | 4 +- frontend/src/views/AngularWorkflow.jsx | 196 +++++++++++------- frontend/src/views/Apps.jsx | 11 +- frontend/src/views/Workflows.jsx | 18 +- 13 files changed, 404 insertions(+), 142 deletions(-) diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index dfd13210..befdf4c9 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -225,7 +225,7 @@ const AppGrid = (props) => { }} autoComplete="off" color="primary" - placeholder="Find Apps" + placeholder="Search more than 2500 Apps" id="shuffle_search_field" onChange={(event) => { setSearchQuery(event.currentTarget.value); @@ -989,7 +989,7 @@ const AppGrid = (props) => { }} autoComplete="off" color="primary" - placeholder="Find Apps" + placeholder="Search more than 2500 Apps" id="shuffle_search_field" onChange={(event) => { setSearchQuery(event.currentTarget.value); diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 298c0511..9ac7fabe 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -11,9 +11,9 @@ import { Badge, Avatar, Grid, - InputLabel, - Select, - ListSubheader, + InputLabel, + Select, + ListSubheader, Paper, Tooltip, Divider, @@ -22,6 +22,7 @@ import { IconButton, Menu, MenuItem, + Link, FormControlLabel, Chip, Switch, @@ -543,6 +544,7 @@ const EditWorkflow = (props) => { margin="dense" fullWidth /> + { innerWorkflow.default_return_value = event.target.value @@ -563,6 +565,106 @@ const EditWorkflow = (props) => { fullWidth /> + + MSSP Suborg Distribution (beta - contact support@shuffler.io) + + {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? + userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ? + + You can only distribute to suborgs from a parent org. + + : + + : + + + Create a sub-org to distribute workflows to suborgs. + + + } + + Input fields diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index edf0ae24..da13952d 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -27,6 +27,7 @@ import { } from "@mui/material"; import { + Close as CloseIcon, MeetingRoom as MeetingRoomIcon, HelpOutline as HelpOutlineIcon, Settings as SettingsIcon, @@ -40,6 +41,7 @@ import { Add as AddIcon, Analytics as AnalyticsIcon, Lightbulb as LightbulbIcon, + ExpandMore as ExpandMoreIcon, } from "@mui/icons-material"; const hoverColor = "#f85a3e"; @@ -58,8 +60,11 @@ const Header = (props) => { isMobile, serverside, setModalOpen, + + curpath, } = props; + const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); const [SoarHoverColor, setSoarHoverColor] = useState(hoverOutColor); const [LoginHoverColor, setLoginHoverColor] = useState(hoverOutColor); @@ -70,6 +75,7 @@ const Header = (props) => { const [anchorElAvatar, setAnchorElAvatar] = React.useState(null); const [subAnchorEl, setSubAnchorEl] = React.useState(null); const [upgradeHovered, setUpgradeHovered] = React.useState(false); + const [showTopbar, setShowTopbar] = useState(false) let navigate = useNavigate(); const handleClick = (event) => { @@ -188,7 +194,15 @@ const Header = (props) => { removeCookie("session_token", { path: "/" }); removeCookie("session_token", { path: "/" }); removeCookie("session_token", { path: "/" }); + + removeCookie("__session", { path: "/" }); + removeCookie("__session", { path: "/" }); + removeCookie("__session", { path: "/" }); + + removeCookie("__session", { path: "/" }); window.location.pathname = "/"; + + localStorage.setItem("globalUrl", "") }) .catch((error) => { console.log(error); @@ -266,7 +280,7 @@ const Header = (props) => { : null, cursor: "pointer", marginRight: 10, - }; + } image = foundOrg.image === "" ? ( @@ -355,14 +369,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 ? (
    - + Notifications generated made by Shuffle to help you discover issues or improvements. Learn more @@ -462,7 +475,9 @@ const Header = (props) => { .then(function (response) { if (response.status !== 200) { console.log("Error in response"); - } + } else { + localStorage.setItem("apps", []) + } return response.json(); }) @@ -502,7 +517,7 @@ const Header = (props) => { rel="noopener noreferrer" target="_blank" > - + { userdata.avatar !== null && userdata.avatar.length > 0 ? userdata.avatar - : ""; + : "" const avatarMenu = ( @@ -545,6 +560,7 @@ const Header = (props) => { alt="Your username here" src={parsedAvatar} /> + { handleClose(); }} > - Organisation + Organization @@ -574,6 +590,18 @@ const Header = (props) => { Account + + + { + handleClose(); + }} + > + Notifications + + + + {/*notificationMenu*/} { }} > {avatarMenu} - {notificationMenu} - {/*supportMenu*/} + {/*notificationMenu*/} + {supportMenu} {logoCheck} @@ -1050,7 +1078,7 @@ const Header = (props) => { src={data.image} style={imageStyle} /> - ); + ) var regiontag = "eu"; if ( @@ -1164,32 +1192,20 @@ const Header = (props) => { onMouseOver={() => { setUpgradeHovered(true) }} onMouseOut={() => { setUpgradeHovered(false) }} onClick={() => { - // if (isCloud) { - // ReactGA.event({ - // category: "header", - // action: "pricing_upgrade_click", - // label: "", - // }); - // } - setModalOpen(true); + if (isCloud) { + ReactGA.event({ + category: "header", + action: "upgrade_popup_click", + }) + } - // if (window.drift !== undefined) { - // if (isCloud) { - // window.drift.api.startInteraction({ - // interactionId: 386404, - // }) - // } else { - // window.drift.api.startInteraction({ - // interactionId: 386403, - // }) - // } - // } + setModalOpen(true) }} > {upgradeHovered ? - isCloud ? "Upgrade License" : "Upgrade License" + "Upgrade License" : - isCloud ? "Upgrade" : "Upgrade" + "Upgrade" } @@ -1420,8 +1436,82 @@ 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: "", + }) + + //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") + + //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") + + //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 ? -
    +
    { backgroundColor: theme.palette.backgroundColor, }} > + + {topbar} +
    {loginTextBrowser}
    diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 6e775652..fe8562f5 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -407,7 +407,7 @@ const AuthenticationOauth2 = (props) => { if (offlineAccess === true && !scopes.includes("offline_access")) { console.log("IN scope 2") - if (!authenticationType.redirect_uri.includes("google")) { + if (!authenticationType.redirect_uri.includes("google") && !authenticationType.redirect_uri.includes("slack")) { console.log("Appending offline access") scopes.push("offline_access") } diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 13e67d7a..1bbe88f5 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -3237,16 +3237,23 @@ const ParsedAction = (props) => { }} defaultValue={selectedAction.app_version} onChange={(event) => { - console.log("VAL: ", event.target.value) - console.log("App: ", selectedApp) const newversion = selectedApp.versions.find( (tmpApp) => tmpApp.version == event.target.value - ); + ) - console.log("NEWVERSION: ", newversion); if (newversion !== undefined && newversion !== null) { - getApp(newversion.id, true); + getApp(newversion.id, true) } + + // Change in all actions in the workflow at the same time and add a toast.success() about it + for (var actionkey in workflow.actions) { + const action = workflow.actions[actionkey] + if (action.app_name === selectedAction.app_name) { + workflow.actions[actionkey].app_version = event.target.value + } + } + + toast.success("Changed version of all nodes to "+event.target.value) }} style={{ marginTop: 10, diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx index 39c110d8..35e7b9e3 100644 --- a/frontend/src/components/SearchData.jsx +++ b/frontend/src/components/SearchData.jsx @@ -24,6 +24,11 @@ import { import ArticleIcon from '@mui/icons-material/Article'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import ManageSearchIcon from '@mui/icons-material/ManageSearch'; + +import { + VerifiedUser as VerifiedUserIcon, +} from '@mui/icons-material' + import { AvatarGroup, } from "@mui/material" @@ -233,9 +238,11 @@ const SearchData = props => { const appGroup = hit.action_references === undefined || hit.action_references === null ? [] : hit.action_references const avatar = baseImage + var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}` parsedUrl += `?queryID=${hit.__queryID}` + const validated = hit.validated !== undefined && hit.validate !== null ? hit.validated : false // return ( @@ -271,7 +278,15 @@ const SearchData = props => { setMouseHoverIndex(index) }}> - {avatar} + {validated === true ? + + + + : + + {avatar} + + }
    { > {isHeader ?
    Search for Docs, Apps, Workflows and more -
    diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 7251b27f..d00d9740 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -1698,7 +1698,7 @@ const CodeEditor = (props) => { Test output - + {executionResult.result} diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index 76840b9c..6cb8a814 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -8,7 +8,7 @@ const data = [ return "" } - elementname = elementname.replace("_", " ", -1) + elementname = elementname.replaceAll("_", " ", -1) elementname = elementname.charAt(0).toUpperCase() + elementname.slice(1) return elementname }, diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index a6c35e7f..c24b6ca7 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1608,7 +1608,9 @@ If you're interested, please let me know a time that works for you, or set up a .then(function (response) { if (response.status !== 200) { console.log("Error in response"); - } + } else { + localStorage.setItem("apps", []) + } return response.json(); }) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3f25507b..c64652fb 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -176,36 +176,6 @@ export const triggers = [ long_description: "Create a schedule based on cron", id: "", }, - /*{ - name: "Office365", - type: "TRIGGER", - status: "uninitialized", - description: "O365 email trigger", - trigger_type: "EMAIL", - errors: null, - is_valid: true, - label: "Email", - environment: "cloud", - large_image: - "data:image/jpg;base64,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", - long_description: "Execute a workflow when you get an email", - id: "", - }, - { - name: "Gmail", - type: "TRIGGER", - status: "uninitialized", - description: "Gmail email trigger", - trigger_type: "EMAIL", - errors: null, - is_valid: true, - label: "Email", - environment: "cloud", - large_image: - "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAzFodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QTIyMjgyMEYwMDJDMTFFQkJBOEE5OUJBM0MzMTA2RDIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QTIyMjgyMTAwMDJDMTFFQkJBOEE5OUJBM0MzMTA2RDIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozQTMwMDQxRTAwMEUxMUVCQkE4QTk5QkEzQzMxMDZEMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBMjIyODIwRTAwMkMxMUVCQkE4QTk5QkEzQzMxMDZEMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAwICAwMCAwMDAwMEBwUEBAQECQYHBQcKCQsL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", - long_description: "Execute a workflow when you get an email", - id: "", - },*/ { name: "Shuffle Workflow", type: "TRIGGER", @@ -622,6 +592,25 @@ const AngularWorkflow = (defaultprops) => { "multiline": true, }] }, + { + "name": "IAM", + "description": "Available actions for IAM", + "label": "IAM", + "parameters": [{ + "name": "action", + "value": "get_kms_key", + "options": [ + "get_kms_key", + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }] + }, ] }] @@ -1578,7 +1567,13 @@ const AngularWorkflow = (defaultprops) => { useworkflow = curworkflow; } - var cyelements = cy.elements(); + + + var cyelements = [] + if (cy !== undefined && cy !== null) { + cyelements = cy.elements() + } + var newActions = []; var newTriggers = []; var newBranches = []; @@ -5335,10 +5330,10 @@ const AngularWorkflow = (defaultprops) => { } setWorkflow(workflow); - if (data.type === "TRIGGER") { - saveWorkflow(workflow); - } - }; + //if (data.type === "TRIGGER") { + // saveWorkflow(workflow); + //} + } //var previouskey = 0 const handleKeyDown = (event) => { @@ -6831,8 +6826,6 @@ const AngularWorkflow = (defaultprops) => { if (selectedNode.data().type === "TRIGGER") { - console.log("Should remove trigger!"); - console.log(selectedNode.data()); const triggerindex = workflow.triggers.findIndex( (data) => data.id === selectedNode.data().id ); @@ -8498,18 +8491,14 @@ const AngularWorkflow = (defaultprops) => { }; const SearchBox = ({ currentRefinement, refine, isSearchStalled, }) => { - - useEffect(() => { - if (document !== undefined) { - const appsearchValue = document.getElementById("appsearch") - if (appsearchValue !== undefined && appsearchValue !== null) { - if (appsearchValue.value !== undefined && appsearchValue.value !== null && appsearchValue.value.length > 0) { - refine(appsearchValue.value) - } - } - //} - } - }, []) + if (document !== undefined) { + const appsearchValue = document.getElementById("appsearch") + if (appsearchValue !== undefined && appsearchValue !== null) { + if (appsearchValue.value !== undefined && appsearchValue.value !== null && appsearchValue.value.length > 0) { + refine(appsearchValue.value) + } + } + } return ( { @@ -8532,9 +8521,6 @@ const AngularWorkflow = (defaultprops) => { placeholder="Find Public Apps, Workflows, Documentation and more" value={currentRefinement} id="shuffle_search_field" - onClick={(event) => { - console.log("Click!") - }} onBlur={(event) => { //setSearchOpen(false) }} @@ -8575,7 +8561,7 @@ const AngularWorkflow = (defaultprops) => { const clickedApp = (hit) => { - toast(`Activating App. Please wait a moment.`) + toast.success(`Activating App. Please wait a moment.`) const queryID = hit.__queryID @@ -8624,6 +8610,8 @@ const AngularWorkflow = (defaultprops) => { : hits.map((hit, index) => { + console.log("HIT: ", hit) + const innerlistitemStyle = { width: positionInfo.width + 35, overflowX: "hidden", @@ -8821,7 +8809,7 @@ const AngularWorkflow = (defaultprops) => { }} > - Click one of the relevant public apps below to Activate it for your organisation. + Click one of the relevant public apps below to Activate it for your organization. { console.log("CLICKED") @@ -14233,7 +14221,9 @@ const AngularWorkflow = (defaultprops) => { .then(function (response) { if (response.status !== 200) { console.log("Error in response"); - } + } else { + localStorage.setItem("apps", []) + } return response.json(); }) @@ -15762,18 +15752,24 @@ const AngularWorkflow = (defaultprops) => { }; const getExecutionSourceImage = (execution) => { + // This is the playbutton at 150x150 const defaultImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACOCAMAAADkWgEmAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAWlBMVEX4Wj69TDgmKCvkVTwlJyskJiokJikkJSkjJSn4Ykf+6+f5h3L////8xLr5alH/9fT7nYz4Wz/919H5cVn/+vr8qpv4XUL94d35e2X//v38t6v4YUbkVDy8SzcVIzHLAAAAAWJLR0QMgbNRYwAAAAlwSFlzAAARsAAAEbAByCf1VAAAAAd0SU1FB+QGGgsvBZ/GkmwAAAFKSURBVHja7dlrTgMxDEXhFgpTiukL2vLc/zbZQH5N7MmReu4KPmlGN4m9WgGzfhgtaOZxM1rQztNoQDvPowHtTKMB7WxHA2TJkiVLlixIZMmSRYgsWbIIkSVLFiGyZMkiRNZirBcma/eKZEW87ZGsOBxPRFbE+R3Jio/LlciKuH0iWfH1/UNkRSR3RRYruSvyWKldkcjK7IpUVl5X5LLSuiKbldQV6aycrihgZXRFCau/K2pY3V1RxersijJWX1cUsnq6opLV0RW1rNldUc2a2RXlrHldsQBrTlfcLwv5EZm/PLIgkHXKPHyQRzXzYoO8BjIvzcgnBvJBxny+Ih/7zNEIcpDEHLshh5TIkS5zAI5cFzCXK8hVFHNxh1xzQpfC0BV6XWTJkkWILFmyCJElSxYhsmTJIkSWLFmEyJIlixBZsmQB8stk/U3/Yb49pVcDMg4AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMDYtMjZUMTE6NDc6MDUrMDI6MDD8QCPmAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIwLTA2LTI2VDExOjQ3OjA1KzAyOjAwjR2bWgAAAABJRU5ErkJggg=="; const size = 40; + const borderRadius = 5 if (execution.execution_source === undefined || execution.execution_source === null || execution.execution_source.length === 0) { return ( default - ); + ) } if (execution.execution_source === "webhook") { @@ -15784,7 +15780,11 @@ const AngularWorkflow = (defaultprops) => { triggers.find((trigger) => trigger.trigger_type === "WEBHOOK") .large_image } - style={{ width: size, height: size }} + style={{ + width: size, + height: size, + borderRadius: borderRadius, + }} /> ); } else if (execution.execution_source === "outlook") { @@ -15795,7 +15795,11 @@ const AngularWorkflow = (defaultprops) => { triggers.find((trigger) => trigger.trigger_type === "EMAIL") .large_image } - style={{ width: size, height: size }} + style={{ + width: size, + height: size, + borderRadius: borderRadius, + }} /> ); } else if (execution.execution_source === "schedule") { @@ -15806,7 +15810,11 @@ const AngularWorkflow = (defaultprops) => { triggers.find((trigger) => trigger.trigger_type === "SCHEDULE") .large_image } - style={{ width: size, height: size }} + style={{ + width: size, + height: size, + borderRadius: borderRadius, + }} /> ); } else if (execution.execution_source === "EMAIL") { @@ -15817,7 +15825,11 @@ const AngularWorkflow = (defaultprops) => { triggers.find((trigger) => trigger.trigger_type === "EMAIL") .large_image } - style={{ width: size, height: size }} + style={{ + width: size, + height: size, + borderRadius: borderRadius, + }} /> ); } else if (execution.execution_source === "ShuffleGPT") { @@ -15841,7 +15853,11 @@ const AngularWorkflow = (defaultprops) => { triggers.find((trigger) => trigger.trigger_type === "SUBFLOW") .large_image } - style={{ width: size, height: size }} + style={{ + width: size, + height: size, + borderRadius: borderRadius, + }} /> ); } @@ -15850,7 +15866,11 @@ const AngularWorkflow = (defaultprops) => { {execution.execution_source} ); }; @@ -16343,16 +16363,22 @@ const AngularWorkflow = (defaultprops) => { maxHeight: 40, }} /> -
    - {getExecutionSourceImage(data)} -
    + +
    + {getExecutionSourceImage(data)} +
    +
    { {data.workflow.actions !== null ? (
    { marginBottom: "auto", }} > - {successActions}/{skippedActions}/{calculatedResult} + {successActions} / {skippedActions > 0 ? skippedActions : {skippedActions}} / {calculatedResult}
    ) : null}
    - {data.execution_source !== "default" && foundnotifications > 0 ? + {foundnotifications > 0 ? { if (triggers.length > 2) { if (data.action.app_name === "shuffle-subflow") { - const parsedImage = triggers[4].large_image; + const parsedImage = triggers[2].large_image; actionimg = ( {"Shuffle { actionimg = ( {"Shuffle { } } + if (result.status === 429) { + return "Rate limit exceeded. Consider using a different API key or wait a bit before trying again." + } + + if (result.status === 405) { + return "Method not allowed. Check the URL to ensure it has all the required parameters. If you keep getting a 405, please forward a screenshot of this to support@shuffler.io" + } + + if (result.status === 415) { + return "Content-Type header missing or wrong. Please add the correct Content-Type header and save the workflow." + } + if (result.status === 401) { return "Authentication failed (401). The URL or auth key is wrong. Check the body of the result for more information." } @@ -17566,7 +17604,7 @@ const AngularWorkflow = (defaultprops) => { {curapp === null ? null : ( {selectedResult.action.app_name} { valid = "false"; } - if (data.actions === undefined || data.actions === null) { - data.actions = [] + if (data.actions === undefined || data.actions === null) { + // Check if data type undefined/bool + if (typeof data === "boolean") { + data = {} } + + data.actions = [] + } if (data === undefined || data.actions === undefined || data.actions === null || data.actions.length === 0) { - valid = "false"; + valid = "false" } var description = data.description; diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index bd655685..32fc5509 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -646,7 +646,7 @@ const Workflows = (props) => { if (sidebar === null || sidebar === undefined) { console.log("No sidebar defined") - localStorage.setItem(sidebarKey, "open"); + localStorage.setItem(sidebarKey, "open"); setDrawerOpen(true) } else { if (sidebar === "open") { @@ -2009,11 +2009,11 @@ const Workflows = (props) => { style={{ display: "flex", flexDirection: "column", width: "100%" }} > - +
    { - addFilter(orgId); + navigate("/admin") }} > {image} @@ -2022,13 +2022,13 @@ const Workflows = (props) => { {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.name} - : null} - - Edit {data.name} - + {data.name} + : null} + + Edit '{data.name}' +
    - } placement="left"> + } placement="right"> Date: Mon, 13 May 2024 08:23:34 +0000 Subject: [PATCH 114/142] improvment over Docs headings --- frontend/src/components/ScrollToTop.jsx | 3 +- frontend/src/views/Docs.jsx | 160 +++++++++++++++--------- 2 files changed, 105 insertions(+), 58 deletions(-) diff --git a/frontend/src/components/ScrollToTop.jsx b/frontend/src/components/ScrollToTop.jsx index a023181b..87c44ec9 100755 --- a/frontend/src/components/ScrollToTop.jsx +++ b/frontend/src/components/ScrollToTop.jsx @@ -24,7 +24,8 @@ function ScrollToTop({ getUserNotifications, curpath, setCurpath, history }) { // Custom handler for certain scroll mechanics // //console.log("OLD: ", curpath, "NeW: ", window.location.pathname) - if (curpath === window.location.pathname && curpath === "/usecases") { + if (curpath === window.location.pathname && (curpath === "/usecases" || + (curpath === "/docs" && location.hash.length > 0))) { } else { window.scroll({ diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 2a6b92d1..44785cd9 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -51,7 +51,7 @@ const Body = { const dividerColor = "rgb(225, 228, 232)"; const hrefStyle = { - color: "rgba(255, 255, 255, 0.40)", + color: "rgba(255, 255, 255, 0.8)", textDecoration: "none", }; @@ -219,6 +219,7 @@ const Docs = (defaultprops) => { const [data, setData] = useState(""); const [firstrequest, setFirstrequest] = useState(true); const [list, setList] = useState([]); + const [lastHeading, setLastHeading] = useState(false); const [activeId, setActiveId] = useState() const [isopen, setOpen] = useState(-1); const [hover, setHover] = useState(false); @@ -245,48 +246,40 @@ const Docs = (defaultprops) => { //} }, []) - function handleClick(event) { + const handleClick = (event) => { setAnchorEl(event.currentTarget); } - function handleClickToc(event) { + const handleClickToc = (event) => { setAnchorElToc(event.currentTarget); } - function handleCollapse(index) { + const handleCollapse = (index) => { setOpen(isopen === index ? -1 : index) } - function handleMouseOver() { + const handleMouseOver = () => { setHover(!hover); } - function handleClose() { + const handleClose = () => { setAnchorEl(null) } - function handleCloseToc() { + const handleCloseToc = () => { setAnchorElToc(null) } - - // Emma Goto - const activeIdsetter = (id) => { - setActiveId(id) - } - const intersectionObserver = () => { const callback = (headings) => { - console.log("Headings: ", headings) - headingElementsRef.current = headings.reduce((map, headingElement) => { - if (map === undefined) { - return {} - } + if (map === undefined) { + return {} + } - if (headingElement === undefined || headingElement.target === undefined || headingElement.target.id === undefined || headingElement.target.id === null || headingElement.target.id === "") { - return map - } + if (headingElement === undefined || headingElement.target === undefined || headingElement.target.id === undefined || headingElement.target.id === null || headingElement.target.id === "") { + return map + } if (headingElement.target.id != undefined || headingElement.target.id != "") { map[headingElement.target.id] = headingElement @@ -295,49 +288,92 @@ const Docs = (defaultprops) => { }, headingElementsRef.current) const visibleHeadings = [] - Object.keys(headingElementsRef.current).forEach((key) => { - const headingElemet = headingElementsRef.current[key] - if (headingElemet.isIntersecting) { - visibleHeadings.push(headingElemet) - } - }) - + const keys = Object.keys(headingElementsRef.current) + + for (var i = 0; i < keys.length; i++) { + const headingElement = headingElementsRef.current[keys[i]] + if (headingElement.isIntersecting) { + visibleHeadings.push(headingElement) + } + } + if (visibleHeadings.length > 0) { - //setActiveId(visibleHeadings[0].target.id) - activeIdsetter(visibleHeadings[0].target.id) + const tocs = document.getElementsByClassName('toc') + for (const t of tocs) { + const currentPoint = t.getElementsByTagName('a')[0] + currentPoint.style.color = "white"; + if (`#${visibleHeadings[0].target.id}` + === currentPoint.hash) { + currentPoint.style.color = "#f86a3e"; + } + } } } - + const observer = new IntersectionObserver(callback, { + threshold: [1] }) - + const headingElements = Array.from(document.querySelectorAll("h2")) if (headingElements.length != 0) { - headingElements.forEach((element) => observer.observe(element)) + for (var i = 0; i < headingElements.length; i++) { + var element = headingElements[i] + observer.observe(element) + } return () => observer.disconnect() } } - console.log("ACTIVEID: ", activeId) + if (lastHeading) { + setTimeout(() => { + intersectionObserver() + }, 100) + } - if (hashRendered) { - setTimeout(() => { - intersectionObserver() - }, 2000) - } const scrollToHash = () => { const hash = window.location.hash.replace("#", "") if (hash) { const element = document.getElementById(hash) if (element) { - element.scrollIntoView({ - behavior: "instant", - }) + element.scrollIntoView({ + behavior: "instant", + }) } } } + + // extract toc from all headings and subheadings + const extractHeadings = (content) => { + const headings = []; + + const headingRegex = /^(#{2,3})\s+(.+)$/gm; + let match; + + while ((match = headingRegex.exec(content)) !== null) { + const level = match[1].length; + const title = match[2].trim(); + + if (level === 2) { + headings.push({ + id: title.toLowerCase().replace(/\s+/g, '-'), + title: title, + items: [] + }); + } else if (level === 3 && headings.length > 0) { + const lastHeading = headings[headings.length - 1]; + lastHeading.items.push({ + id: title.toLowerCase().replace(/\s+/g, '-'), + title: title + }); + } + } + return headings + } + + + // extract TOC from actual markdown const tocvalue = (markdown) => { const items = []; let currentMainItem = null; @@ -346,7 +382,7 @@ const Docs = (defaultprops) => { for (let i = 0; i < lines.length; i++) { const line = lines[i]; - if (line.startsWith('* [')) { + if (line.startsWith('* [') && !line.startsWith(" ", 5)) { const matches = line.match(/^\* \[([^)]+)\]\(#([^)]+)\)/); if (matches) { currentMainItem = { @@ -396,12 +432,18 @@ const Docs = (defaultprops) => { props.children, ) - if (serverside !== true && window.location.hash.length > 0 && props.level === 1 && !hashRendered) { - setTimeout(() => { - setHashRendered(true) - scrollToHash() - }, 500) - } + if (serverside !== true && window.location.hash.length > 0 && props.level === 1 && !hashRendered) { + setTimeout(() => { + setHashRendered(true) + scrollToHash() + }, 500) + } + + if (serverside !== true && !lastHeading) { + setTimeout(() => { + setLastHeading(true) + }, 500) + } var extraInfo = ""; if (props.level === 1) { @@ -413,7 +455,6 @@ const Docs = (defaultprops) => { borderRadius: theme.palette.borderRadius, marginBottom: 30, display: "flex", - scrollPaddingTop: 20, }} >
    @@ -496,9 +537,9 @@ const Docs = (defaultprops) => { setHover(true); }} id={id} - style={{ - scrollPaddingTop: 20, - }} + style={{ + scrollPaddingTop: 20, + }} > {props.level !== 1 ? ( { paddingLeft: "0.3em", rotate: "-30deg", paddingTop: "0.9em", display: props.level === 1 ? "none" : "block", }}> - - + +
    {extraInfo} @@ -533,6 +574,7 @@ const Docs = (defaultprops) => { width: "17%", position: "sticky", top: 50, + paddingTop: "0.25em", minHeight: "93vh", maxHeight: "93vh", overflowX: "hidden", @@ -587,7 +629,7 @@ const Docs = (defaultprops) => { if (responseJson.success && responseJson.reason !== undefined) { // Find tags and translate them into ![]() format const imgRegex = / { responseJson.reason !== undefined && responseJson.reason !== null ) { + /* const values = tocvalue(responseJson.reason.match(tocRegex) .join() .toString()); setTocLines(values); + */ + const values = extractHeadings(newdata) + setTocLines(values) } } else { setData("# Error\nThis page doesn't exist."); @@ -1134,7 +1180,7 @@ const Docs = (defaultprops) => { // Padding and zIndex etc set because of footer in cloud. const loadedCheck = ( -
    +
    {postDataBrowser} {postDataMobile}
    From 3b0ecbb5500a8a8b5cd617a3caf8ba4951d83d82 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 13 May 2024 14:34:36 +0200 Subject: [PATCH 115/142] Minor fixes --- frontend/src/components/AppGrid.jsx | 6 +- frontend/src/components/OrgHeaderexpanded.jsx | 552 +----------------- frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/Workflows.jsx | 49 +- 4 files changed, 50 insertions(+), 559 deletions(-) diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index befdf4c9..0f3fedef 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -989,7 +989,7 @@ const AppGrid = (props) => { }} autoComplete="off" color="primary" - placeholder="Search more than 2500 Apps" + placeholder="Search your Activated or Self-built apps" id="shuffle_search_field" onChange={(event) => { setSearchQuery(event.currentTarget.value); @@ -1008,7 +1008,7 @@ const AppGrid = (props) => { useEffect(() => { if (currTab === 2) { const baseUrl = globalUrl; - const userAppsUrl = `${baseUrl}/api/v1/users/apps`; + const userAppsUrl = `${baseUrl}/api/v1/users/me/apps`; fetch(userAppsUrl, { method: "GET", credentials: "include", @@ -1559,8 +1559,8 @@ const AppGrid = (props) => { //Component to fetch all apps created by user and Org const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => { - const [searchQuery, setSearchQuery] = useState(""); + const [searchQuery, setSearchQuery] = useState(""); const [appsToShow, setAppsToShow] = useState([]); useEffect(() => { if (currTab === 1) { diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 6946de94..19352b66 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -5,7 +5,7 @@ import theme from '../theme.jsx'; import { toast } from "react-toastify" import Chip from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; -import AuthenticationData from "./AuthenticationWindow"; +import SubflowSuggestions from "../components/SubflowSuggestions.jsx"; import { FormControl, @@ -45,7 +45,7 @@ const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important", }, -}); +}) const OrgHeaderexpanded = (props) => { const { @@ -113,6 +113,7 @@ const OrgHeaderexpanded = (props) => { ? "" : selectedOrganization.sso_config.sso_certificate ); + const [notificationWorkflow, setNotificationWorkflow] = React.useState( selectedOrganization.defaults === undefined ? "" @@ -120,7 +121,7 @@ const OrgHeaderexpanded = (props) => { selectedOrganization.defaults.notification_workflow.length === 0 ? "" : selectedOrganization.defaults.notification_workflow - ); + ) const [documentationReference, setDocumentationReference] = React.useState( selectedOrganization.defaults === undefined @@ -166,25 +167,6 @@ const OrgHeaderexpanded = (props) => { const [workflows, setWorkflows] = React.useState([]) const [workflow, setWorkflow] = React.useState({}) - // notification workflow - const [notificationWorkflowModal, setNotificationWorkflowModal] = React.useState(false); - const [selectedAppDetails, setSelectedAppDetails] = React.useState({}); - const [notificationWorkflowTestModal, setNotificationWorkflowTestModal] = React.useState(false); - const [selectedAuth, setSelectedAuth] = React.useState(''); - const [emailData,setEmailData] = React.useState([]); - const [notificationAppDetails, setNotificationAppDetails] = React.useState([]); - const [generatedWorkflow, setGeneatedWorkflow] = React.useState({}); - - - // for jira & email modal - const [textFieldValue, setTextFieldValue] = React.useState(""); - const [textFieldOneValue, setTextFieldOneValue] = React.useState(""); - - useEffect(() => { - let nameList = notificationAppList.length > 0? notificationAppList.map(item => item.name): ["email"]; - prepareNotificationAppList(nameList) - }, [notificationAppList,workflows]); - const getAvailableWorkflows = (trigger_index) => { fetch(globalUrl + "/api/v1/workflows", { method: "GET", @@ -351,142 +333,13 @@ const OrgHeaderexpanded = (props) => { }) } - // getting comms & cases app from app framework - var notificationAppList = []; - if (selectedOrganization.security_framework.cases && selectedOrganization.security_framework.cases.name.length > 0) { - notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.cases); - } - if (selectedOrganization.security_framework.communication && selectedOrganization.security_framework.communication.name.length > 0) { - notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.communication); - } - const mergeAuthData = (result, responseJson) => { - const updatedResult = result.map(item => { - const matches = responseJson.filter(authItem => authItem.app.name === item.name); - return { - ...item, - authentication_data: matches.length > 0 ? matches : null - }; - }); - return updatedResult; - }; + - const prepareNotificationAppList = async (appList) => { - // getting App ID,Authentication fields and saved auths for each app - var result = [] - 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) { - const filteredApps = responseJson.filter(app => appList.includes(app.name)); - const emailData = responseJson.filter(app => app.name === "email") - setEmailData(emailData) - const appDetails = filteredApps.map(app => ({ name: app.name, id: app.id })); //mapped apps with IDs as sometime Ids were not correct in security framework - // console.log("appDetails: ", appDetails) - // result = appDetails - - fetch(globalUrl + "/api/v1/apps/authentication", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - toast(`Failed getting auth for : `, response.reason); - console.log("Status not 200 for app auth :O!"); - return; - } - return response.json(); - }).then(async (responseJson) => { - if (!responseJson.success) { - console.log("Could not get app auth") - return; - } - // console.log("responseJson of auth: ", responseJson.data) - result = await mergeAuthData(appDetails, responseJson.data) - // console.log("merged auth data: ", result) - // console.log("result", result) - result.map(item => { - fetch(globalUrl + `/api/v1/apps/${item.id}/config`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }).then((response) => { - if (response.status !== 200) { - toast(`Failed getting config for ${item.id}: `, response.reason); - console.log("Status not 200 for app config :O!"); - return; - } - return response.json(); - }).then((responseJson) => { - if (!responseJson.success) { - console.log("Could not get app config") - return; - } - var decodedString = JSON.parse(atob(responseJson.app)); - // console.log("dcodedString: ",decodedString) - item.auth_config = decodedString.authentication - item.large_image = decodedString.large_image - setNotificationAppDetails(result) - console.log("notificationAppDetails: ", notificationAppDetails) - }).then(async()=>{ - await checkIfAlreadyGenerated(appList,workflows); - - }).catch((error) => { - console.log("Error getting app config: " + error); - toast("Error getting app config: " + error); - }) - }) - }) - } - }).catch((error) => { - console.log("Error getting app ids: " + error); - }) - } + - const executeTestWorkflow = async (workflowid) => { - const data = { "execution_argument": '{"title":"THIS IS TEST ALERT","description":"TEST ALERT FROM SHUFFLE","reference_url": "shuffler.io"}' } - fetch(globalUrl + `/api/v1/workflows/${workflowid}/execute`, { - 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; - } - toast("Notification workflow ran successfully"); - return response.json(); - }).catch((error) => { - console.log("Error getting workflows: " + 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` @@ -615,379 +468,7 @@ const generateNotificationWorkflow = async (appname,appImage,appAuthId,projectId }) } -const generateEmailNotificationWorkflow = async (appname,appImage,shuffleAPIKey,recepients) => { - //currently only supports 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() === "email"){ - console.log("updating workflow for email") - var workflowBody = { - "name": workflowName, - "Description": workflowDescription, - "id": workflow_id, - "actions": [ - { - "app_name": "email", - "name": "send_email_shuffle", - "large_image":appImage, - "isStartNode": true, - "label": "send_email_shuffle", - "app_version": "1.3.0", - "parameters": [ - { - "name": "apikey", - "value": shuffleAPIKey - }, - { - "name": "recipients", - "value": recepients - }, - { - "name": "subject", - "value": "$exec.title" - }, - { - "name":"body", - "value":"$exec.description" - } - ] - } - ] - } - } - fetch(globalUrl + `/api/v1/workflows/${workflow_id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(workflowBody), - 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) { - handleEditOrg( - orgName, - orgDescription, - selectedOrganization.id, - selectedOrganization.image, - { - app_download_repo: appDownloadUrl, - app_download_branch: appDownloadBranch, - workflow_download_repo: workflowDownloadUrl, - workflow_download_branch: workflowDownloadBranch, - notification_workflow: workflow_id, - documentation_reference: documentationReference, - }, - { - sso_entrypoint: ssoEntrypoint, - sso_certificate: ssoCertificate, - client_id: openidClientId, - client_secret: openidClientSecret, - openid_authorization: openidAuthorization, - openid_token: openidToken, - } - ) - console.log("Notification workflow updated successfully") - toast("Notification workflow updated successfully") - } - }) - } - }).catch((error) => { - console.log("Error setting workflows: " + error); - }) -} - - -const testWorkflowModal = notificationWorkflowTestModal ? - ( { - setNotificationWorkflowTestModal(false); - }} - > - - {/* -
    - Notification workflow -
    -
    */} - - We have updated the Notification workflow. Do you want to test it? - - - - - -
    -
    ) : null - -const modalView = notificationWorkflowModal ? ( - { - setNotificationWorkflowModal(false); - }} - > - - -
    - {`Configure ${selectedAppDetails.name} workflow`} -
    -
    - - - {console.log("len Selected app details: ", selectedAppDetails)} - {(selectedAppDetails.authentication_data || (selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false) || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? - <> - - {(selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? "No authentication required": - <> - - Pick an authentication method from the list - - - Available authentications - - } - - - - Provide additional required details: - - { - setTextFieldOneValue(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} /> - { - setTextFieldValue(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} /> - - : - <> - 0) ? false : true} - // // setAuthenticationModalOpen={false} - selectedApp={{...selectedAppDetails,authentication: selectedAppDetails.auth_config}} - // getAppAuthentication={selectedAppDetails.name} - /> - - } - - - - - -
    -
    -) : null - - - const checkIfAlreadyGenerated = async (appList, workflows) => { // fixxxxxxxxxxxxxxxxxxxxx - - var workflowName = workflows.find(workflow => workflow.id === notificationWorkflow) - if (workflowName) { - workflowName = workflowName.name - } - else { - console.log("no workflow set") - return - } - if (workflowName) { - const parts = workflowName.split(' '); - console.log("parts", parts) - if (parts[0].toString() === "[GENERATED]" && parts.length > 1) { - console.log("parts1", parts[1]) - if ((appList.includes(parts[1]))) { - console.log("workflow already generated") - setGeneatedWorkflow({"app_name": parts[1]}) - } - } - } - else { - return - } - } - -const renderChips = (apps) => { - return ( - - {apps.map((app) => ( - { - console.log(`Clicked ${app.name}`) - console.log("app: ",app) - setSelectedAppDetails(app) - if (app.authentication_data && app.authentication_data.length > 0){ //fixxxxxxxx - console.log("authdata: ",app.authentication_data[0]) - setSelectedAuth(app.authentication_data[app.authentication_data.length-1].id) - } - setNotificationWorkflowModal(true) - // getAppAuth(app.name) - console.log("selectedAppDEtails",selectedAppDetails) - }} - avatar={{app.name}} - - /> - ))} - - ); -}; + return (
    @@ -995,11 +476,18 @@ const renderChips = (apps) => { Notification Workflow - {modalView} - {/*{testWorkflowModal} */} -
    - {renderChips(notificationAppDetails.length > 0 ? notificationAppDetails : emailData)} -
    + + {/* + + */} + +
    {workflows !== undefined && workflows !== null && workflows.length > 0 ? { return "The app's Docker Image is not available in the environment yet. Re-run the app to force a re-download of the app. If the problem persists, contact support" } - if (result.status !== 200 && stringjson.includes("192.168") || stringjson.includes("172.16") || stringjson.includes("10.0")) { + if (result.status !== 200 && (result.url.includes("192.168") || result.url.includes("172.16") || result.url.includes("10.0"))) { return "Consider whether your Orborus environment can connect to a local IP or not." } diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 32fc5509..4f82f517 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1678,6 +1678,7 @@ const Workflows = (props) => { const [hover, setHover] = React.useState(false); const innerColor = "rgba(255,255,255,0.3)" + const setupPaperStyle = { minHeight: paperAppStyle.minHeight, maxWidth: "100%", @@ -1691,7 +1692,7 @@ const Workflows = (props) => { cursor: "pointer", backgroundColor: hover ? "rgba(39,41,45,0.5)" : "rgba(39,41,45,1)", borderRadius: paperAppStyle.borderRadius, - }; + } return ( @@ -1763,7 +1764,8 @@ const Workflows = (props) => { const menuClick = (event) => { setOpen(!open); setAnchorEl(event.currentTarget); - }; + } + var parsedName = data.name; if ( @@ -1776,8 +1778,9 @@ const Workflows = (props) => { const actions = data.actions !== null ? data.actions.length : 0; const appGroup = getWorkflowAppgroup(data) - const [triggers, subflows] = getWorkflowMeta(data); + const [triggers, subflows] = getWorkflowMeta(data) + const isDistributed = data.suborg_distribution !== undefined && data.suborg_distribution !== null && data.suborg_distribution.includes(userdata.active_org.id) const workflowMenuButtons = ( { } return ( -
    +
    - {selectedCategory !== "" ? - -
    { - addFilter(selectedCategory) - }} - /> - - : null} + {selectedCategory !== "" ? + +
    { + addFilter(selectedCategory) + }} + /> + + : null} Date: Tue, 14 May 2024 15:05:27 +0000 Subject: [PATCH 116/142] skips the code part of the markdown --- frontend/src/views/Docs.jsx | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 44785cd9..61b83fac 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -343,34 +343,33 @@ const Docs = (defaultprops) => { } } - - // extract toc from all headings and subheadings - const extractHeadings = (content) => { + const extractHeadings = content => { const headings = []; + const lines = content.split('\n'); + let inCodeBlock = false; - const headingRegex = /^(#{2,3})\s+(.+)$/gm; - let match; + for (const line of lines) { + if (line.startsWith('```')) { + inCodeBlock = !inCodeBlock; + } - while ((match = headingRegex.exec(content)) !== null) { - const level = match[1].length; - const title = match[2].trim(); + if (!inCodeBlock) { + const headingMatch = line.match(/^(#{2,3})\s+(.+)$/); + if (headingMatch) { + const level = headingMatch[1].length; + const title = headingMatch[2].trim(); + const id = title.toLowerCase().replace(/\s+/g, '-'); - if (level === 2) { - headings.push({ - id: title.toLowerCase().replace(/\s+/g, '-'), - title: title, - items: [] - }); - } else if (level === 3 && headings.length > 0) { - const lastHeading = headings[headings.length - 1]; - lastHeading.items.push({ - id: title.toLowerCase().replace(/\s+/g, '-'), - title: title - }); + if (level === 2) { + headings.push({ id, title, items: [] }); + } else if (level === 3 && headings.length) { + headings[headings.length - 1].items.push({ id, title }); + } + } } } - return headings - } + return headings; + }; // extract TOC from actual markdown @@ -587,7 +586,9 @@ const Docs = (defaultprops) => { alignSelf: "flex-start", position: "sticky", top: 80, - overflow: "auto", + overflowY: "auto", + minHeight: "93vh", + maxHeight: "93vh", marginTop: 70, } From 8ec6c0c0741c392cae55a58e5975da88b1e2939c Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Wed, 15 May 2024 06:15:06 +0000 Subject: [PATCH 117/142] fix for '_' in hash and with '?' --- frontend/src/views/Docs.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 61b83fac..39c40cd0 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -332,8 +332,12 @@ const Docs = (defaultprops) => { const scrollToHash = () => { - const hash = window.location.hash.replace("#", "") + var hash = window.location.hash.replace("#", "").replaceAll("_", "-"); + if (hash.includes('?')) { + hash = hash.split('?')[0] + } if (hash) { + console.log("HASH: ", hash) const element = document.getElementById(hash) if (element) { element.scrollIntoView({ From 5f8f5ab22407754134f14a8d0c5918bdb1a52738 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 17 May 2024 00:59:28 +0200 Subject: [PATCH 118/142] Added json changes and local storage --- backend/app_sdk/app_base.py | 2 +- frontend/src/components/NewHeader.jsx | 9 +- frontend/src/components/Oauth2Auth.jsx | 6 +- frontend/src/components/ParsedAction.jsx | 1 - frontend/src/theme.jsx | 11 +- frontend/src/views/Admin.jsx | 4 +- frontend/src/views/AngularWorkflow.jsx | 99 +++++++-- frontend/src/views/AppCreator.jsx | 19 +- frontend/src/views/Apps.jsx | 28 ++- frontend/src/views/Docs.jsx | 7 +- frontend/src/views/Usecases.jsx | 10 +- frontend/src/views/Workflows.jsx | 252 ++++++++++++++++------- 12 files changed, 336 insertions(+), 112 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 88b9a217..47d6e1ec 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -660,7 +660,7 @@ class AppBase: break else: # FIXME: Add a checker for 403, and Proxy logs failing - self.logger.info(f"[ERROR] Bad resp {ret.status_code} for url {url}") + self.logger.info(f"[ERROR] Bad resp ({ret.status_code}) in send_result for url '{url}'") time.sleep(sleeptime) diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index da13952d..f1ca7520 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -203,6 +203,11 @@ const Header = (props) => { window.location.pathname = "/"; localStorage.setItem("globalUrl", "") + + // Delete userinfo from localstorage + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") }) .catch((error) => { console.log(error); @@ -476,7 +481,9 @@ const Header = (props) => { 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(); diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index fe8562f5..c14e167d 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -70,8 +70,6 @@ const registeredApps = [ "microsoft_teams", "microsoft_teams_user_access", "todoist", - "microsoft_sentinel", - "microsoft_365_defender", "google_chat", "google_sheets", "google_drive", @@ -711,7 +709,7 @@ const AuthenticationOauth2 = (props) => { - Oauth2 requires a client ID and secret to authenticate, defined in the remote system. Your redirect URL is {window.location.origin}/set_authentication -  + Oauth2 requires a client ID and secret to authenticate, defined in the remote system. {authenticationType.type === "oauth2-app" ? null : Your redirect URL is {window.location.origin}/set_authentication - } { const fieldname = data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name return ( -
    +
    {fieldname} diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 1bbe88f5..5b8737cf 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -3552,7 +3552,6 @@ const ParsedAction = (props) => { fullWidth variant="contained" onClick={() => { - console.log(authenticationType); //if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { // return null //} diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index b183a1e4..5c240f4b 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -26,7 +26,16 @@ const theme = createTheme(adaptV4Theme({ green: "#5cc879", borderRadius: 10, defaultBorder: "1px solid rgba(255,255,255,0.3)", - jsonTheme: "brewer", + + //jsonTheme: "brewer", + //jsonTheme: "chalk", + //jsonTheme: "monokai", + //jsonTheme: "google", + //jsonTheme: "tomorrow", + jsonIconStyle: "round", + jsonTheme: "summerfruit", + jsonCollapseStringsAfterLength: 75, + reactJsonStyle: { padding: 5, width: "98%", diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index c24b6ca7..0d47a9de 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1609,7 +1609,9 @@ If you're interested, please let me know a time that works for you, or set up a 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(); diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e06bf9bc..a9c2f825 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -655,6 +655,48 @@ const AngularWorkflow = (defaultprops) => { // Handle the activation case, as they are NOT in the event management system yet if (selectedApp.actions === undefined || selectedApp.actions === null || selectedApp.actions.length > 1) { 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}`) + }) + + } } for (let appkey in apps) { @@ -2198,7 +2240,9 @@ const AngularWorkflow = (defaultprops) => { return } - return response.json(); + console.log("Apps loaded. JSON decoding next") + + return response.json() }) .then((responseJson) => { if (responseJson === null) { @@ -4178,14 +4222,18 @@ const AngularWorkflow = (defaultprops) => { // Check local storage has it const foundapps = localStorage.getItem("apps") if (foundapps !== null && foundapps !== undefined) { - const parsedapps = JSON.parse(foundapps) - if (parsedapps !== null && parsedapps !== undefined && parsedapps.length > 0) { - for (let appkey in parsedapps) { - if (parsedapps[appkey].name === curaction.app_name) { - curapp = parsedapps[appkey] - break + try { + const parsedapps = JSON.parse(foundapps) + if (parsedapps !== null && parsedapps !== undefined && parsedapps.length > 0) { + for (let appkey in parsedapps) { + if (parsedapps[appkey].name === curaction.app_name) { + curapp = parsedapps[appkey] + break + } } } + } catch (e) { + console.log("Problem with parsing apps from local storage", e) } } else { @@ -4193,6 +4241,12 @@ const AngularWorkflow = (defaultprops) => { } } + /* + if (curapp && curapp.app_id !== undefined && curapp.app_id !== null && curapp.app_id.length > 0 &&curapp.actions.length <= 1) { + toast(`Side-loading app ${curapp.name} to get actions.`) + } + */ + if (!curapp || curapp === undefined) { const tmpapp = { name: curaction.app_name, @@ -4205,6 +4259,7 @@ const AngularWorkflow = (defaultprops) => { setSelectedApp(tmpapp) setSelectedAction(curaction) } else { + curaction.app_id = curapp.id setAuthenticationType( @@ -6810,7 +6865,7 @@ const AngularWorkflow = (defaultprops) => { } console.log("Setupgraph done 2!") - }; + } const removeNode = (nodeId) => { const selectedNode = cy.getElementById(nodeId); @@ -8082,10 +8137,17 @@ const AngularWorkflow = (defaultprops) => { 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"; @@ -8610,8 +8672,6 @@ const AngularWorkflow = (defaultprops) => { : hits.map((hit, index) => { - console.log("HIT: ", hit) - const innerlistitemStyle = { width: positionInfo.width + 35, overflowX: "hidden", @@ -15728,6 +15788,9 @@ const AngularWorkflow = (defaultprops) => { theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={true} + iconStyle={theme.palette.jsonIconStyle} + collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} + displayArrayKey={false} enableClipboard={(copy) => { handleReactJsonClipboard(copy); }} @@ -15735,7 +15798,7 @@ const AngularWorkflow = (defaultprops) => { onSelect={(select) => { HandleJsonCopy(validate.result, select, "exec"); }} - name={"Execution Argument"} + name={false} />
    ) @@ -16034,6 +16097,9 @@ const AngularWorkflow = (defaultprops) => { theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={parsedCollapse} + iconStyle={theme.palette.jsonIconStyle} + collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} + displayArrayKey={false} shouldCollapse={(field) => { console.log("FIELD: ", field) }} @@ -17134,6 +17200,9 @@ const AngularWorkflow = (defaultprops) => { theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={true} + iconStyle={theme.palette.jsonIconStyle} + collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} + displayArrayKey={false} enableClipboard={(copy) => { handleReactJsonClipboard(copy); }} @@ -17263,6 +17332,9 @@ const AngularWorkflow = (defaultprops) => { theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={data.value.length < 10000 ? false : true} + iconStyle={theme.palette.jsonIconStyle} + collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} + displayArrayKey={false} displayDataTypes={false} name={"Parsed data for variable " + data.name} /> @@ -17371,7 +17443,7 @@ const AngularWorkflow = (defaultprops) => { return "The app's Docker Image is not available in the environment yet. Re-run the app to force a re-download of the app. If the problem persists, contact support" } - if (result.status !== 200 && (result.url.includes("192.168") || result.url.includes("172.16") || result.url.includes("10.0"))) { + if (result.status !== 200 && result.url !== undefined && result.url !== null && (result.url.includes("192.168") || result.url.includes("172.16") || result.url.includes("10.0"))) { return "Consider whether your Orborus environment can connect to a local IP or not." } @@ -17648,6 +17720,9 @@ const AngularWorkflow = (defaultprops) => { theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={selectedResult.result.length < 10000 ? false : true} + iconStyle={theme.palette.jsonIconStyle} + collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} + displayArrayKey={false} enableClipboard={(copy) => { handleReactJsonClipboard(copy); }} diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 04e0d6be..e9db7736 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -2994,15 +2994,15 @@ const AppCreator = (defaultprops) => { toast("Auth URL must start with http(s)://"); } - if (tmpstring.includes("?")) { - var newtmp = tmpstring.split("?") - if (tmpstring.length > 1) { - tmpstring = newtmp[0] - } - } + if (tmpstring.includes("?")) { + var newtmp = tmpstring.split("?") + if (tmpstring.length > 1) { + tmpstring = newtmp[0] + } + } - setParameterName(tmpstring) - }} + setParameterName(tmpstring) + }} InputProps={{ classes: { notchedOutline: classes.notchedOutline, @@ -6054,10 +6054,11 @@ const AppCreator = (defaultprops) => { { - selectedTrigger.environment = e.target.value - setSelectedTrigger(selectedTrigger) - - setWorkflow(workflow) - setUpdate(Math.random()) + const PipelineSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined && selectedTrigger.trigger_type !== "SCHEDULE" ? null : +
    +

    + {selectedTrigger.app_name}: {selectedTrigger.status} +

    +
    + What are pipelines? + + +
    Name
    + - {environments.map((data) => { - if (data.archived) { - return null - } + InputProps={{ + style: {}, + }} + fullWidth + color="primary" + placeholder={selectedTrigger.label} + onChange={selectedTriggerChange} + /> - if (data.Name.toLowerCase() === "cloud") { - return null - } +
    + Environment + -
    - -
    -
    - Parameters - - {/* -
    { - const pipelineConfig = { - "name": "HTTP Testing", - "type": "create", - "command": "from http://192.168.86.44:5002/api/v1/orgs/7e9b9007-5df2-4b47-bca5-c4d267ef2943/cache/CIDR%20ranges?type=text&authorization=cec9d01f-09b2-4419-8a0a-76c6046e3fef read lines | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines", - "environment": selectedTrigger.environment, - } - - submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) - }} - > - Run HTTP Request -
    - */} - -
    { - const pipelineConfig = { - "name": selectedTrigger.label, - "type": "create", - "command": "load tcp://0.0.0.0:514 | read syslog | export", - "environment": selectedTrigger.environment, - } - - submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) - }} - > - Start Syslog listener -
    - -
    { - const pipelineConfig = { - "name": selectedTrigger.label, - "type": "create", - "command": "export --live | sigma /path/to/rules | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines", - "environment": selectedTrigger.environment, - } - - submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) - }} - > - Run Sigma Rulesearch -
    - -
    { - const pipelineConfig = { - "name": selectedTrigger.label, - "type": "create", - "command": "from kafka://1.2.3.4 --topic foo | to http://api.com X-Token:Secret", - "environment": selectedTrigger.environment, - } - - submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig) - }} - > - Follow Kafka Queue -
    - -
    -
    + +
    +
    + Parameters +
    { - toast("Should start. But it doesn't") + // setSelectedOption("Syslog listener") + // setTenzirConfigModalOpen(true); + }} + style={{ + border: "1px solid rgba(255,255,255,0.3)", + borderRadius: theme.palette.borderRadius, + padding: 10, + cursor: "not-allowed", + marginTop: 5, + display: "flex", + alignItems: "center", }} - color="primary" > - Start - -
    + +
    { - toast("Should stop triggert") + // setSelectedOption("Sigma Rulesearch") + // setTenzirConfigModalOpen(true); + }} + style={{ + border: "1px solid rgba(255,255,255,0.3)", + borderRadius: theme.palette.borderRadius, + padding: 10, + cursor: "not-allowed", + marginTop: 5, + display: "flex", + alignItems: "center", }} - color="primary" > - Stop - + setSelectedOption("Sigma Rulesearch")} + value={"Sigma Rulesearch"} + name="option" + disabled={true} + /> + } + label="Run Sigma Rulesearch" + /> +
    + +
    { + if(selectedTrigger.status === "running"){ + toast("please stop the trigger to edit the configuration"); + return; + } else { + setSelectedOption("Kafka Queue"); + setTenzirConfigModalOpen(true); + }}} + style={{ + border: "1px solid rgba(255,255,255,0.3)", + borderRadius: theme.palette.borderRadius, + padding: 10, + cursor: "pointer", + marginTop: 5, + display: "flex", + alignItems: "center", + }} + > + setSelectedOption("Kafka Queue")} + value={"Kafka Queue"} + name="option" + /> + } + label="Follow Kafka Queue" + /> +
    + +
    + + +
    -
    const ScheduleSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined && selectedTrigger.trigger_type !== "SCHEDULE" ? null :
    @@ -14048,7 +14123,7 @@ const AngularWorkflow = (defaultprops) => { selectedTrigger.status === "running" } defaultValue={ - selectedTrigger.parameters === undefined ? "" : selectedTrigger.parameters[0].value + selectedTrigger.parameters === undefined ? "" : selectedTrigger.parameters[0]?.value } color="primary" placeholder="" @@ -18749,6 +18824,146 @@ const AngularWorkflow = (defaultprops) => { ) : null; + const tenzirConfigModal = tenzirConfigModalOpen ? ( + +
    + +
    Configuration options for {selectedOption}
    +
    + + {selectedOption === "Kafka Queue" && ( + <> + Topic + param.name === "topic")?.value) || ''} + /> + bootstrap.servers + param.name === "bootstrap_servers")?.value) || ''} + /> + group.id + param.name === "group_id")?.value) || ''} + /> + auto.offest.reset + param.name === "auto_offset_reset")?.value) || ''} + /> + + )} + + + + + +
    + + { + setTenzirConfigModalOpen(false); + }} + > + + +
    + ) : null; + // Should get AI autocompletes const aiSubmit = (value, setResponseMsg, setSuggestionLoading, inputAction) => { if (setResponseMsg !== undefined) { @@ -19565,6 +19780,7 @@ const AngularWorkflow = (defaultprops) => { {codePopoutModal} {workflowRevisions} {authenticationModal} + {tenzirConfigModal} {/*editWorkflowModal*/} {executionArgumentModal} {configureWorkflowModal} From 897b19f961c41d634d5a81e11314eb8fe99b20be Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 20 May 2024 13:45:08 +0200 Subject: [PATCH 123/142] Registration fix --- backend/go-app/go.mod | 8 +++++--- backend/go-app/go.sum | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 1a886389..a9555704 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,8 @@ module shuffle -go 1.22 +go 1.22.0 + +toolchain go1.22.2 require ( cloud.google.com/go/datastore v1.15.0 @@ -16,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.18 + github.com/shuffle/shuffle-shared v0.6.24 golang.org/x/crypto v0.22.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.63.2 @@ -54,7 +56,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/frikky/schemaless v0.0.9 // indirect + github.com/frikky/schemaless v0.0.11 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 6d9cd464..f8a126bd 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -158,6 +158,8 @@ github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2+8= github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.11 h1:c4r6CJX30XI+SoJdT9RlUd9qYSQlx6hvwGRtsypu+uM= +github.com/frikky/schemaless v0.0.11/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.11.0 h1:4ZAk6W7rPAtPXm7198EFqA5S68rwnNQORxlOA5OurCA= @@ -433,6 +435,8 @@ github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGS github.com/shuffle/shuffle-shared v0.6.16/go.mod h1:HhQTn7xZZ69ZTc4EptO9OeNmgbKDyGlWAhFkUFUAHSA= github.com/shuffle/shuffle-shared v0.6.18 h1:mKc3vGuCz9ubdqMwaLocSbEUZyso620CY73RBqcF6LI= github.com/shuffle/shuffle-shared v0.6.18/go.mod h1:00QOcSPlUWMXzJj1D7pjcV9h6nVRWfSWqTM10+fhTd0= +github.com/shuffle/shuffle-shared v0.6.24 h1:gSUsI7o7DG0Z/AYtDQuSvhivWVV4MQPa5JU08YKAy2U= +github.com/shuffle/shuffle-shared v0.6.24/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= From b10831970099af7464c38e67f6127f6584d2babe Mon Sep 17 00:00:00 2001 From: satti-hari-krishna-reddy Date: Tue, 21 May 2024 12:53:36 +0530 Subject: [PATCH 124/142] making start and stop buttons to work in PipelineSidebar --- frontend/src/views/AngularWorkflow.jsx | 103 +++++++++++++++++++++---- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index f825f18f..0aa2f38d 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1298,6 +1298,9 @@ const AngularWorkflow = (defaultprops) => { name: "topic", value: topic }); + } else { + toast("please enter the topic name"); + return; } if (bootstrapServers) { @@ -1305,6 +1308,9 @@ const AngularWorkflow = (defaultprops) => { name: "bootstrap_servers", value: bootstrapServers }); + } else { + toast("please enter bootstrap server details"); + return; } if (groupId) { @@ -6895,6 +6901,20 @@ const AngularWorkflow = (defaultprops) => { } else if (selectedNode.data().trigger_type === "EMAIL") { setSelectedTrigger(selectedNode.data()); stopMailSub(selectedTrigger, triggerindex); + } else if (selectedNode.data().trigger_type === "PIPELINE") { + setSelectedTrigger(selectedNode.data()); + + const pipelineConfig = { + command: "", + name: selectedNode.data().label, + type: "delete", + environment: selectedNode.data().environment, + workflow_id: workflow.id, + trigger_id: selectedNode.data().id, + start_node: "", + }; + + submitPipeline(selectedNode.data(), triggerindex, pipelineConfig); } } @@ -7293,8 +7313,14 @@ const AngularWorkflow = (defaultprops) => { } } const data = usecase; - if (data.type === "create") toast("Creating pipeline"); - else toast("stopping pipeline"); + data.start_node = mappedStartnode + + if (data.type === "create") { + toast("Creating pipeline"); + } else if (data.type === "stop") { + toast("stopping pipeline"); + } + const url = `${globalUrl}/api/v1/triggers/pipeline`; fetch(url, { method: "POST", @@ -7316,8 +7342,14 @@ const AngularWorkflow = (defaultprops) => { if (!responseJson.success) { toast("Failed to set pipeline: " + responseJson.reason); } else { - if (data.type === "create") toast("Pipeline will be created!"); - else toast("Pipeline will be stopped!"); + if (data.type === "create") { + toast("Pipeline will be created!"); + } else if (data.type === "stop") { + toast("Pipeline will be stopped!"); + } else { + toast("Pipeline deleted!") + return + } trigger.parameters.push({ name: data.name, @@ -13947,24 +13979,46 @@ const AngularWorkflow = (defaultprops) => { disabled={selectedTrigger.status === "running"} onClick={() => { - const topic = document.getElementById('topic')?.value; - const bootstrapServers = document.getElementById('bootstrap_servers')?.value; - const groupId = document.getElementById('group_id')?.value; - const autoOffsetReset = document.getElementById('auto_offset_reset')?.value; + const topic = (selectedTrigger?.parameters?.find(param => param.name === "topic")?.value) || '' + const bootstrapServers = (selectedTrigger?.parameters?.find(param => param.name === "bootstrap_servers")?.value) || '' + const groupId = (selectedTrigger?.parameters?.find(param => param.name === "group_id")?.value) || '' + const autoOffsetReset = (selectedTrigger?.parameters?.find(param => param.name === "auto_offset_reset")?.value) || '' let command = "from kafka" - if(topic) command = `${command} --topic ${topic}` - if(bootstrapServers) command = `${command} --set bootstrap.servers=${bootstrapServers}` - if(groupId) command = `${command},group.id=${groupId}` - if(autoOffsetReset) command = `${command},auto.offset.reset=${autoOffsetReset}` + if(topic) { + command = `${command} -t ${topic}` + } else { + toast("please enter the topic name") + return; + } + if(bootstrapServers) { + command = `${command} -e -o stored -X bootstrap.servers=${bootstrapServers}` + } else { + toast("please enter the bootstrap servers details") + return; + } + + if(groupId) { + command = `${command},group.id=${groupId}` + } else { + command = `${command},group.id=${selectedTrigger.id}` + } + if(autoOffsetReset) { + command = `${command},auto.offset.reset=${autoOffsetReset}` + } else { + command = `${command},auto.offset.reset=earliest` + } + command = `${command},client.id=${selectedTrigger.id},enable.auto.commit=true,auto.commit.interval.ms=1` + command = `${command} read json | to ${globalUrl}/api/v1/pipelines/pipeline_${selectedTrigger.id}` const pipelineConfig = { command: command, name: selectedTrigger.label, - type: "start", + type: "create", environment: selectedTrigger.environment, workflow_id: workflow.id, trigger_id: selectedTrigger.id, + start_node: "", }; submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig); }} @@ -13977,7 +14031,15 @@ const AngularWorkflow = (defaultprops) => { variant="contained" disabled={selectedTrigger.status !== "running"} onClick={() => { - toast("Should stop trigger"); + const pipelineConfig = { + name: selectedTrigger.label, + type: "stop", + environment: selectedTrigger.environment, + workflow_id: workflow.id, + trigger_id: selectedTrigger.id, + start_node: "", + }; + submitPipeline(selectedTrigger, selectedTriggerIndex, pipelineConfig); }} color="primary" > @@ -15902,7 +15964,18 @@ const AngularWorkflow = (defaultprops) => { style={{paddingTop: 8, paddingLeft: 4, height: 25, width: 25, }} /> ); - } + } else if (execution.execution_source === "pipeline") { + return ( + {"pipeline"} trigger.trigger_type === "PIPELINE") + .large_image + } + style={{ width: size, height: size }} + /> + ); + } if ( execution.execution_parent !== null && From 646d69fcdcb6c2187f279a7192194d8b89c55894 Mon Sep 17 00:00:00 2001 From: satti-hari-krishna-reddy Date: Tue, 21 May 2024 12:59:12 +0530 Subject: [PATCH 125/142] made pipeline an actual trigger --- backend/go-app/main.go | 141 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 9db78ed0..a08a27e1 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1973,6 +1973,144 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { } +func handlePipelineCallback(resp http.ResponseWriter, request *http.Request) { + + if request.Method != "POST" { + request.Method = "POST" + } + + if request.Body == nil { + stringReader := strings.NewReader("") + request.Body = ioutil.NopCloser(stringReader) + } + + path := strings.Split(request.URL.String(), "/") + if len(path) < 4 { + resp.WriteHeader(403) + resp.Write([]byte(`{"success": false}`)) + return + } + + ctx := context.Background() + location := strings.Split(request.URL.String(), "/") + + var pipelineId string + + if location[1] == "api" { + if len(location) <= 4 { + log.Printf("[INFO] Couldn't handle location. Too short in pipeline: %d", len(location)) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + pipelineId = location[4] + } + + userAgent := request.Header.Get("User-Agent") + if strings.Contains(strings.ToLower(userAgent), "microsoftpreview") || strings.Contains(strings.ToLower(userAgent), "googlebot") { + log.Printf("[AUDIT] Blocking googlebot and microsoftbot for pielines. UA: '%s'", userAgent) + resp.WriteHeader(400) + resp.Write([]byte(`{"success": false, "reason": "Google/Microsoft preview bots not allowed. Please change the useragent."}`)) + return + } + + if len(pipelineId) != 45 { + log.Printf("[INFO] Couldn't handle pipeline. Too short in pipeline: %d", len(pipelineId)) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "pipeline ID not valid"}`)) + return + } + + pipelineId = pipelineId[9:] + + pipeline, err := shuffle.GetPipeline(ctx, pipelineId) + if err != nil { + log.Printf("[WARNING] Failed getting pipeline %s (callback): %s", pipelineId, err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if pipeline.Status != "running" { + log.Printf("[WARNING] Not running %s because pipeline status is %s", pipeline.TriggerId, pipeline.Status) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The pipeline isn't running"}`))) + return + } + + if pipeline.WorkflowId == "" { + log.Printf("[DEBUG] Not running because pipeline isn't connected to any workflows") + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflows are defined"}`))) + return + } + + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("[DEBUG] Body data error: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + parsedBody := shuffle.GetExecutionbody(body) + newBody := shuffle.ExecutionStruct{ + Start: pipeline.StartNode, + ExecutionSource: "pipeline", + ExecutionArgument: parsedBody, + } + + workflow, err := shuffle.GetWorkflow(ctx, pipeline.WorkflowId) + if err == nil { + for _, branch := range workflow.Branches { + if branch.SourceID == pipeline.TriggerId { + log.Printf("[DEBUG] Found ID %s for pipeline", pipeline.TriggerId) + if branch.DestinationID != pipeline.StartNode { + newBody.Start = branch.DestinationID + break + } + } + } + } + + b, err := json.Marshal(newBody) + if err != nil { + log.Printf("[ERROR] Failed newBody marshaling for pipeline: %s", err) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false}`)) + return + } + + log.Printf("[INFO] Running pipeline for workflow %s with startnode %s", pipeline.WorkflowId, pipeline.StartNode) + + newWorkflow := shuffle.Workflow{ + ID: "", + } + + if len(pipeline.StartNode) == 0 { + log.Printf("[WARNING] No start node for pipeline %s - running with workflow default.", pipeline.TriggerId) + + } + + newRequest := &http.Request{ + URL: &url.URL{}, + Method: "POST", + Body: ioutil.NopCloser(bytes.NewReader(b)), + } + + workflowExecution, executionResp, err := handleExecution(pipeline.WorkflowId, newWorkflow, newRequest, pipeline.OrgId) + + if err == nil { + resp.WriteHeader(200) + resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s"}`, workflowExecution.ExecutionId))) + return + } + + resp.WriteHeader(500) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp))) +} + func executeCloudAction(action shuffle.CloudSyncJob, apikey string) error { data, err := json.Marshal(action) if err != nil { @@ -4909,7 +5047,8 @@ func initHandlers() { r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/pipeline", shuffle.HandleNewPipelineRegister).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/triggers/pipeline/save", shuffle.HandleSavePipelineInfo).Methods("PUT", "OPTIONS") + //r.HandleFunc("/api/v1/triggers/pipeline/save", shuffle.HandleSavePipelineInfo).Methods("PUT", "OPTIONS") + r.HandleFunc("/api/v1/pipelines/{key}", handlePipelineCallback).Methods("POST", "GET", "PATCH", "PUT", "DELETE", "OPTIONS") r.HandleFunc("/api/v1/triggers", shuffle.HandleGetTriggers).Methods("GET", "OPTIONS") //r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") From ac66b9b623a6ee396254e6dbce355c2f9f77d7ad Mon Sep 17 00:00:00 2001 From: satti-hari-krishna-reddy Date: Tue, 21 May 2024 12:59:55 +0530 Subject: [PATCH 126/142] removed localhost from the url --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 61885b3b..64e325bc 100755 --- a/.env +++ b/.env @@ -101,7 +101,7 @@ SHUFFLE_OPENSEARCH_INDEX_PREFIX= SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true #Tenzir related -SHUFFLE_TENZIR_URL=http://localhost:5160 +SHUFFLE_TENZIR_URL= DEBUG_MODE=false From 1f09cce1ddfd2b8be3486bee8133e92d8c79671e Mon Sep 17 00:00:00 2001 From: satti-hari-krishna-reddy Date: Tue, 21 May 2024 14:06:32 +0530 Subject: [PATCH 127/142] reverting styling changes made to TriggersView --- frontend/src/views/AngularWorkflow.jsx | 35 +++++++++++--------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 0aa2f38d..83970ed6 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -7848,7 +7848,7 @@ const AngularWorkflow = (defaultprops) => { if (trigger.trigger_type === "PIPELINE") { if (userdata.support !== true) { - // return null + return null } } @@ -7913,31 +7913,26 @@ const AngularWorkflow = (defaultprops) => { >
    {imageline} {isMobile ? null : - - + -

    +

    {trigger.name}

    - + {trigger.description} @@ -8876,11 +8871,11 @@ const AngularWorkflow = (defaultprops) => { } if (app.trigger_type === "PIPELINE" && userdata.support !== true) { - //return null + return null } if (app.id === "integration" && userdata.support !== true) { - //return null + return null } var extraMessage = "" @@ -13784,7 +13779,7 @@ const AngularWorkflow = (defaultprops) => { return null } - const PipelineSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined && selectedTrigger.trigger_type !== "SCHEDULE" ? null : + const PipelineSidebar = Object.getOwnPropertyNames(selectedTrigger).length === 0 || workflow.triggers[selectedTriggerIndex] === undefined && selectedTrigger.trigger_type !== "SCHEDULE" ? null : !userdata.support === true ? null :

    {selectedTrigger.app_name}: {selectedTrigger.status} From abef955448609aa7b5944095fc56d1b8b8c46eb0 Mon Sep 17 00:00:00 2001 From: satti-hari-krishna-reddy Date: Tue, 21 May 2024 14:09:02 +0530 Subject: [PATCH 128/142] added image pull checking and removed few log messages --- functions/onprem/orborus/orborus.go | 211 ++++++++++++++++------------ 1 file changed, 125 insertions(+), 86 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 6a8e260e..dc9e554c 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1667,7 +1667,7 @@ func main() { newrequests := []shuffle.ExecutionRequest{} for _, incRequest := range executionRequests.Data { // Looking for specific jobs - if incRequest.Type == "PIPELINE_CREATE" || incRequest.Type == "PIPELINE_STOP" || incRequest.Type == "PIPELINE_DELETE" { + if incRequest.Type == "PIPELINE_CREATE" || incRequest.Type == "PIPELINE_START" || incRequest.Type == "PIPELINE_STOP" || incRequest.Type == "PIPELINE_DELETE" { err := handlePipeline(incRequest) if err != nil { @@ -2027,40 +2027,38 @@ func main() { // Read from Cache and send it to a webhook // docker run tenzir/tenzir:latest 'from http://192.168.86.44:5002/api/v1/orgs/7e9b9007-5df2-4b47-bca5-c4d267ef2943/cache/CIDR%20ranges?type=text&authorization=cec9d01f-09b2-4419-8a0a-76c6046e3fef read lines | to http://192.168.86.44:5002/api/v1/hooks/webhook_665ace5f-f27b-496a-a365-6e07eb61078c write lines' func handlePipeline(incRequest shuffle.ExecutionRequest) error { + + if tenzirUrl == "" { + tenzirUrl = "http://localhost:5160" + log.Printf("[WARNING] SHUFFLE_TENZIR_URL not set, falling back to default URL: %s",tenzirUrl) + } + err := deployTenzirNode() if err != nil{ log.Printf("[ERROR] failed to deploy the pipeline, reason: %s", err) + return err } - // no need of execution arguments for state updates - if incRequest.Type != "PIPELINE_STOP" && len(incRequest.ExecutionArgument) == 0 { + // no need of execution arguments for STOP and DELETE + if (incRequest.Type != "PIPELINE_STOP" && incRequest.Type != "PIPELINE_DELETE") && len(incRequest.ExecutionArgument) == 0 { log.Printf("[ERROR] No execution argument found for pipeline create. Skipping") return errors.New("no execution argument found for pipeline create. Skipping") } - //image := "tenzir/tenzir:latest" identifier := fmt.Sprintf("shuffle-%s", strings.ToLower(strings.ReplaceAll(incRequest.ExecutionSource, " ", "-"))) command := incRequest.ExecutionArgument if incRequest.Type == "PIPELINE_CREATE" { - log.Printf("[INFO] Should delete -> recreate new pipeline %#v. Name: %#v", incRequest.ExecutionArgument, identifier) + log.Printf("[INFO] Should delete -> recreate new pipeline with id %#v", identifier) //err := deployPipeline(image, identifier, command) - pipelineId, err := createPipeline(command, identifier) + _, err := createPipeline(command, identifier) if err != nil { log.Printf("[ERROR] Failed to create pipeline: %s", err) return err - } else { - log.Printf("[INFO] Pipeline created successfully with Id: %s", pipelineId) - newErr := savePipelineData(pipelineId, identifier, "running") - if newErr != nil { - log.Printf("[DEBUG] failed to save the pipeline data: %s", newErr) - } else { - log.Printf("[INFO] succesfully saved the pipeline info ") - } } } else if incRequest.Type == "PIPELINE_DELETE" { - log.Printf("[INFO] Should delete pipeline %#v", incRequest.ExecutionArgument) + log.Printf("[INFO] Should delete pipeline %#v", identifier) pipelineId, err := searchPipeline(identifier) if err != nil { log.Printf("[ERROR] Failed searching for Pipeline with name %s reason:%s ", identifier, err) @@ -2070,6 +2068,8 @@ func handlePipeline(incRequest shuffle.ExecutionRequest) error { if err != nil { log.Printf("[ERROR] Failed Deleting Pipeline %s", err) return err + } else { + log.Printf("[INFO] successfully deleted the Pipeline: %s", pipelineId) } } else if incRequest.Type == "PIPELINE_STOP" { log.Printf("[INFO] Should stop the pipeline %#v", identifier) @@ -2078,18 +2078,32 @@ func handlePipeline(incRequest shuffle.ExecutionRequest) error { log.Printf("[ERROR] Failed searching for Pipeline with name %s reason:%s ", identifier, err) return err } - state, err := updatePipelineState(pipelineId, "stop") + _, err = updatePipelineState(pipelineId, "stop") if err != nil { log.Printf("[ERROR] Failed to stop Pipeline: %s reason:%s ", pipelineId, err) return err } else { log.Printf("[INFO] successfully stopped the Pipeline: %s", pipelineId) } - err = savePipelineData(pipelineId, identifier, state) + + } else if incRequest.Type == "PIPELINE_START" { + log.Printf("[INFO] Should start the pipeline %#v", identifier) + pipelineId, err := searchPipeline(identifier) + if err != nil { + if err.Error() == "no existing pipeline found with name" { + log.Printf("[WARNING] no pipeline found for %s, creating a new one", identifier) + _, CreateErr := createPipeline(command, identifier) + return CreateErr + } + log.Printf("[ERROR] Failed searching for Pipeline with name %s reason:%s ", identifier, err) + return err + } + _, err = updatePipelineState(pipelineId, "start") if err != nil { - log.Printf("[DEBUG] failed to save the pipeline data: %s", err) + log.Printf("[ERROR] Failed to start Pipeline: %s reason:%s ", pipelineId, err) + return err } else { - log.Printf("[INFO] succesfully saved the pipeline info ") + log.Printf("[INFO] successfully started the Pipeline: %s", pipelineId) } } else { @@ -2106,7 +2120,7 @@ func deployTenzirNode() error { } ctx := context.Background() - cacheKey := "tenzir-key" + cacheKey := "tenzir-key" imageName := "tenzir/tenzir:latest" containerName := "tenzir-node" @@ -2114,19 +2128,29 @@ func deployTenzirNode() error { _, err := shuffle.GetCache(ctx, cacheKey) if err == nil { - return nil - } + return nil + } containerInfo, err := dockercli.ContainerInspect(ctx, containerName) if err != nil { if dockerclient.IsErrNotFound(err) { - pullOptions := types.ImagePullOptions{} - out, err := dockercli.ImagePull(ctx, imageName, pullOptions) - if err != nil { - log.Printf("[ERROR] Failed to pull the Tenzir image: %s", err) + + // Check if image exists + _, _, err := dockercli.ImageInspectWithRaw(ctx, imageName) + if dockerclient.IsErrNotFound(err) { + log.Printf("[DEBUG] pulling image %s", imageName) + pullOptions := types.ImagePullOptions{} + out, err := dockercli.ImagePull(ctx, imageName, pullOptions) + if err != nil { + log.Printf("[ERROR] Failed to pull the Tenzir image: %s", err) + return err + } + defer out.Close() + + io.Copy(io.Discard, out) + } else if err != nil { return err } - defer out.Close() err = createAndStartTenzirNode(ctx, containerName, imageName, containerStartOptions) if err != nil { @@ -2137,37 +2161,35 @@ func deployTenzirNode() error { } } else { if !containerInfo.State.Running { - log.Printf("[DEBUG] Tenzir Node exists but is not running, starting it") + log.Printf("[DEBUG] Tenzir Node exists but is not running") err := dockercli.ContainerStart(ctx, containerName, containerStartOptions) if err != nil { log.Printf("[ERROR] Failed to start Tenzir Node container: %v", err) return err } - log.Printf("[INFO] Tenzir Node container started successfully") log.Printf("[INFO] Waiting for Tenzir to become available ...") err = checkTenzirNode() if err != nil { return err } - log.Printf("[INFO] Successfully deployed Tenzir Node!") } } - tenzirStatus := struct { - ContainerStatus string `json:"container_status"` - }{ - ContainerStatus: "running", - } + tenzirStatus := struct { + ContainerStatus string `json:"container_status"` + }{ + ContainerStatus: "running", + } - cacheData, err := json.Marshal(tenzirStatus) - if err != nil { - log.Printf("[WARNING] Failed marshalling execution: %s", err) - } - err = shuffle.SetCache(ctx, cacheKey, cacheData, 1) - if err != nil { + cacheData, err := json.Marshal(tenzirStatus) + if err != nil { + log.Printf("[WARNING] Failed marshalling execution: %s", err) + } + err = shuffle.SetCache(ctx, cacheKey, cacheData, 1) + if err != nil { log.Printf("[WARNING] Failed updating cache for tenzir: %s", err) - } + } return nil } @@ -2265,18 +2287,35 @@ func createPipeline(command, identifier string) (string, error) { toBeDeleted = true } + if strings.Contains(command, "kafka") { + var scheme string + if strings.Contains(command, "http://") { + scheme = "http://" + } else if strings.Contains(command, "https://") { + scheme = "https://" + } + + startIndex := strings.Index(command, scheme) + if startIndex != -1 { + endIndex := startIndex + len(scheme) + endIndex += strings.Index(command[endIndex:], "/") + + command = command[:startIndex] + baseUrl + command[endIndex:] + } + } + requestBody := map[string]interface{}{ "definition": command, "name": identifier, "hidden": false, "autostart": map[string]bool{ "created": true, - "completed": false, - "failed": false, + "completed": true, + "failed": true, }, "autodelete": map[string]bool{ "completed": false, - "failed": true, + "failed": false, "stopped": false, }, "retry_delay": "500.0ms", @@ -2348,12 +2387,12 @@ func updatePipelineState(pipelineId, action string) (string, error) { "action": action, "autostart": map[string]bool{ "created": true, - "completed": false, - "failed": false, + "completed": true, + "failed": true, }, "autodelete": map[string]bool{ "completed": false, - "failed": true, + "failed": false, "stopped": false, }, } @@ -2490,53 +2529,53 @@ func searchPipeline(identifier string) (string, error) { return "", errors.New("no existing pipeline found with name") } -func savePipelineData(pipelineId, identifier, status string) error { +// func savePipelineData(pipelineId, identifier, status string) error { - url := fmt.Sprintf("%s/api/v1/triggers/pipeline/save", baseUrl) - identifierWithoutPrefix := strings.TrimPrefix(identifier, "shuffle-") +// url := fmt.Sprintf("%s/api/v1/triggers/pipeline/save", baseUrl) +// identifierWithoutPrefix := strings.TrimPrefix(identifier, "shuffle-") - forwardMethod := "PUT" +// forwardMethod := "PUT" - payload := map[string]interface{}{ - "pipeline_id": pipelineId, - "trigger_id": identifierWithoutPrefix, - "status": status, - } +// payload := map[string]interface{}{ +// "pipeline_id": pipelineId, +// "trigger_id": identifierWithoutPrefix, +// "status": status, +// } - payloadBytes, err := json.Marshal(payload) - if err != nil { - log.Printf("[ERROR] Failed to marshal payload: %s", err) - return err - } +// payloadBytes, err := json.Marshal(payload) +// if err != nil { +// log.Printf("[ERROR] Failed to marshal payload: %s", err) +// return err +// } - forwardData := bytes.NewBuffer(payloadBytes) +// forwardData := bytes.NewBuffer(payloadBytes) - req, err := http.NewRequest( - forwardMethod, - url, - forwardData, - ) - if err != nil { - log.Printf("[ERROR] Failed to create HTTP request: %s", err) - return err - } - req.Header.Set("Content-Type", "application/json") +// req, err := http.NewRequest( +// forwardMethod, +// url, +// forwardData, +// ) +// if err != nil { +// log.Printf("[ERROR] Failed to create HTTP request: %s", err) +// return err +// } +// req.Header.Set("Content-Type", "application/json") - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) - if err != nil { - log.Printf("[ERROR] Failed to send HTTP request: %s", err) - return err - } - defer resp.Body.Close() +// client := &http.Client{Timeout: 10 * time.Second} +// resp, err := client.Do(req) +// if err != nil { +// log.Printf("[ERROR] Failed to send HTTP request: %s", err) +// return err +// } +// defer resp.Body.Close() - if resp.StatusCode != 200 { - log.Printf("[ERROR] Received non-successful HTTP status code: %d", resp.StatusCode) - return fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode) - } +// if resp.StatusCode != 200 { +// log.Printf("[ERROR] Received non-successful HTTP status code: %d", resp.StatusCode) +// return fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode) +// } - return nil -} +// return nil +// } // Is this ok to do with Docker? idk :) func getRunningWorkers(ctx context.Context, workerTimeout int) int { From 22e6db08c5dc6da6946082326ee07a77eb2139f7 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 11:53:47 +0200 Subject: [PATCH 129/142] SDK build --- backend/app_sdk/app_base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index d8482fed..995ac7b6 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -3687,6 +3687,7 @@ class AppBase: self.logger.info(f"[ERROR] Failed parsing timeout to int: {e}") #timeout = 30 + self.logger.info("[DEBUG] Running function '%s' with timeout %d" % (action["name"], timeout)) try: executor = concurrent.futures.ThreadPoolExecutor() @@ -3710,7 +3711,7 @@ class AppBase: except concurrent.futures.TimeoutError as e: newres = json.dumps({ "success": False, - "reason": "Timeout error within %d seconds (2). This happens if we can't reach or use the API you're trying to use within the time limit. Configure SHUFFLE_APP_SDK_TIMEOUT=100 in Orborus to increase it to 100 seconds. Not changeable for cloud." % timeout, + "reason": "Timeout error (2) within %d seconds (2). This happens if we can't reach or use the API you're trying to use within the time limit. Configure SHUFFLE_APP_SDK_TIMEOUT=100 in Orborus to increase it to 100 seconds. Not changeable for cloud." % timeout, }) break From d52b024716206277536f6bf8baedb28f340d9705 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 12:00:22 +0200 Subject: [PATCH 130/142] Added more debug info for ai build --- backend/app_sdk/app_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 995ac7b6..15338d89 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -3687,7 +3687,7 @@ class AppBase: self.logger.info(f"[ERROR] Failed parsing timeout to int: {e}") #timeout = 30 - self.logger.info("[DEBUG] Running function '%s' with timeout %d" % (action["name"], timeout)) + self.logger.info("[DEBUG][%s] Running function '%s' with timeout %d" % (self.current_execution_id, action["name"], timeout)) try: executor = concurrent.futures.ThreadPoolExecutor() From 2efa051f68d0d3c62bb941b8292e3429c379f5a5 Mon Sep 17 00:00:00 2001 From: satti-hari-krishna-reddy Date: Tue, 21 May 2024 15:31:21 +0530 Subject: [PATCH 131/142] added a conditional check to not replace the base url if it shuffler.io --- functions/onprem/orborus/orborus.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index dc9e554c..cdface09 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -2286,8 +2286,9 @@ func createPipeline(command, identifier string) (string, error) { log.Printf("[INFO] an existing pipeline found with ID: %s. it will be deleted", pipelineId) toBeDeleted = true } + if strings.Contains(command, "shuffler.io") { - if strings.Contains(command, "kafka") { + } else { var scheme string if strings.Contains(command, "http://") { scheme = "http://" From b5ee9863e2818221c667072e610d6fc0077c6250 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 16:53:37 +0200 Subject: [PATCH 132/142] Added integration framework properly --- frontend/src/components/ConfigureWorkflow.jsx | 94 +- frontend/src/components/NewHeader.jsx | 326 +++--- frontend/src/components/OrgHeaderexpanded.jsx | 984 ++++++++---------- frontend/src/components/ParsedAction.jsx | 67 +- frontend/src/components/Priorities.jsx | 72 +- frontend/src/components/Searchfield.jsx | 3 +- frontend/src/defaultCytoscapeStyle.jsx | 4 +- frontend/src/theme.jsx | 4 +- frontend/src/views/Admin.jsx | 10 +- frontend/src/views/AngularWorkflow.jsx | 298 ++++-- frontend/src/views/Usecases.jsx | 8 +- frontend/src/views/Workflows.jsx | 2 +- 12 files changed, 1000 insertions(+), 872 deletions(-) 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) { From d40d75cc630c0f58aa1cc5318c6a3e0060b1cac6 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 17:00:34 +0200 Subject: [PATCH 133/142] Fixed workflow rendering problems --- frontend/src/views/AngularWorkflow.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index de8c0bae..e7767889 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2963,7 +2963,7 @@ const AngularWorkflow = (defaultprops) => { //console.log('got chunk of', chunk.length, 'bytes. Value: ', chunk) text += chunk; - //console.log('text so far is', text.length, 'bytes\n'); + //console.log('text so far is', text.length, 'bytes if (result.done) { console.log('returning') return text; @@ -9345,7 +9345,7 @@ const AngularWorkflow = (defaultprops) => { event.target.value = event.target.value.replaceAll("^", "_"); event.target.value = event.target.value.replaceAll("'", "_"); event.target.value = event.target.value.replaceAll("\"", "_"); - event.target.value = event.target.value.replaceAll("\\", "_"); + event.target.value = event.target.value.replaceAll("\"", "_"); event.target.value = event.target.value.replaceAll(":", "_"); event.target.value = event.target.value.replaceAll(";", "_"); event.target.value = event.target.value.replaceAll("=", "_"); @@ -17661,8 +17661,8 @@ const AngularWorkflow = (defaultprops) => { if (valid.valid === false) { if (stringjson.startsWith("{") && stringjson.endsWith("}")) { // Look for newline - if (stringjson.includes("\n") && !stringjson.includes("\\n")) { - return "Looks like you have a newline problem. Consider using the | replace: '\\n', '\\\\n' }} filter in Liquid." + if (stringjson.includes("\n") && !stringjson.includes("\n")) { + return "Looks like you have a newline problem. Consider using the | replace: '\n', '\\n' }} filter in Liquid." } else { return "The result looks like it should be JSON, but is invalid. Look for potential" } From 089ee084fcc9934b07929a7b17267a0263d1a0dd Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 17:00:58 +0200 Subject: [PATCH 134/142] Added subflowsuggestor --- .../src/components/SubflowSuggestions.jsx | 566 ++++++++++++++++++ 1 file changed, 566 insertions(+) create mode 100644 frontend/src/components/SubflowSuggestions.jsx diff --git a/frontend/src/components/SubflowSuggestions.jsx b/frontend/src/components/SubflowSuggestions.jsx new file mode 100644 index 00000000..01ad8f96 --- /dev/null +++ b/frontend/src/components/SubflowSuggestions.jsx @@ -0,0 +1,566 @@ +import React, { useEffect } from "react"; + +import { makeStyles } from "@mui/styles"; +import theme from '../theme.jsx'; +import { toast } from "react-toastify" + +import AuthenticationData from "../components/AuthenticationWindow.jsx" + +import { + Chip, + Modal, + Button, + Typography, + FormControl, + TextField, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Select, + MenuItem, + Box, + Divider, + InputLabel, + Stack, +} from '@mui/material'; + +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important", + }, +}) + +const SubflowSuggestions = (props) => { + const { + type, + globalUrl, + workflows, + notificationWorkflow, + selectedOrganization, + } = props + + const classes = useStyles(); + + const [notificationWorkflowModal, setNotificationWorkflowModal] = React.useState(false); + const [selectedAppDetails, setSelectedAppDetails] = React.useState({}); + const [notificationWorkflowTestModal, setNotificationWorkflowTestModal] = React.useState(false); + const [selectedAuth, setSelectedAuth] = React.useState(''); + const [emailData,setEmailData] = React.useState([]); + const [notificationAppDetails, setNotificationAppDetails] = React.useState([]); + const [generatedWorkflow, setGeneatedWorkflow] = React.useState({}); + const [textFieldValue, setTextFieldValue] = React.useState(""); + const [textFieldOneValue, setTextFieldOneValue] = React.useState(""); + + // getting comms & cases app from app framework + var notificationAppList = []; + if (selectedOrganization.security_framework.cases && selectedOrganization.security_framework.cases.name.length > 0) { + notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.cases); + } + if (selectedOrganization.security_framework.communication && selectedOrganization.security_framework.communication.name.length > 0) { + notificationAppList = notificationAppList.concat(selectedOrganization.security_framework.communication); + } + + useEffect(() => { + let nameList = notificationAppList.length > 0? notificationAppList.map(item => item.name): ["email"]; + prepareNotificationAppList(nameList) + }, [notificationAppList,workflows]); + + const executeTestWorkflow = (workflowid) => { + const data = { "execution_argument": '{"title":"THIS IS TEST ALERT","description":"TEST ALERT FROM SHUFFLE","reference_url": "shuffler.io"}' } + + fetch(globalUrl + `/api/v1/workflows/${workflowid}/execute`, { + 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; + } + toast("Notification workflow ran successfully"); + return response.json(); + }).catch((error) => { + console.log("Error getting workflows: " + error); + }) + } + + + const testWorkflowModal = notificationWorkflowTestModal ? + { + setNotificationWorkflowTestModal(false); + }} + > + + {/* +
    + Notification workflow +
    +
    */} + + We have updated the Notification workflow. Do you want to test it? + + + + + +
    +
    : null + + // fixxxxxxxxxxxxxxxxxxxx + const checkIfAlreadyGenerated = (appList, workflows) => { + + var workflowName = workflows.find(workflow => workflow.id === notificationWorkflow) + if (workflowName) { + workflowName = workflowName.name + } + else { + console.log("no workflow set") + return + } + if (workflowName) { + const parts = workflowName.split(' '); + console.log("parts", parts) + if (parts[0].toString() === "[GENERATED]" && parts.length > 1) { + console.log("parts1", parts[1]) + if ((appList.includes(parts[1]))) { + console.log("workflow already generated") + setGeneatedWorkflow({"app_name": parts[1]}) + } + } + } + else { + return + } + } + + const mergeAuthData = (result, responseJson) => { + const updatedResult = result.map(item => { + const matches = responseJson.filter(authItem => authItem.app.name === item.name); + return { + ...item, + authentication_data: matches.length > 0 ? matches : null + } + }) + + return updatedResult + } + + const prepareNotificationAppList = (appList) => { + var result = [] + 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) { + const filteredApps = responseJson.filter(app => appList.includes(app.name)); + const emailData = responseJson.filter(app => app.name === "email") + setEmailData(emailData) + const appDetails = filteredApps.map(app => ({ name: app.name, id: app.id })); //mapped apps with IDs as sometime Ids were not correct in security framework + // console.log("appDetails: ", appDetails) + // result = appDetails + + fetch(globalUrl + "/api/v1/apps/authentication", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + toast(`Failed getting auth for : `, response.reason); + console.log("Status not 200 for app auth :O!"); + return; + } + return response.json(); + }).then((responseJson) => { + if (!responseJson.success) { + console.log("Could not get app auth") + return; + } + // console.log("responseJson of auth: ", responseJson.data) + result = mergeAuthData(appDetails, responseJson.data) + // console.log("merged auth data: ", result) + // console.log("result", result) + result.map(item => { + fetch(globalUrl + `/api/v1/apps/${item.id}/config`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }).then((response) => { + if (response.status !== 200) { + toast(`Failed getting config for ${item.id}: `, response.reason); + console.log("Status not 200 for app config :O!"); + return; + } + return response.json(); + }).then((responseJson) => { + if (!responseJson.success) { + console.log("Could not get app config") + return; + } + var decodedString = JSON.parse(atob(responseJson.app)); + // console.log("dcodedString: ",decodedString) + item.auth_config = decodedString.authentication + item.large_image = decodedString.large_image + setNotificationAppDetails(result) + console.log("notificationAppDetails: ", notificationAppDetails) + }).then(()=>{ + checkIfAlreadyGenerated(appList,workflows); + + }).catch((error) => { + console.log("Error getting app config: " + error); + toast("Error getting app config: " + error); + }) + }) + }) + } + }).catch((error) => { + console.log("Error getting app ids: " + error); + }) + } + + const renderChips = (apps) => { + return ( + + {apps.map((app) => ( + { + console.log(`Clicked ${app.name}`) + console.log("app: ",app) + setSelectedAppDetails(app) + if (app.authentication_data && app.authentication_data.length > 0){ //fixxxxxxxx + console.log("authdata: ",app.authentication_data[0]) + setSelectedAuth(app.authentication_data[app.authentication_data.length-1].id) + } + setNotificationWorkflowModal(true) + // getAppAuth(app.name) + console.log("selectedAppDEtails",selectedAppDetails) + }} + avatar={{app.name}} + + /> + ))} + + ) + } + + const generateEmailNotificationWorkflow = async (appname,appImage,shuffleAPIKey,recepients) => { + //currently only supports figure out a way to support more apps + const workflowName = `[GENERATED] ${appname} notification workflow` + const workflowDescription = "Generated by Shuffle for sending info/error notifications." + const 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() === "email"){ + console.log("updating workflow for email") + var workflowBody = { + "name": workflowName, + "Description": workflowDescription, + "id": workflow_id, + "actions": [ + { + "app_name": "email", + "name": "send_email_shuffle", + "large_image":appImage, + "isStartNode": true, + "label": "send_email_shuffle", + "app_version": "1.3.0", + "parameters": [ + { + "name": "apikey", + "value": shuffleAPIKey + }, + { + "name": "recipients", + "value": recepients + }, + { + "name": "subject", + "value": "$exec.title" + }, + { + "name":"body", + "value":"$exec.description" + } + ] + } + ] + } + } + fetch(globalUrl + `/api/v1/workflows/${workflow_id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(workflowBody), + 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) { + if (type === "notification") { + console.log("FIXME: Notification workflow updated successfully") + toast("FIXME: Notification workflow updated successfully") + } + } + }) + } + }).catch((error) => { + console.log("Error setting workflows: " + error); + }) +} + + + + + + + const modalView = notificationWorkflowModal ? ( + { + setNotificationWorkflowModal(false); + }} + > + + +
    + {`Configure ${selectedAppDetails.name} workflow`} +
    +
    + + + {console.log("len Selected app details: ", selectedAppDetails)} + {(selectedAppDetails.authentication_data || (selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false) || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? + <> + + {(selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? "No authentication required": + <> + + Pick an authentication method from the list + + + Available authentications + + } + + + + Provide additional required details: + + { + setTextFieldOneValue(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} /> + { + setTextFieldValue(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} /> + + : + <> + 0) ? false : true} + // // setAuthenticationModalOpen={false} + selectedApp={{...selectedAppDetails,authentication: selectedAppDetails.auth_config}} + // getAppAuthentication={selectedAppDetails.name} + /> + + } + + + + + +
    +
    +) : null + + return ( +
    + {modalView} + + {/*{testWorkflowModal} */} +
    + {renderChips(notificationAppDetails.length > 0 ? notificationAppDetails : emailData)} +
    +
    + ) +} + +export default SubflowSuggestions; From 3cd8d5f0ce88f1ee889ef74c3f86460fdeccd736 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 16:18:57 +0000 Subject: [PATCH 135/142] Changed opensearch to run default 2.14.0, and fixed github.dev redirect --- docker-compose.yml | 8 ++++---- frontend/src/App.jsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a1c86dbf..2c50d5a1 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:latest + image: ghcr.io/shuffle/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -15,7 +15,7 @@ services: depends_on: - backend backend: - image: ghcr.io/shuffle/shuffle-backend:latest + image: ghcr.io/shuffle/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} # Here for debugging: @@ -34,7 +34,7 @@ services: - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped orborus: - image: ghcr.io/shuffle/shuffle-orborus:latest + image: ghcr.io/shuffle/shuffle-orborus:nightly container_name: shuffle-orborus hostname: shuffle-orborus networks: @@ -60,7 +60,7 @@ services: security_opt: - seccomp:unconfined opensearch: - image: opensearchproject/opensearch:2.11.1 + image: opensearchproject/opensearch:2.14.0 hostname: shuffle-opensearch container_name: shuffle-opensearch env_file: .env diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f2a79f65..0fbfe2e0 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -56,8 +56,8 @@ if (window.location.port === "3000") { // Development on Github Codespaces if (globalUrl.includes("app.github.dev")) { - //globalUrl = globalUrl.replace("3000", "5001") - globalUrl = "https://frikky-shuffle-5gvr4xx62w64-5001.preview.app.github.dev" + globalUrl = globalUrl.replace("-3001", "-5001") + //globalUrl = "https://frikky-shuffle-5gvr4xx62w64-5001.preview.app.github.dev" } //console.log("global: ", globalUrl) From 98d2e6ced259c4985a114343802691a77005bf2c Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 21 May 2024 18:58:02 +0200 Subject: [PATCH 136/142] Added no-image image --- frontend/public/images/no_image.png | Bin 0 -> 831 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 frontend/public/images/no_image.png diff --git a/frontend/public/images/no_image.png b/frontend/public/images/no_image.png new file mode 100644 index 0000000000000000000000000000000000000000..41a3db2f97a072b2f8182cc88341493267e0a081 GIT binary patch literal 831 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&di3`|!%T^vIy7~kIA>!%bbaqQ!C!TrL?$&16v zZmpPF7FNYv62n;Tdfc^uHNcToG^E6&<$ZqH{LPX~yA8QN=M?eHem-5H<>0gTGc65z zj91>ieS2!Ig`M5D$&-bxZEd$MUaTy<`De|$yyk-krk#FzZBxk~g{fXQpFUkWcmDj@ zIo``Jzbh*%OIv*LL~G32y58L!_5$yJ{fhcn*5H=Wn)ZMx>B+(kZ`jse;8`@^Jn4oX%C(SOcnk{p0rG^4y{%0lOE~ZH$6P7fnu&QV(crto&c?3C3!Y_34b@{CF z^73!Le!bck@4G5&b>!YS^Y}fiHC0uc&Y$<6?W1-rZ@X^i>ebr6fBo7Nw?2Js*y)-N zVjn(!ytwP`x~HEjlb;k>R##Sj6ua=+On@;`V(%HX2GJ{pvibS>W%c#>y>5qBU0o&H zl#rI_qxSy&`#h7{nwlLm+7)Ir@bmNMmFkFay^~Y^D8M2tF!{jQv%RUCBg^mSnEkKd zRqMXsmS%AKK+(>e%P+6=y4b7>(TYAT$hZ2(v)9f7S5%rZ_O~}%#L4LLNje>Q(7*0Z zPq^09Yv=R(+~zm&{fOQ3!*H76{FHyK3Cwlk`eOFatAjX;^3U9Vbaca+=P#NcFujw~ zZwtM#d;!xtX?>Q1EH+0f9_T8B+15N_P?P({`D;p5U0vKv_8)6zpG=AIc;n#lsf?wb z;ZNSih%ej1Z$&#Ch|7Ev;mdV#_pq)g|3@#o|$_u=38^xjXyg*S)R8YFPL)oh`1VqxPbb#_a#_Ip!zclTFOo+Ecu4URqD_}g6}=aboAhPewBr!cq+Y68 Date: Tue, 21 May 2024 17:16:08 +0000 Subject: [PATCH 137/142] Minor app.jsx fix --- frontend/src/App.jsx | 73 +++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 0fbfe2e0..cf67f686 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -56,10 +56,9 @@ if (window.location.port === "3000") { // Development on Github Codespaces if (globalUrl.includes("app.github.dev")) { - globalUrl = globalUrl.replace("-3001", "-5001") - //globalUrl = "https://frikky-shuffle-5gvr4xx62w64-5001.preview.app.github.dev" + globalUrl = globalUrl.replace("-3000.", "-5001.") + globalUrl = globalUrl.replace("-3001.", "-5001.") } -//console.log("global: ", globalUrl) const App = (message, props) => { @@ -69,6 +68,7 @@ const App = (message, props) => { const [isLoggedIn, setIsLoggedIn] = useState(false) const [dataset, setDataset] = useState(false) const [isLoaded, setIsLoaded] = useState(false) + const [modalOpen, setModalOpen] = useState(false) const [curpath, setCurpath] = useState(typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname) @@ -177,34 +177,45 @@ const App = (message, props) => { curpath={curpath} setCurpath={setCurpath} /> - {!isLoaded ? null : - userdata.chat_disabled === true ? null : - - } -
    + {!isLoaded ? null : + userdata.chat_disabled === true ? null : + + } + +
    +
    +
    + {/*
    */} From 71a37b4e685fbe2b686874b1e6d347bd3cab41df Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 22 May 2024 01:23:09 +0200 Subject: [PATCH 138/142] Added license popup properly --- frontend/src/components/LicencePopup.jsx | 935 +++++++++++++++++++++++ frontend/src/components/NewHeader.jsx | 93 ++- frontend/src/views/Admin.jsx | 332 ++++++++ frontend/src/views/AngularWorkflow.jsx | 271 ++++++- functions/kubernetes/orborus.yaml | 80 ++ 5 files changed, 1699 insertions(+), 12 deletions(-) create mode 100644 frontend/src/components/LicencePopup.jsx create mode 100644 functions/kubernetes/orborus.yaml diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx new file mode 100644 index 00000000..f2a1ef99 --- /dev/null +++ b/frontend/src/components/LicencePopup.jsx @@ -0,0 +1,935 @@ +import React, { useState, useEffect } from "react"; +import ReactGA from 'react-ga4'; + +import theme from "../theme.jsx"; +import { useTheme } from "@mui/styles"; +import countries from "../components/Countries.jsx"; +import { + Box, + Paper, + Typography, + Divider, + Button, + Grid, + Card, + Dialog, + DialogTitle, + DialogContent, + TextField, + InputAdornment, + IconButton, + Chip, + Checkbox, + Tooltip, + Slider, + DialogActions, + CardContent, + ButtonGroup, +} from "@mui/material"; + +import { useNavigate, Link } from "react-router-dom"; +import { Autocomplete } from "@mui/material"; +import { toast } from "react-toastify" + +import { + Cached as CachedIcon, + ContentCopy as ContentCopyIcon, + Draw as DrawIcon, + Close as CloseIcon, + Done as DoneIcon, + Clear as ClearIcon, + AddTask as AddTaskIcon, +} from "@mui/icons-material"; + +import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; +import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" + +const LicencePopup = (props) => { + const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile } = props; + //const alert = useAlert(); + let navigate = useNavigate(); + const isCloud = typeof window === 'undefined' || window === undefined || window.location === undefined ? true : window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + + const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); + const [dealList, setDealList] = React.useState([]); + const [dealName, setDealName] = React.useState(""); + const [dealAddress, setDealAddress] = React.useState(""); + const [dealType, setDealType] = React.useState("MSSP"); + const [selectedOrganization, setSelectedOrganization] = React.useState({}); + const [dealCountry, setDealCountry] = React.useState("United States"); + const [dealCurrency, setDealCurrency] = React.useState("USD"); + const [dealStatus, setDealStatus] = React.useState("initiated"); + const [dealValue, setDealValue] = React.useState(""); + const [dealDiscount, setDealDiscount] = React.useState(""); + const [dealerror, setDealerror] = React.useState(""); + const [variant, setVariant] = useState(0) + const [shuffleVariant, setShuffleVariant] = useState(isCloud ? 0 : 1) + + + // const parsedFields = maxFields === undefined ? 300 : maxFields + const initialShuffleVariant = isCloud ? 0 : 1; + const [paymentType, setPaymentType] = useState(0) + const [currentPrice, setCurrentPrice] = useState(129) + const [isLoaded, setIsLoaded] = useState(false) + const [errorMessage, setErrorMessage] = useState("") + const [highlight, setHighlight] = useState(false) + + // Cloud + const [calculatedApps, setCalculatedApps] = useState(600) + const [calculatedCost, setCalculatedCost] = useState("$600") + const [selectedValue, setSelectedValue] = useState(100) + + // Onprem + const [calculatedCores, setCalculatedCores] = useState('600') + const [onpremSelectedValue, setOnpremSelectedValue] = useState(8) + + const payasyougo = "Pay as you go" + const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : "" + + const paperStyle = { + padding: 20, + borderRadius: theme.palette.borderRadius, + height: "100%", + } + + billingInfo.subscription = { + "active": true, + "name": "Pay as you go", + "price": typecost_single, + "currency": "USD", + "currency_text": "$", + "interval": "app run / month", + "description": "Pay as you go", + "features": [ + "Basic Support", + "Limited App Runs (10.000)", + ], + "limit": 10000, + } + + const sendSignatureRequest = (subscription) => { + const url = `${globalUrl}/api/v1/orgs/${selectedOrganization.id}`; + + fetch(url, { + body: JSON.stringify({ + org_id: selectedOrganization.id, + subscription: subscription, + }), + mode: "cors", + method: "POST", + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + if (response.status !== 200) { + console.log("Error in response"); + } + return response.json(); + }) + .then((responseJson) => { + console.log("Response from signature request: ", responseJson); + }) + .catch((error) => { + console.log("Error: ", error); + }) + } + + const SubscriptionObject = (props) => { + const { globalUrl, index, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props; + + const [signatureOpen, setSignatureOpen] = React.useState(false); + const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed) + const [hovered, setHovered] = React.useState(false) + + var top_text = "Base Cloud Access" + if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) { + subscription.name = "Enterprise" + subscription.currency_text = "$" + subscription.price = subscription.level * 180 + subscription.limit = subscription.level * 100000 + subscription.interval = subscription.recurrence + subscription.features = [ + "Includes " + subscription.limit + " app runs/month. ", + "Multi-Tenancy and Region-Selection", + "And all other features from /pricing", + ] + } + + var newPaperstyle = JSON.parse(JSON.stringify(paperStyle)) + if (subscription.name === "Enterprise" && subscription.active === true) { + top_text = "Current Plan" + + newPaperstyle.border = "1px solid #f85a3e" + } + + var showSupport = false + if (subscription.name.includes("default")) { + top_text = "Custom Contract" + newPaperstyle.border = "1px solid #f85a3e" + showSupport = true + } + + if (subscription.name.includes("App Run Units")) { + top_text = "Cloud Access" + showSupport = true + } + + if (subscription.name.includes("Open Source")) { + top_text = "Open Source" + showSupport = true + } + + if (subscription.name.includes("Scale")) { + top_text = "Scale access" + } + + if (highlight === true) { + // Add an "Upgrade now" button + // newPaperstyle.border = "1px solid #f85a3e" + } + + return ( + +
    + setHovered(true)} + // onMouseLeave={() => setHovered(false)} + > + + + { + e.preventDefault(); + setSignatureOpen(false); + setTosChecked(false) + }} + > + + + + Read and Accept the EULA + + + { + setTosChecked(e.target.checked) + }} + inputProps={{ 'aria-label': 'primary checkbox' }} + /> + { + setTosChecked(!tosChecked) + }}> + Accept + + + By clicking the “accept” button, you are signing the document, electronically agreeing that it has the same legal validity and effects as a handwritten signature, and that you have the competent authority to represent and sign on behalf an entity. Need support or have questions? Contact us at support@shuffler.io. + + +
    + +
    +
    +
    + +
    + {top_text === "Base Cloud Access" && userdata.has_card_available === true ? + { + console.log("Clicked chip") + }} + variant="outlined" + color="primary" + /> + : null} + + {top_text} + + + {top_text === "Base Cloud Access" && userdata.has_card_available === false ? + + : null} + {isCloud && highlight === true && top_text !== "Base Cloud Access" ? + + { + setSignatureOpen(true) + }} + > + + + + : null} +
    + +
    + + {subscription.name} + + + {subscription.currency_text !== undefined ? +
    + + {subscription.currency_text}{subscription.price} + + + / {subscription.interval} + +
    + : null} + + + Features + +
      + {subscription.features !== undefined && subscription.features !== null ? + subscription.features.map((feature, index) => { + var parsedFeature = feature + if (feature.includes("Documentation: ")) { + parsedFeature = + + Documentation to get started + + } + + if (feature.includes("Worker License: ")) { + const fieldId = "webhook_uri_field_" + index + parsedFeature = + + + Use the {feature.split("Worker License: ")[0]} Worker + + { }} + InputProps={{ + endAdornment: + + { + var copyText = document.getElementById(fieldId); + if (copyText !== undefined && copyText !== null) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(copyText.value); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999 + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + toast("Copied Webhook URL"); + } else { + console.log("Couldn't find webhook URI field: ", copyText); + } + }} + edge="end" + > + + + + }} + fullWidth + /> + + } + + return ( +
    • + + {parsedFeature} + +
    • + ) + }) + : null} +
    + Billing email: {selectedOrganization.org} +
    + + + + +
    + {/* +
    + + Schedule Call Now +
    + */} + +
    +
    + ) + } + + useEffect(() => { + console.log("New variant: ", shuffleVariant) + + if (shuffleVariant === 1) { + setCalculatedCost("$600") + setSelectedValue(8) + } else { + setCalculatedCost("$540") + setSelectedValue(300) + } + }, [shuffleVariant]) + + if (typeof window === 'undefined' || window.location === undefined) { + return null + } + + const setMonthlyCost = (variant, paymentType) => { + setErrorMessage("") + if (variant === 0 && paymentType === 0) { + setCurrentPrice(129) + } else if (variant === 0 && paymentType === 1) { + setCurrentPrice(155) + } else if (variant === 1 && paymentType === 0) { + setCurrentPrice(999) + } else if (variant === 1 && paymentType === 1) { + setCurrentPrice(1199) + } else if (variant === 2 && paymentType === 0) { + setCurrentPrice(15) + } else if (variant === 2 && paymentType === 1) { + setCurrentPrice(18) + } + } + + const handleChange = (event, newValue) => { + console.log("Event, value: ", event.target, newValue) + + if (shuffleVariant === 1) { + setSelectedValue(newValue) + if (newValue === 32) { + setCalculatedCost(`Get A Quote`) + } else { + setCalculatedCost(`$${newValue * 75}`) + } + } else { + setSelectedValue(newValue) + if (newValue < 300) { + setCalculatedCost(`Pay as you go`) + } else if (newValue === 1000) { + setCalculatedCost(`Get A Quote`) + } else { + setCalculatedCost(`$${newValue * 1000 * typecost}`) + } + } + } + + if (!isLoaded) { + setIsLoaded(true) + + const tmpsearch = typeof window === 'undefined' || window.location === undefined ? "" : window.location.search + const tmpVar = new URLSearchParams(tmpsearch).get("variant") + if (tmpVar !== undefined && tmpVar !== null && tmpVar < 3) { + setVariant(parseInt(tmpVar)) + } + + const tmpType = new URLSearchParams(tmpsearch).get("payment_type") + if (tmpType !== undefined && tmpType !== null && tmpType < 2) { + setPaymentType(parseInt(tmpType)) + } + + const modal = new URLSearchParams(tmpsearch).get("payment_modal") + if (modal !== undefined && modal !== null && modal === "open") { + setModalOpen(true) + } + + const tmpView = new URLSearchParams(tmpsearch).get("view") + if (tmpView !== undefined && tmpView !== null && tmpView === "failure") { + setErrorMessage("Something went wrong with your payment. Please try again.") + } + + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundTab = params["tab"]; + if (foundTab !== null && foundTab !== undefined) { + if (foundTab === "onprem") { + setShuffleVariant(1); + } else if (foundTab === "cloud") { + setShuffleVariant(0); + } + } + + const foundHighlight = params["highlight"]; + if (foundHighlight !== null && foundHighlight !== undefined) { + setHighlight(true) + } + } + + //const skipFreemode = window.location.pathname.startsWith("/admin") + const skipFreemode = false + const maxwidth = isMobile ? "91%" : skipFreemode ? 1100 : 1200 + const activeIcon = + const inActiveIcon = + const defaultTaskIcon = + + const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)" + const level1Button = + + + const level2Button = + + + const level3Button = skipFreemode ? null : + + + + const cardStyle = { + // height: "100%", + // width: "100%", + // textAlign: "center", + color: "white", + } + + + const isLoggedInHandler = () => { + if (calculatedCost === payasyougo) { + handlePayasyougo(props.userdata) + return + } + + const priceItem = window.location.origin === "https://shuffler.io" ? + shuffleVariant === 0 ? "app_executions" : "cores" + : + shuffleVariant === 0 ? "price_1MROFrDzMUgUjxHShcSxgHO1" : "price_1NXjQqDzMUgUjxHSg690R4FP" + + const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success` + const failUrl = `${window.location.origin}/pricing?admin_tab=billing&payment=failure` + + console.log("Priceitem: ", priceItem, shuffleVariant) + var checkoutObject = { + lineItems: [ + { + price: priceItem, + quantity: shuffleVariant === 0 ? selectedValue / 100 : selectedValue, + }, + ], + mode: "subscription", + billingAddressCollection: "auto", + successUrl: successUrl, + cancelUrl: failUrl, + clientReferenceId: props.userdata.active_org.id, + } + + stripe.redirectToCheckout(checkoutObject) + .then(function (result) { + console.log("SUCCESS STRIPE?: ", result) + + ReactGA.event({ + category: "pricing", + action: "add_card_success", + label: "", + }) + }) + .catch(function (error) { + console.error("STRIPE ERROR: ", error) + + ReactGA.event({ + category: "pricing", + action: "add_card_error", + label: "", + }) + }) + } + + return ( +
    + + + {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? + + : !isCloud ? + + + + + : null} + + {isCloud && + selectedOrganization.subscriptions !== undefined && + selectedOrganization.subscriptions !== null && + selectedOrganization.subscriptions.length > 0 ? + selectedOrganization.subscriptions + .reverse() + .map((sub, index) => { + return ( + + ) + }) + : null} + + + + {errorMessage.length > 0 ? Error: {errorMessage} : null} + +
    + + +
    + {shuffleVariant === 1 ? "Scale" : "Enterprise"} + + + {shuffleVariant === 0 ? + "SaaS / Cloud - Per Month" + : + "Open Source + Scale License" + } + + + { + + if (calculatedCores === "Get A Quote") { + console.log("Clicked on get a quote") + if (window.drift !== undefined) { + window.drift.api.startInteraction({ interactionId: 340785 }) + } + } + }}>{calculatedCost} + For {shuffleVariant === 1 ? `${selectedValue} CPU cores` : `${selectedValue}k App Runs`}: +
    + + { + handleChange(event, newValue) + }} + marks + value={selectedValue} + step={shuffleVariant === 0 ? 100 : 4} + min={shuffleVariant === 0 ? 100 : 8} + max={shuffleVariant === 0 ? 1000 : 32} + valueLabelDisplay="auto" + /> +
    + +
    +
    + {defaultTaskIcon} + Priority Support +
    + +
    + {defaultTaskIcon} + + {shuffleVariant === 0 ? "Multi-Tenant" : "Scalable Orborus"} + +
    + +
    + {defaultTaskIcon} + + {shuffleVariant === 0 ? "Multi-Region Tenants" : "High Availability"} + +
    + +
    + {defaultTaskIcon} + Help with Workflow and App development +
    +
    +
    + + + + + + + + +
    + ) +} + +export default LicencePopup; diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 7104f873..8df3fc0f 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -5,6 +5,7 @@ import { BrowserView, MobileView } from "react-device-detect"; import { useNavigate, Link } from "react-router-dom"; import ReactGA from "react-ga4"; +import LicencePopup from "../components/LicencePopup.jsx"; import SearchField from "../components/Searchfield.jsx"; import { Paper, @@ -24,6 +25,8 @@ import { Divider, LinearProgress, AppBar, + Dialog, + DialogTitle, } from "@mui/material"; import { @@ -59,9 +62,8 @@ const Header = (props) => { userdata, isMobile, serverside, - setModalOpen, - curpath, + billingInfo } = props; @@ -71,11 +73,13 @@ const Header = (props) => { const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor); const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor); const [isHeader, setIsHeader] = React.useState(false); + const [modalOpen, setModalOpen] = useState(false); const [anchorEl, setAnchorEl] = React.useState(null); const [anchorElAvatar, setAnchorElAvatar] = React.useState(null); const [subAnchorEl, setSubAnchorEl] = React.useState(null); const [upgradeHovered, setUpgradeHovered] = React.useState(false); const [showTopbar, setShowTopbar] = useState(false) + const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_XAxwE2Fp9DEbEcNYw4UKmyby00vIlIPPRp" : "pk_test_EdxgKfqmQGXY5JLjdBqtuhCw00BHbiKJDB" let navigate = useNavigate(); const handleClick = (event) => { @@ -679,6 +683,80 @@ const Header = (props) => { marginRight: 10, }; + const modalView = ( + <> + {modalOpen && ( +
    + )} + { + setModalOpen(false); + }} + PaperProps={{ + style: { + color: "white", + minWidth: 850, + minHeight: 370, + padding: 10, + backgroundColor: "rgba(0, 0, 0, 1)", + borderRadius: theme.palette.borderRadius, + }, + }} + > + + + Upgrade your plan + + { + if (isCloud) { + ReactGA.event({ + category: "header", + action: "close_Upgread_popup", + label: "", + })}; + setModalOpen(false); + }} + style={{ + marginLeft: "auto", + position: "absolute", + top: 20, + right: 20, + }} + > + + + +
    + +
    +
    + + ); + // Handle top bar or something const defaultTop = -2 const loginTextBrowser = !isLoggedIn ? ( @@ -1537,11 +1615,12 @@ const Header = (props) => { {topbar} -
    - {loginTextBrowser} -
    - -
    +
    + {loginTextBrowser} +
    + {modalView} + +
    : {loginTextMobile} }; diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 13a9323d..beed4601 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -164,6 +164,11 @@ const Admin = (props) => { const [selectedOrganization, setSelectedOrganization] = React.useState({}); //console.log("Selected: ", selectedOrganization) + const [appAuthenticationGroupModalOpen , setAppAuthenticationGroupModalOpen] = React.useState(false); + const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]); + const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState(""); + const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState(""); + const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]); const [organizationFeatures, setOrganizationFeatures] = React.useState({}); const [loginInfo, setLoginInfo] = React.useState(""); const [curTab, setCurTab] = React.useState(0); @@ -426,6 +431,40 @@ const Admin = (props) => { }); }; + const createAppAuthenticationGroup = (name, description, appAuthIds) => { + let app_auths = appAuthIds.map((appAuthId) => { + return { id: appAuthId }; + }); + + fetch(globalUrl + "/api/v1/apps/authentication/group", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify({ + label: name, + description: description, + app_auths: app_auths + }), + }) + .then((response) => { + if (response.status !== 200) { + throw new Error("Failed to create app authentication group"); + } + + return response.json(); + }) + .then((responseJson) => { + // getAppAuthenticationGroups(); + toast("App authentication group created"); + }) + .catch((error) => { + toast(error.toString()); + }); + }; + const categories = [ { name: "Ticketing", @@ -2050,6 +2089,33 @@ If you're interested, please let me know a time that works for you, or set up a }); }; + const getAppAuthenticationGroups = () => { + fetch(globalUrl + "/api/v1/apps/authentication/group", { + 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; + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === true) { + setAppAuthenticationGroups(responseJson.data); + } + }) + .catch((error) => { + toast(error.toString()); + }); + }; + const getAppAuthentication = () => { fetch(globalUrl + "/api/v1/apps/authentication", { method: "GET", @@ -2238,6 +2304,7 @@ If you're interested, please let me know a time that works for you, or set up a } else if (newValue === 2) { document.title = "Shuffle - admin - app authentication"; getAppAuthentication(); + getAppAuthenticationGroups(); } else if (newValue === 3) { document.title = "Shuffle - admin - Files"; } else if (newValue === 4) { @@ -5095,8 +5162,145 @@ If you're interested, please let me know a time that works for you, or set up a setAuthenticationFields(newfields); }; + + const handleAppAuthGroupCheckbox = (data) => { + let appOrginal = data.app + if (appsForAppAuthGroup.includes(appOrginal.id)) { + return; + } + + setAppsForAppAuthGroup([...appsForAppAuthGroup, data.id]); + console.log("Apps for app auth group: ", appsForAppAuthGroup); + }; + const authenticationView = curTab === 2 ? ( + <> + {/* (appAuthenticationGroupModalOpen : { */} + {appAuthenticationGroupModalOpen && ( + { + setAppAuthenticationGroupModalOpen(false); + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: "1200px", + minHeight: "320px", + }, + }} + > + + App Authentication Groups + + + +
    + { + setAppAuthenticationGroupName(event.target.value); + }} + /> +
    +
    + { + setAppAuthenticationGroupDescription(event.target.value); + }} + /> +
    + +
    + {/* Show a check box list of all app authentications to add to the auth group */} +
    + {authentication.map((data, index) => ( +
    + +
    + + { + handleAppAuthGroupCheckbox(data) + }} + name={data.label} + disabled={data.app.id in appsForAppAuthGroup} + /> +
    + + } + label={data.label} + /> +
    + ))} +
    + +
    + + +
    + +
    +
    +
    + )} + +

    App Authentication

    @@ -5376,6 +5580,134 @@ If you're interested, please let me know a time that works for you, or set up a })}
    + + {/*
    +
    +

    App Authentication Groups

    + + Groups of authentication options for subflows.{" "} + + Learn more about App Authentication Groups + + + + + + + + + + + + + + {appAuthenticationGroups.map((data, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + return ( + + + + + {data.app_auths.map((appAuth, index) => ( + + {appAuth.app.name} + + ))} +
    + } + style={{ minWidth: 250, maxWidth: 250 }} + /> + + + { + }} + disabled={true} + > + + + { + // deleteAppAuthenticationGroup(data); + }} + disabled={true} + > + + +
    + } + style={{ minWidth: 150, maxWidth: 150 }} + /> + + + ); + } + )} + + + + +
    +
    */} + ) : null; const getLogs = async (ip, userId) => { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e7767889..71c584ac 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -141,6 +141,7 @@ import ParsedAction from "../components/ParsedAction.jsx"; import PaperComponent from "../components/PaperComponent.jsx" import ExtraApps from "../components/ExtraApps.jsx" import EditWorkflow from "../components/EditWorkflow.jsx" +import { act } from "react"; // import AppStats from "../components/AppStats.jsx"; const noImage = "/public/no_image.png"; @@ -3034,6 +3035,48 @@ const AngularWorkflow = (defaultprops) => { } } + const [usedSubflowApps, setUsedSubflowApps] = React.useState([]); + + const getWorkflowApps = (workflow_id) => { + let apps = [] + + if (workflow_id === "") { + console.log("workflow_id is empty"); + return {}; + } + + fetch(`${globalUrl}/api/v1/workflows/${workflow_id}`, { + 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 response.json(); + }) + .then((responseJson) => { + for (let index in responseJson.actions) { + apps.push(responseJson.actions[index]); + } + + console.log("Setting used subflow apps: ", apps) + setUsedSubflowApps(apps); + + return apps + }) + .catch((error) => { + console.log("Get workflow apps error: ", error); + }); + + return apps + }; + const getWorkflow = (workflow_id, sourcenode) => { fetch(`${globalUrl}/api/v1/workflows/${workflow_id}`, { method: "GET", @@ -3863,6 +3906,16 @@ const AngularWorkflow = (defaultprops) => { //const data = JSON.parse(JSON.stringify(event.target.data())) const data = event.target.data() + + console.log("NODE SELECT: ", data) + + if (data.app_name === "Shuffle Workflow") { + console.log("Shuffle Workflow selected") + if (data.parameters[0].value !== undefined && data.parameters[0].value !== null && data.parameters[0].value.length > 0) { + console.log("Get workflow apps calling") + getWorkflowApps(data.parameters[0].value) + } + } if (data.buttonType == "ACTIONSUGGESTION") { const attachedToId = data.attachedTo @@ -11374,6 +11427,193 @@ const AngularWorkflow = (defaultprops) => { setWorkflow(workflow); } + // Function to transform the data + const transformAuthData = (authData) => { + const transformedData = {}; + + let subflowId = workflow.triggers[selectedTriggerIndex].parameters[0].value; + + // get the apps used in "find your workflow" + if (subflowId === "" && subflowId === undefined && subflowId === null) { + console.log("subflow is empty") + return {}; + } + + let workflowApps = usedSubflowApps; + + if (workflowApps === undefined || workflowApps === null) { + console.log("workflow apps is empty"); + return {}; + } + + // get the app ids + // let appIdsInWorkflow = [...new Set(workflowApps.map(app => app.app_id))]; + let appIdsInWorkflow = []; + + Object.entries(workflowApps).forEach(([key, value]) => { + console.log("VALUE: ", value) + appIdsInWorkflow.push(value.app_id); + }) + + appIdsInWorkflow = [...new Set(appIdsInWorkflow)]; + + console.log("appIdsInWorkflow: ", appIdsInWorkflow) + + console.log("authData: ", authData, "workflowApps: ", workflowApps) + + // loop through the authData and create transformedData which looks like: + // appId: [auth1, auth2, ...] + authData.forEach((auth) => { + const { app } = auth; + const appId = app.id; + + // check if the app is used in the workflow + if (appIdsInWorkflow.includes(appId)) { + if (transformedData[appId] === undefined) { + transformedData[appId] = []; + } + + transformedData[appId].push(auth); + } + + }); + + console.log("transformedData: ", transformedData) + + return transformedData; + + }; + + const AppAuthSelector = ({ appAuthData }) => { + const [selectedAuth, setSelectedAuth] = useState(""); + const [transformedAuthData, setTransformedAuthData] = useState({}); + + useEffect(() => { + setTransformedAuthData(transformAuthData(appAuthData)); + }, [appAuthData, selectedAuth]); + + const handleShowingValue = (appName) => { + let mappingWithName = {} + let listWithValues = workflow.triggers[selectedTriggerIndex].parameters[5]?.value.split(";").filter(e => e).map(e => e.split("=")) + console.log("LIST WITH VALUES: ", listWithValues) + for (let i = 0; i < listWithValues.length; i++) { + mappingWithName[listWithValues[i][0]] = listWithValues[i][1] + } + + if (mappingWithName[appName] !== undefined) { + return mappingWithName[appName]; + } + + return "no-overrides"; + } + + const handleSelectChange = (appName, appId, event) => { + const authId = event.target.value || "no-override"; + + if (authId === "no-override") { + // remove the override parameter + let oldValue = workflow.triggers[selectedTriggerIndex].parameters[5].value; + // replace from appName= to the next ; + let newValue = oldValue.replace(new RegExp(appName + "=[^;]*;"), ""); + + workflow.triggers[selectedTriggerIndex].parameters[5].value = newValue + setSelectedAuth(""); + return + } + + const auth = transformedAuthData[appId].find((auth) => auth.id === authId); + + if (auth === undefined) { + setSelectedAuth(""); + return; + } + + // // check if the trigger already has an override parameter + // for (let i = 0; i < workflow.triggers[selectedTriggerIndex].parameters.length; i++) { + // // if name includes the app id + // if (workflow.triggers[selectedTriggerIndex].parameters[i].name.includes(appId + "_override")) { + // // update the value + // workflow.triggers[selectedTriggerIndex].parameters[i].value = auth.id; + // setSelectedAuth(auth.id); + // return; + // } + // } + + if (workflow.triggers[selectedTriggerIndex].parameters[5] === undefined || workflow.triggers[selectedTriggerIndex].parameters[5] === null) { + workflow.triggers[selectedTriggerIndex].parameters[5] = { + name: "auth_override", + value: "", + }; + } + + let authGroupValue = workflow.triggers[selectedTriggerIndex].parameters[5].value; + + if (authGroupValue === undefined || authGroupValue === null || authGroupValue === "") { + workflow.triggers[selectedTriggerIndex].parameters[5].value = appName + "=" + auth.id + ";"; + } else { + // check if the app is already in the list + if (authGroupValue.includes(appName)) { + let oldValue = workflow.triggers[selectedTriggerIndex].parameters[5].value; + let newValue = oldValue.replace(new RegExp(appName + "=[^;]*;"), appName + "=" + auth.id + ";"); + workflow.triggers[selectedTriggerIndex].parameters[5].value = newValue; + } else { + workflow.triggers[selectedTriggerIndex].parameters[5].value += appName + "=" + auth.id + ";"; + } + } + + // workflow.triggers[selectedTriggerIndex].parameters.push({ + // name: auth.label + "_" + auth.app.id + "_override", + // value: auth.id, + // }); + setSelectedAuth(auth.id); + }; + + console.log("TRANSFORMED AUTH DATA: ", transformedAuthData); + + return ( +
    + {Object.entries(transformedAuthData).map(([appId, authList]) => ( +
    + + +
    + ))} +
    + ); + }; const SubflowSidebar = () => { const [menuPosition, setMenuPosition] = useState(null); @@ -11860,6 +12100,10 @@ const AngularWorkflow = (defaultprops) => { name: "check_result", value: "false", }; + workflow.triggers[selectedTriggerIndex].parameters[5] = { + name: "auth_override", + value: "", + }; /* // API-key has been replaced by auth key for the execution. @@ -11880,8 +12124,6 @@ const AngularWorkflow = (defaultprops) => { */ } - - const handleSubflowStartnodeSelection = (e) => { setSubworkflowStartnode(e.target.value); @@ -12203,8 +12445,8 @@ const AngularWorkflow = (defaultprops) => { borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { - setLastSaved(false) - console.log("Found value: ", newValue) + setLastSaved(false) + console.log("Found value: ", newValue) var parsedinput = { target: { value: newValue } } @@ -12248,6 +12490,7 @@ const AngularWorkflow = (defaultprops) => { }} value={data} onClick={() => { + getWorkflowApps(data.id); handleWorkflowSelectionUpdate({ target: { value: data @@ -12329,7 +12572,7 @@ const AngularWorkflow = (defaultprops) => { borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { - setLastSaved(false) + setLastSaved(false) handleSubflowStartnodeSelection({ target: { value: newValue } }) }} renderOption={(props, action, state) => { @@ -12477,6 +12720,24 @@ const AngularWorkflow = (defaultprops) => { */}
    + +
    +
    +
    +
    +
    + Auth Override +
    +
    + +
    +
    + +
    +
    +
    +
    +
    ); } diff --git a/functions/kubernetes/orborus.yaml b/functions/kubernetes/orborus.yaml new file mode 100644 index 00000000..a94622ba --- /dev/null +++ b/functions/kubernetes/orborus.yaml @@ -0,0 +1,80 @@ +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: default + name: pod-manager +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "create", "update", "delete"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create", "get", "list", "watch", "delete"] + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pod-manager-binding + namespace: default +subjects: +- kind: ServiceAccount + name: default + namespace: default +roleRef: + kind: Role + name: pod-manager + apiGroup: rbac.authorization.k8s.io + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: orborus + name: orborus +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: orborus + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: orborus + spec: + containers: + - env: + - name: BASE_URL + value: "https://shuffler.io" + - name: SHUFFLE_SCALE_REPLICAS + value: "7" + - name: IS_KUBERNETES + value: "true" + - name: ENVIRONMENT_NAME + value: "environment test" + - name: ORG + value: "9c938e5b-d812-40d9-92f0-93783f43ec0d" + - name: AUTH + value: "3663a270-bb3a-4678-a365-d879601a1a0c" + + image: ghcr.io/shuffle/shuffle-orborus:nightly + #imagePullPolicy: Never + name: shuffle-orborus + resources: {} + hostname: shuffle-orborus + restartPolicy: Always From 714fa9fbc4526c09500ab7566a6a0ea61c7c4203 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 22 May 2024 01:23:57 +0200 Subject: [PATCH 139/142] Modal fix in header --- frontend/src/App.jsx | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index cf67f686..5b8f4103 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -68,7 +68,6 @@ const App = (message, props) => { const [isLoggedIn, setIsLoggedIn] = useState(false) const [dataset, setDataset] = useState(false) const [isLoaded, setIsLoaded] = useState(false) - const [modalOpen, setModalOpen] = useState(false) const [curpath, setCurpath] = useState(typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname) @@ -195,24 +194,22 @@ const App = (message, props) => {
    From fda26840f8cfa25883f52016bce6ea9c01c3c83c Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 22 May 2024 02:03:31 +0200 Subject: [PATCH 140/142] Fixed last minor bugs for open source 1.4 --- frontend/public/images/no_image.png | Bin 831 -> 433 bytes .../public/images/social/resized/discord.png | Bin 0 -> 4575 bytes .../public/images/social/resized/github.png | Bin 0 -> 4563 bytes .../public/images/social/resized/twitter.png | Bin 0 -> 4378 bytes frontend/src/App.jsx | 2 + frontend/src/components/Billing.jsx | 2 +- frontend/src/components/LicencePopup.jsx | 62 ++++++++++++------ frontend/src/components/NewHeader.jsx | 26 ++------ frontend/src/views/Workflows.jsx | 2 +- 9 files changed, 51 insertions(+), 43 deletions(-) create mode 100755 frontend/public/images/social/resized/discord.png create mode 100755 frontend/public/images/social/resized/github.png create mode 100755 frontend/public/images/social/resized/twitter.png diff --git a/frontend/public/images/no_image.png b/frontend/public/images/no_image.png index 41a3db2f97a072b2f8182cc88341493267e0a081..8c6937e2ba887d00fd301e5f6cf3fd5d5cd8ee2c 100644 GIT binary patch literal 433 zcmeAS@N?(olHy`uVBq!ia0vp^>p_@>8Aw*{nd}CnL<4+6T=n(!|NsAg>C&b3>(_U8 zcjx8hSy)&on0l{fU|1O3k$`5TUOP1D?v zq3>1hFh^B><03K9%l$75-f}X$b5(um{yt@rHRFk|>nrwK-1IT=;XZJ3&A*LhWo4<8 zuiyF*_Dpr-AhLY7ZPsW_`W+QYB(qci7UKNB=#(-E~j$^BQ6P_wC0scc&lME>MDkMfbn4 Y{%D*VSFdZR0*nI&Pgg&ebxsLQ0DXhJyZ`_I literal 831 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 literal 0 HcmV?d00001 diff --git a/frontend/public/images/social/resized/twitter.png b/frontend/public/images/social/resized/twitter.png new file mode 100755 index 0000000000000000000000000000000000000000..46c8b3d72d2f5fb85c081c612842ae0d61e50123 GIT binary patch literal 4378 zcmY*dbyO5iv|hRqSd{Li1f)YsKx(A~TqPC+T)Mk+X%>N13F!qv7bK;-OG+99>8=Io z@a^xsf8INDX6~6g_x^M1`_9C@)YYIMWh4av02E+NRRgS4|8El$Vo&!ctC3iNZ>y}M z3;370$8Nl zPW~h$7nuPVL?Obeh*2m-HT{(a@=d@Y++N$;Y~rK_#)W*SGLZGT*&AEIJdDaaBs==Zr3;TV(CvgXr5Yy z_(%^(MMjPv4SBZ(8F`@m^7q~syf{o7#iKW#6V9@PAe7?WZs&GcdfKv@30`QExBqxZI4lM{<@AYbWp%O-d^2)WUsCjNk#jRuG;oU zQOwucolh?M^C9zZ{N90wA9xDzhp3lnm_6P>acxtX_Y9I>> zf(MGfgm;gyDTPnxK?mTEF)OJCaq4<~;e6n;h>ngao;D!Xn?v2oXWaRo48G(6V zK56(IjI-?Rv)-hpXnyJ6O1okA!}ckFR>Dm{Uj%otF$tSNG{6;yD-$_9tT52$I9+kJ z=gRlwi82xdYjC!8gg~fqQ7F#(`$n*K+o>)RNt4NR?6Pbrp;2k<@mbJWX^_;yj(u(r z{dhXGHvCh`Yr~grgvOL+;wn*Z60Xk*)%6IX9i(x;^X0{u?n{I%(&Yrhfwc<6k>M;S-|fImC@O}NGx z?6*r)&E8D4-;T%N(2yKd3s)Heagt&>-Tb-v{+ppFBc5WZbQo_~LcqF2BGvMx2QQylx1S8a<(lco=RXC+^OtA~RL`0l*W3h&WM zGc+^w+$n{5vx8}-BqasS8?&4jeDP@#E)Y}m6?r-IY`rcxF4K-1nj`w=Ce4Ms_s1O(c~jvOBp0-tcbM`POCq zp{lyNl~l${$R{+~;XSKdZ?8_~mCb?_8jVf?kw73Rpt#vtdYU!hY*T3HUFW2Q$noNc z_O$bC`_G%hsip&Bi^s=B7x&1~_5j?>@$u9(Uftv6f@+!IYwdzJn#3LTl`}7m?6x#@ zbxVGLiNgnAtA}#Mm}B?@Dh6umm(v>_l?n>*H)(=j_HX@ZfHd_1fBATHvf8gHnEXRG zVnQCgfIzO#54sjyZxcVR!&-QJ@eISnz(|p6yCFX>Xb1#id^f;Is+-%9_xy#C5q*;U zB@wNfWyi#5a_H`>gn<<6jGM!Zjx6KQLeH(DI$v!b;0InE5WgsnhO-cfJ0>#9d+mnp z{J3XR=&T&&x!{c%{(>2B090-Ac zW0Sa^@~5nHMW6i6&E@77rS7TTp~dEU1)aj>zy08WgjT$;0rCTbm>rd+_9p>@`r;{pL>)u%GJ2eVLnw!FjEbTFQ0q#Xy5+_Xq?}+H$s>_le<92mF`c!KbwPn-P1jo!?LS z4)h_Pupy47XlYg}1gNU39xvmGXZZgMW|j|1FK=5cy0HL(tu)jLqkNjo#aqVS2PThG zHumm-?<#!Rib-{e&W%BF*s@XA^H4*z3FB!KGxCr+vwTvei4x5Y6`e z69DfKZtFj&TYO<)#c3w%tMU-H&a~seXU!+Zr6O1#YS5Yb-4xFu{%sp~-1E7)kEy9U z4=)G}65B2E9J_us-v;fgsSPi6uY9#0ZajnfZ#?v>F~J+0bCN2{^b?wS&!%B-%+SDl zD&w;+b5*RXZ&wF&sji8q&nE>%EJ%I?#bH4P3tnduH3A~t#D)3QcW$?;Pbf84;Rc0V|MB>1wsd7)xHZgW*yhZNc-QVTY>xZNGoW;goS_(f# zm5neMWUB;HyVRMMx3wVbF#I8Ive{TOiNIRUA+ZL3|5vXe)C>&DFc^Ltt!~cGt^*&U z7Uq<&lM_;G8L)~fz<%VhgjzwLI51O=idq5_wP>#VuXILUu!J|t@;=*Ik9rpv;iwL6c)&L!LK!CuLGR#5EHbPTVQ*HF3K26zceR}Ls75+T=-^U=qeG`+r&NA>0 zrOh%yMd+F6;UhuT1>Bnlx0={a)47J`tMmd#H+{z~+F@eU`}n&)P5yh?zE^#LM{8~} zvL9)SCkD>{{=L@uX2CI*N{!=$)KVwI{|ldkby`*0C8V=2pOKlH)@C)F(p89i(tY~$ ziG^nF?`aCx{#=vrUj;i3EbBB3hNrpg5^)Yl$hCF2rFf#*&iA>u zwkwq!Q>5T%w9{;T9G1j9Ps#&0)v~4!+qA5ba+!c&%}0=ean17C^f4C@J{EBMY!v1l zE) zZo(Hb{(;644jkB!T_tB?R&wN}pOqErIj1@j^g?UM?_78kvor5KIXzuV#(#6pw*0;B z&zBK~d7AdL^x5=uB}PLEW@I??vQyWu*w`F$U9IO7?l#!byo#i`zbGF)z0A#l4NaHx z@yX~|TU%$+uQlHq3I<@TM;^_aI+?!Wp#PYvuBxinvRd6v%`kk)I?1%Wt^dYyI(OtV zcOSOHoo^^Nhu^k`D*PqRY`c9yrs+g5k|VS(JSw!hod2AjTkKrb3Z?|ye3eOnkcHj+7PBMv9}B6^dQk+ZT=_~kBj z0LbfcmOdUCQx|moNaeVE@z3e1K>5kUsD=rPHhUQ6T^|h-Q^^}>Y-)3OS~^hak%?6# zn@b~xuywXM!*lDlSj{-~YPdZd)ZNDsXysl|P@wrj8>aM+_WJK3OTRxU24C>b}s7bS$^ktzS#qx`ld&yCu(U(`!8_*R)Y5j8EHEwSv>DZ0QtkJ8(PJ$CmgZuI~ zskgT`c3fE1(WQ9q(_`DUzvFofpWu&}A3!=mn6XVq?j|PiPK1i!lIz%a`Hv;L;yOMh zGrH93zOx7~CX(t5$>f<^KM}VbqbCCeF0iY!5p%h3ob_{vOIY}=F8~Re`mpKK#4jYU z2DP_uQ4av}{xH?jq&aIKIC%Kw3;Jo4)(q1_!>!msmw&)tzl~mhju%Ru>8C8TyJlD z$1u9FlVHY`d6KmZX4DZeuZ)`v4P~ z$)4zkSeTwaFwBtK5-okC)#R#PgO5gA)flB?6-A4RlF6^l{~|C-@JeZ(qVV3e%M;VzfjXDt{y>}anX7DS+&4A_AXSrDxHM3 zM&brPL}$nznZ*ulP_*4ZLuj)U4RbeVa>j0Xl-ojr>wSB@ik5sFZ+JZj*m=KBwoT=R z3{ID~O+_%~XB+&(A0O9;hAx5*KT9R9zt_H-;3$4(f*$df$P^4f=$N*`G_HNK9`o6) z!Nyw=Y;viYMZf#Q?)f`F2_XdXx~4n{yUD^EB1MM4h#+Txzrh&n=9+UBfS!ORO-JIRaT0E zx_Oh4NL7`fev+Nyv8uBLX%4d(=CSEldJ}_s^!3oPYquGx+el@lt?y=-Jq_Uq_I|kL zvXsPv=TljKk^_9+d5K$`V}=}vhhxq9%!9oj(xS5X{?B?O2!%lZ#O4nTwHfSqnbPiy z(?4^3Tj^{1HP&edzw(;_FN?6;V#@yE-92g*zmu;~z)@Bh RSBrH3fYo$W%Tz2P{s)AJJP-f? literal 0 HcmV?d00001 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5b8f4103..dba2d5db 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -194,6 +194,8 @@ const App = (message, props) => {

    { : null}
    - {(highlight === true && (subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name === "Open Source") ? + {isCloud && (highlight === true && (subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name === "Open Source") ? {subscription.name.includes("Scale") ? diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index f2a1ef99..1c12d7fd 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -444,7 +444,9 @@ const LicencePopup = (props) => { }) : null} - Billing email: {selectedOrganization.org} + {isCloud ? + Billing email: {selectedOrganization.org} + : null}
    - - ); // Handle top bar or something const defaultTop = -2 @@ -1247,7 +1231,7 @@ const Header = (props) => { title={""} placement="left" > -
    +
    Add suborgs diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 829c5ceb..2bf545f4 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -3279,7 +3279,7 @@ const Workflows = (props) => {