diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index e1ae2378..7ce2da6c 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -19,7 +19,6 @@ import urllib.parse import jinja2 import datetime import dateutil - import threading import concurrent.futures @@ -1714,6 +1713,112 @@ class AppBase: ret = requests.post("%s%s" % (self.url, upload_path), files=files, headers=new_headers, verify=False, proxies=self.proxy_config) return file_ids + + def validate_condition(self, sourcevalue, check, destinationvalue): + if check == "=" or check == "==" or check.lower() == "equals": + if str(sourcevalue).lower() == str(destinationvalue).lower(): + return True + elif check == "!=" or check.lower() == "does not equal": + if str(sourcevalue).lower() != str(destinationvalue).lower(): + return True + elif check.lower() == "startswith": + if str(sourcevalue).lower().startswith(str(destinationvalue).lower()): + return True + elif check.lower() == "endswith": + if str(sourcevalue).lower().endswith(str(destinationvalue).lower()): + return True + elif check.lower() == "contains": + if destinationvalue.lower() in sourcevalue.lower(): + return True + + elif check.lower() == "is empty" or check.lower() == "is_empty": + try: + if len(json.loads(sourcevalue)) == 0: + return True + except Exception as e: + self.logger.info(f"[ERROR] Failed to check if empty as list: {e}") + + if len(str(sourcevalue)) == 0: + return True + + elif check.lower() == "contains_any_of": + newvalue = [destinationvalue.lower()] + if ", " in destinationvalue: + newvalue = destinationvalue.split(", ") + elif "," in destinationvalue: + newvalue = destinationvalue.split(",") + + for item in newvalue: + if not item: + continue + + if item.strip() in sourcevalue: + return True + + + # FIXME: This will be buggy if using > and >= operators in the future. + elif check.lower() == "larger than" or check.lower() == "bigger than" or check == ">" or check == ">=": + try: + if str(sourcevalue).isdigit() and str(destinationvalue).isdigit(): + if int(sourcevalue) > int(destinationvalue): + return True + + except AttributeError as e: + self.logger.info("[WARNING] Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + + try: + destinationvalue = len(json.loads(destinationvalue)) + except Exception as e: + self.logger.info(f"[WARNING] Failed to convert destination to list: {e}") + try: + # Check if it's a list in autocast and if so, check the length + if len(json.loads(sourcevalue)) > int(destinationvalue): + return True + except Exception as e: + self.logger.info(f"[WARNING] Failed to check if larger than as list: {e}") + + + # FIXME: This will be buggy if using < and <= operators in the future. + elif check.lower() == "smaller than" or check.lower() == "less than" or check == "<" or check == "<=": + self.logger.info("In smaller than check: %s %s" % (sourcevalue, destinationvalue)) + + try: + if str(sourcevalue).isdigit() and str(destinationvalue).isdigit(): + if int(sourcevalue) < int(destinationvalue): + return True + + except AttributeError as e: + pass + + try: + destinationvalue = len(json.loads(destinationvalue)) + except Exception as e: + self.logger.info(f"[WARNING] Failed to convert destination to list: {e}") + + try: + # Check if it's a list in autocast and if so, check the length + if len(json.loads(sourcevalue)) < int(destinationvalue): + return True + except Exception as e: + self.logger.info(f"[WARNING] Failed to check if smaller than as list: {e}") + + elif check.lower() == "re" or check.lower() == "matches regex": + try: + found = re.search(str(destinationvalue), str(sourcevalue)) + except re.error as e: + return False + except Exception as e: + return False + + if found == None: + return False + + return True + else: + self.logger.error("[DEBUG] Condition: can't handle %s yet. Setting to true" % check) + + return False + #async def execute_action(self, action): def execute_action(self, action): @@ -2597,7 +2702,6 @@ class AppBase: return returndata, is_loop - # Sending self as it's not a normal function def parse_liquid(template, self): @@ -3084,110 +3188,7 @@ class AppBase: return "", parameter["value"], is_loop - def run_validation(sourcevalue, check, destinationvalue): - #self.logger.info("[DEBUG] Checking %s '%s' %s" % (sourcevalue, check, destinationvalue)) - - if check == "=" or check.lower() == "equals": - if str(sourcevalue).lower() == str(destinationvalue).lower(): - return True - elif check == "!=" or check.lower() == "does not equal": - if str(sourcevalue).lower() != str(destinationvalue).lower(): - return True - elif check.lower() == "startswith": - if str(sourcevalue).lower().startswith(str(destinationvalue).lower()): - return True - elif check.lower() == "endswith": - if str(sourcevalue).lower().endswith(str(destinationvalue).lower()): - return True - elif check.lower() == "contains": - if destinationvalue.lower() in sourcevalue.lower(): - return True - - elif check.lower() == "is empty" or check.lower() == "is_empty": - try: - if len(json.loads(sourcevalue)) == 0: - return True - except Exception as e: - self.logger.info(f"[ERROR] Failed to check if empty as list: {e}") - - if len(str(sourcevalue)) == 0: - return True - - elif check.lower() == "contains_any_of": - newvalue = [destinationvalue.lower()] - if "," in destinationvalue: - newvalue = destinationvalue.split(",") - elif ", " in destinationvalue: - newvalue = destinationvalue.split(", ") - - for item in newvalue: - if not item: - continue - - if item.strip() in sourcevalue: - return True - - elif check.lower() == "larger than" or check.lower() == "bigger than": - try: - if str(sourcevalue).isdigit() and str(destinationvalue).isdigit(): - if int(sourcevalue) > int(destinationvalue): - return True - - except AttributeError as e: - self.logger.info("[WARNING] Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) - - try: - destinationvalue = len(json.loads(destinationvalue)) - except Exception as e: - self.logger.info(f"[WARNING] Failed to convert destination to list: {e}") - try: - # Check if it's a list in autocast and if so, check the length - if len(json.loads(sourcevalue)) > int(destinationvalue): - return True - except Exception as e: - self.logger.info(f"[WARNING] Failed to check if larger than as list: {e}") - - - elif check.lower() == "smaller than" or check.lower() == "less than": - self.logger.info("In smaller than check: %s %s" % (sourcevalue, destinationvalue)) - - try: - if str(sourcevalue).isdigit() and str(destinationvalue).isdigit(): - if int(sourcevalue) < int(destinationvalue): - return True - - except AttributeError as e: - pass - - try: - destinationvalue = len(json.loads(destinationvalue)) - except Exception as e: - self.logger.info(f"[WARNING] Failed to convert destination to list: {e}") - - try: - # Check if it's a list in autocast and if so, check the length - if len(json.loads(sourcevalue)) < int(destinationvalue): - return True - except Exception as e: - self.logger.info(f"[WARNING] Failed to check if smaller than as list: {e}") - - elif check.lower() == "re" or check.lower() == "matches regex": - try: - found = re.search(str(destinationvalue), str(sourcevalue)) - except re.error as e: - return False - except Exception as e: - return False - - if found == None: - return False - - return True - else: - self.logger.error("[DEBUG] Condition: can't handle %s yet. Setting to true" % check) - - return False - + def check_branch_conditions(action, fullexecution, self): # relevantbranches = workflow.branches where destination = action try: @@ -3287,8 +3288,7 @@ class AppBase: self.logger.error("[ERROR] Skipping '%s' -> %s -> '%s' because %s is invalid." % (sourcevalue, condition["condition"]["value"], destinationvalue, condition["condition"]["value"])) continue - # Configuration = negated because of WorkflowAppActionParam.. - validation = run_validation(sourcevalue, condition["condition"]["value"], destinationvalue) + validation = self.validate_condition(sourcevalue, condition["condition"]["value"], destinationvalue) try: if condition["condition"]["configuration"]: validation = not validation diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index cd36ec7c..8951ecbe 100644 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -6,3 +6,4 @@ flask[async]==2.0.2 waitress==2.1.0 #flask==1.1.2 python-dateutil==2.8.1 +PyJWT==2.9.0 diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 630a9d97..25b746f6 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -20,7 +20,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.63 + github.com/shuffle/shuffle-shared v0.6.77 golang.org/x/crypto v0.22.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.63.2 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 7d974d2a..3a682615 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -6,16 +6,7 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -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/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 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= @@ -24,29 +15,17 @@ cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKF 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/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.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= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -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/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= 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= @@ -65,7 +44,6 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 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/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= @@ -115,7 +93,6 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK 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/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= @@ -139,7 +116,6 @@ 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.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= @@ -148,46 +124,32 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM 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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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/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.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/frikky/schemaless v0.0.13 h1:ARiN9V7wr2VZXAr9JK5wvTbyPgpGrgeiL1VhR5MlgaQ= github.com/frikky/schemaless v0.0.13/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= 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/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= 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.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/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 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/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= @@ -200,7 +162,6 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -215,26 +176,19 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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/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= @@ -245,12 +199,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a 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= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -265,21 +215,11 @@ 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= 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.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= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -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= @@ -302,9 +242,7 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= 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= 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.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -339,7 +277,6 @@ 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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= 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= @@ -359,46 +296,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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 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.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= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -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/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= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -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/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= @@ -414,16 +313,13 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK 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= 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/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/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= @@ -436,42 +332,13 @@ github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA= github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 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/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/shuffle/shuffle-shared v0.6.26 h1:UZ4o3s+GPULLv/gy31SdhUw1ttvF+f1FfP18bxD7+Bw= -github.com/shuffle/shuffle-shared v0.6.26/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= -github.com/shuffle/shuffle-shared v0.6.27 h1:q4qZD6bGZFIvZ5Y10unGr3N3rZ7OryWyvvaGgANZJZU= -github.com/shuffle/shuffle-shared v0.6.27/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= -github.com/shuffle/shuffle-shared v0.6.31 h1:MK1SW1pwjIP7hznq+mMlTPM1R3LIOfi1/bUL5xFSg/8= -github.com/shuffle/shuffle-shared v0.6.31/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= -github.com/shuffle/shuffle-shared v0.6.46 h1:v/IXc+4V8DCWflGKGgaI37uuGnsylMjqKRylwNoXVrA= -github.com/shuffle/shuffle-shared v0.6.46/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4= -github.com/shuffle/shuffle-shared v0.6.47 h1:EOalfIIBX97uGkgRRPsdUApts6yCCcXf1iSphm6UoE0= -github.com/shuffle/shuffle-shared v0.6.47/go.mod h1:XVIcR2/GyIk+6qmlpOG3NvIao6kCOma36EdMym4AeSY= -github.com/shuffle/shuffle-shared v0.6.50 h1:MBeGAiBNkw9Eg+3YTJIlBOuskWntGvT0uefFUYOBhbY= -github.com/shuffle/shuffle-shared v0.6.50/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= -github.com/shuffle/shuffle-shared v0.6.59 h1:Pjvq4Lz6OAjA+hycwzUnm+f/pUiR5apR9Cz5f0fkAVs= -github.com/shuffle/shuffle-shared v0.6.59/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= -github.com/shuffle/shuffle-shared v0.6.60 h1:8OaiNxNpzJmIbYIcXI3TIYZVrPJ1sSCa+u7itMUMmxs= -github.com/shuffle/shuffle-shared v0.6.60/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= -github.com/shuffle/shuffle-shared v0.6.61 h1:+9CCLeZLiAVDgNRTkZxnIgz+FZ7UrEHez2BAGPS/axc= -github.com/shuffle/shuffle-shared v0.6.61/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= -github.com/shuffle/shuffle-shared v0.6.62 h1:NWVjVbnNpm6osDHQS2NGqwAgWk1Fk3s5RKUO3P5n4js= -github.com/shuffle/shuffle-shared v0.6.62/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= -github.com/shuffle/shuffle-shared v0.6.63 h1:eNQMpVhe/mAMxl61W9Wj6/Z4PrtPeEnbjvMtDdT1mqw= -github.com/shuffle/shuffle-shared v0.6.63/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= +github.com/shuffle/shuffle-shared v0.6.74 h1:os3BDSFZnl4U8ZgsTAY8IsTDADcMXhbc1rS9UMa0BIY= +github.com/shuffle/shuffle-shared v0.6.74/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= 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/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= @@ -489,30 +356,23 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -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.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= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 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/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.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= @@ -537,7 +397,6 @@ go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0 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= 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= @@ -547,13 +406,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 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/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= @@ -563,9 +417,7 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -577,31 +429,19 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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/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= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= 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/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/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= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -610,48 +450,22 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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-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= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -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= -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-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= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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/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= @@ -659,10 +473,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -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.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= @@ -671,18 +481,12 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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= 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= @@ -692,72 +496,33 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/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-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= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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-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= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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-20211019181941-9d821ace8654/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= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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-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= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 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/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= @@ -765,23 +530,16 @@ 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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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= 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/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= 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.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= @@ -805,47 +563,15 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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.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/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/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= @@ -862,18 +588,6 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -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/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 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= @@ -881,8 +595,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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/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= @@ -897,31 +609,8 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -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-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= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -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-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-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= @@ -936,15 +625,7 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -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.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.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= @@ -955,11 +636,9 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 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.28.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= @@ -968,7 +647,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 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= -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 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= @@ -977,11 +655,9 @@ gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOA 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 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= 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= gopkg.in/yaml.v2 v2.2.2/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= @@ -997,18 +673,10 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= -k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI= k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI= -k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= -k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg= k8s.io/apimachinery v0.30.2/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/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50= k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= @@ -1018,7 +686,6 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVh 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= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index acfde3c4..7ce1309c 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1131,9 +1131,9 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) { } // No childorg setup, only parent org - if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 { - continue - } + // if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 { + // continue + // } // Should run calculations if len(org.SSOConfig.OpenIdAuthorization) > 0 { @@ -5213,6 +5213,7 @@ func initHandlers() { //r.HandleFunc("/api/v1/orgs/", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleGetOrg).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleEditOrg).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/orgs/{orgid}/forms", shuffle.HandleGetOrgForms).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/create_sub_org", shuffle.HandleCreateSubOrg).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org @@ -5270,7 +5271,7 @@ func initHandlers() { // This is weird. r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS") - r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS") + //r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS") // Introduced in 0.9.21 to handle notifications for e.g. failed Workflow diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index b4e4d0db..7b8008ce 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -571,14 +571,20 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { //return } + if len(actionResult.ExecutionId) == 0 { + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Provide execution_id and authorization"}`))) + return + } + ctx := context.Background() workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) - if err != nil { + if err != nil || workflowExecution.ExecutionId != actionResult.ExecutionId { if len(actionResult.ExecutionId) > 0 { log.Printf("[WARNING][%s] Failed getting execution (streamresult): %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 } @@ -637,9 +643,26 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { } } + if workflowExecution.Workflow.Sharing == "form" { + newWorkflow := shuffle.Workflow{ + Name: workflowExecution.Workflow.Name, + ID: workflowExecution.Workflow.ID, + Owner: workflowExecution.Workflow.Owner, + OrgId: workflowExecution.Workflow.OrgId, + + Sharing: workflowExecution.Workflow.Sharing, + Description: workflowExecution.Workflow.Description, + InputQuestions: workflowExecution.Workflow.InputQuestions, + InputMarkdown: workflowExecution.Workflow.InputMarkdown, + } + + workflowExecution.Results = []shuffle.ActionResult{} + workflowExecution.Workflow = newWorkflow + } + 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 } @@ -936,6 +959,27 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { } } + // Look for Child workflows and delete them + if workflow.ParentWorkflowId == "" { + log.Printf("[DEBUG] Looking for child workflows for workflow %s to delete. User %s (%s) in org %s (%s)", workflow.ID, user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id) + + childWorkflows, err := shuffle.ListChildWorkflows(ctx, workflow.ID) + if err != nil { + log.Printf("[ERROR] Failed to list child workflows: %s", err) + } else { + log.Printf("\n\n[DEBUG] Found %d child workflows for workflow %s\n\n", len(childWorkflows), workflow.ID) + + // Find cookies and append them to request.Header to replicate current request as closely as possible + for _, childWorkflow := range childWorkflows { + if childWorkflow.ID == workflow.ID { + continue + } + + go shuffle.SendDeleteWorkflowRequest(childWorkflow, request) + } + } + } + // Clean up triggers and executions for _, item := range workflow.Triggers { if item.TriggerType == "SCHEDULE" && item.Status != "uninitialized" { @@ -2398,7 +2442,7 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { return } - workflow.Schedules = append(workflow.Schedules, schedule) + //workflow.Schedules = append(workflow.Schedules, schedule) err = shuffle.SetWorkflow(ctx, *workflow, workflow.ID) if err != nil { log.Printf("Failed setting workflow for schedule: %s", err) @@ -3366,7 +3410,15 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) { } ctx := context.Background() - workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, fileId, body) + + runValidationAction := false + query := request.URL.Query() + validation, ok := query["validation"] + if ok && validation[0] == "true" { + runValidationAction = true + } + + workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, fileId, body, runValidationAction) if err != nil { log.Printf("[INFO] Failed workflowrequest POST read: %s", err) resp.WriteHeader(401) diff --git a/docker-compose.yml b/docker-compose.yml index 65a60ce4..64155cc9 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,7 +62,6 @@ services: image: opensearchproject/opensearch:2.14.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 +72,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 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index fad87105..807ca56d 100755 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -13,7 +13,7 @@ COPY package.json /usr/src/app/package.json #RUN yarn config set "strict-ssl" false -g #RUN yarn install --network-timeout 1000000 -RUN npm install --legacy-peer-deps +RUN npm install --timeout=60000 --legacy-peer-deps # copy only required files to not trigger rebuilding every time COPY ./certs /usr/src/app/certs/ diff --git a/frontend/README.md b/frontend/README.md index 571c5625..92b4ab6b 100755 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,3 +1,5 @@ +## Lalits frontend magic + ## Localhost Certificate info: diff --git a/frontend/package.json b/frontend/package.json index 635c079f..41c0790d 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,10 +1,9 @@ { "name": "shuffler", "homepage": "https://shuffler.io", - "version": "1.4.0", + "version": "2.0.0", "private": true, "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.18.6", "@codemirror/commands": "^6.2.4", "@codemirror/lang-python": "^6.1.3", "@emotion/react": "^11.11.1", @@ -13,7 +12,7 @@ "@metamask/detect-provider": "^1.2.0", "@mui/icons-material": "^5.14.0", "@mui/material": "^5.14.0", - "@mui/styles": "^5.14.0", + "@mui/styles": "^6.1.4", "@mui/x-data-grid": "^5.17.11", "@mui/x-date-pickers": "^6.11.1", "@types/algoliasearch": "^3.34.11", @@ -21,7 +20,6 @@ "@uiw/codemirror-theme-vscode": "^4.21.20", "@uiw/codemirror-themes": "^4.21.9", "@uiw/react-codemirror": "^4.21.21", - "@use-it/interval": "^0.1.3", "algoliasearch": "^4.8.3", "class-transformer": "^0.2.0", "codemirror": "^6.0.1", @@ -49,7 +47,6 @@ "i18next-localstorage-backend": "^4.1.0", "i18next-xhr-backend": "^3.2.2", "import": "0.0.6", - "interweave": "^11.2.0", "is-plain-obj": "^4.1.0", "json-bigint": "^1.0.0", "match-sorter": "^6.3.1", @@ -58,12 +55,12 @@ "moment": "~2.29.4", "mui-chips-input": "^2.1.3", "mui-nested-menu": "^3.2.1", - "react": "^18.2.0", + "react": "^18.3.1", "react-ace": "^10.1.0", "react-alice-carousel": "^2.6.4", "react-avatar-editor": "^11.1.0", "react-beforeunload": "^2.2.1", - "react-chartjs-2": "^2.11.1", + "react-chartjs-2": "^5.2.0", "react-cookie": "^4.0.1", "react-cytoscapejs": "^2.0.0", "react-device-detect": "^2.2.3", @@ -73,21 +70,18 @@ "react-dropzone": "^14.2.3", "react-ga4": "^2.0.0", "react-hotkeys": "^2.0.0", - "react-i18next": "^13.1.2", "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", - "react-json-view": "^1.21.3", "react-json-view-ssr": "^1.19.1", "react-markdown": "^8.0.7", - "react-markdown-github": "^3.3.1", "react-powerhooks": "^0.0.7", "react-router": "^6.14.1", "react-router-dom": "^6.14.1", "react-scripts": "^5.0.1", "react-social-icons": "^5.15.0", - "react-stripe-elements": "^6.1.2", "react-toastify": "^9.1.3", "reaviz": "^14.9.7", + "rehype-raw": "^7.0.0", "remark-gfm": "^3.0.1", "remark-html": "^16.0.1", "remark-images": "^4.0.0", @@ -134,7 +128,6 @@ "babel-preset-es2015": "^6.24.1", "postcss": "^8.4.38", "promise-window": "^1.2.1", - "react-hot-loader": "^4.13.0", "webpack-cli": "^5.1.4" } } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 62c76ae1..a2aec50c 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -48,6 +48,8 @@ import 'react-toastify/dist/ReactToastify.css'; import Drift from "react-driftjs"; +import { AppContext } from './context/contextApi.jsx'; + // Production - backend proxy forwarding in nginx var globalUrl = window.location.origin; @@ -612,26 +614,28 @@ const App = (message, props) => { return ( - - - - - {includedData} - - - - + + + + + + {includedData} + + + + + ); }; diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 093c9550..87fe533e 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -1129,7 +1129,7 @@ const AppFramework = (props) => { }, [newSelectedApp]) - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); const imgSize = 50; var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index 635302d6..5bbb5ff0 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -63,7 +63,7 @@ const AppSelection = props => { document.title = "Choose your apps" const ref = useRef() let navigate = useNavigate(); - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); useEffect(() => { if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index e4f1782f..b5871f2c 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -24,7 +24,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52 const Appsearch = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs //const theme = useTheme(); diff --git a/frontend/src/components/AuthenticationNormal.jsx b/frontend/src/components/AuthenticationNormal.jsx index 271dc59a..de0f5cfe 100644 --- a/frontend/src/components/AuthenticationNormal.jsx +++ b/frontend/src/components/AuthenticationNormal.jsx @@ -344,12 +344,13 @@ const AuthenticationData = (props) => { onClick={() => { setAuthenticationModalOpen(false); }} - color="primary" + color="secondary" > Cancel ) : ( @@ -1315,7 +1316,7 @@ const Billing = (props) => { width: 340, backgroundColor: hovered ? "#232427" : theme.palette.platformColor, borderRadius: theme.palette.borderRadius * 2, - border: "1px solid #f85a3e", + border: "1px solid rgba(255,255,255,0.3)", marginRight: 10, marginTop: 15, }} @@ -1365,7 +1366,7 @@ const Billing = (props) => { + setOpenPrivateTraining(false)} fullWidth @@ -1818,6 +1821,7 @@ const Billing = (props) => { // Update currentIndex based on remaining elements const findCurrentIndex = newAlertThresholds.some(threshold => threshold.Email_send === false); setCurrentIndex(findCurrentIndex ? newAlertThresholds.findIndex(threshold => threshold.Email_send === false) : - 1); + toast.info("Alert Threshold deleted successfully. Don't forget to save your changes."); }; const HandleEditOrgForAlertThreshold = (orgId) => { @@ -1997,15 +2001,6 @@ const Billing = (props) => { /> : null} - {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : - : null} - - - {isCloud && selectedOrganization.subscriptions !== undefined && @@ -2261,7 +2256,29 @@ const Billing = (props) => { ) : null*/} -
+ {!isChildOrg && isCloud && ( +
+ + Professional Services + + + We offer priority support through consultations and training to help you make the most of our product. If you have any questions, please reach out to us at support@shuffler.io. + +
+ {billingInfo.subscription !== undefined && billingInfo.subscription !== null ? ( + isChildOrg ? null : ( + + ) + ) : null} + +
+
+ )} +
{ : " " + 0 + " "} app runs. - + + Please note: Once your app runs reach the set alert threshold, all admins in the organization will receive an email notification. +
{alertThresholds.map((threshold, index) => (
@@ -2354,6 +2373,7 @@ const Billing = (props) => { margin="normal" variant="outlined" /> + {alertThresholds[index].Email_send === true && } { alertThresholds.length > 1 && ( @@ -2362,18 +2382,24 @@ const Billing = (props) => { disableElevation sx={{ padding: 0, - color: 'red', '&:hover': { backgroundColor: 'transparent', }, }} - onClick={() => { handleDeleteAlertThreshold(index) }} + onClick={() => { setDeleteAlertVerification(true) }} > - + ) } + setDeleteAlertVerification(false)} sx={{ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)', }, }}> + Are you sure you want to delete this threshold? + + + + +
))}
diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index bd653e7d..59d541ba 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import theme from "../theme.jsx"; import { toast } from 'react-toastify'; -import ReactJson from "react-json-view"; +import ReactJson from "react-json-view-ssr"; import { Typography, diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index f7bbb7ed..d200b7a3 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; import { toast } from 'react-toastify'; import theme from "../theme.jsx"; +import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx" import { InputAdornment, @@ -79,7 +80,7 @@ const ConfigureWorkflow = (props) => { useEffect(() => { if (requiredActions.length === 0) { if (setConfigurationFinished !== undefined) { - setConfigurationFinished(true) + setConfigurationFinished(true) } } }, [requiredActions]) @@ -141,17 +142,18 @@ const ConfigureWorkflow = (props) => { // Where is this from? if (workflow === undefined || workflow === null || workflow.id === undefined) { - return null; + //console.log("Workflow is undefined or null: ", workflow) + return null } if (apps === undefined || apps === null) { console.log("Apps is undefined or null: ", apps) - return null; + return null } if (appAuthentication === undefined || appAuthentication === null) { console.log("App authentication is undefined or null: ", appAuthentication) - return null; + return null } const getApp = (actionId, appId) => { @@ -310,7 +312,7 @@ const ConfigureWorkflow = (props) => { } } - if (action.authentication_id === "" && app.authentication.required === true && action.parameters !== undefined && action.parameters !== null) { + if (action?.authentication_id === "" && app?.authentication?.required === true && action.parameters !== undefined && action.parameters !== null) { // Check if configuration is filled or not var filled = true; for (let [key,keyval] in Object.entries(action.parameters)) { @@ -1386,9 +1388,23 @@ const ConfigureWorkflow = (props) => { : null } +
+ + {/* + +
+ */} + {requiredActions.length > 0 ? ( - + Please configure the following steps to help us complete your workflow. This can also be done later. diff --git a/frontend/src/components/DashboardBarchart.jsx b/frontend/src/components/DashboardBarchart.jsx new file mode 100644 index 00000000..58f20708 --- /dev/null +++ b/frontend/src/components/DashboardBarchart.jsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { Bar } from 'react-chartjs-2'; +import { toast } from "react-toastify"; + +export const LoadStats = (globalUrl, cachekey) => { + if (globalUrl === undefined) { + console.log("Error: Global URL is undefined") + return + } + + if (cachekey === undefined) { + console.log("Error: Cachekey is undefined") + return + } + + var basedata = { + "key": cachekey, + "total": 0, + "available_keys": [], + "labels": [], + "datasets": [ + { + "label": "", + "data": [], + "backgroundColor": [], + "barThickness": 15, + } + ] + } + + //const url = `${globalUrl}/api/v1/stats/app_executions_test2` + //cachekey = cachekey.replace(" ", "_", -1) + const url = `${globalUrl}/api/v1/stats/${cachekey}` + return fetch(url, { + method: "GET", + credentials: "include", + }) + .then((resp) => { + return resp.json() + }).then((respJson) => { + const selectedIndex = 0 + + if (respJson.success === true) { + for (let entryKey in respJson.entries) { + const entry = respJson.entries[entryKey] + basedata.labels.push(entry.date) + + basedata.datasets[0].data.push(entry.value) + basedata.datasets[0].backgroundColor.push(entry.value > 0 ? "rgba(255,255,255,0.4)" : "red") + } + + basedata.available_keys = respJson.available_keys + basedata.total = respJson.total + + return basedata + } else { + console.log("Failed to get stats") + return basedata + } + }) + .catch((err) => { + toast("Failed to get stats") + return basedata + }) +} + +const DashboardBarchart = (props) => { + const { timelineData, title, height, } = props; + var inputHeight = 15 + if (height !== undefined && height !== null) { + inputHeight = height + } + + const barOptions = { + plugins: { + tooltip: { + enabled: true, // Ensure tooltips are enabled + }, + }, + tooltips: { + mode: 'index', + intersect: false, + }, + legend: { + display: false + }, + layout: { + padding: { + top: 0, // Adjust the top padding as needed + bottom: -10, // Adjust the bottom padding as needed + left: 0, // Adjust the left padding as needed + right: 0, // Adjust the right padding as needed + }, + }, + scales: { + y: { + beginAtZero: false, + }, + yAxes: [{ + ticks: { + display: false + }, + beginAtZero: false, + }], + xAxes: [{ + ticks: { + display: false + }, + beginAtZero: false, + }] + }, + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + const label = data.labels[tooltipItem.index] + return label.split('\n')[0] + }, + afterLabel: function (tooltipItem, data) { + const amount = tooltipItem.value === undefined || tooltipItem.value === null ? 0 : tooltipItem.value + return `Amount: ${amount}` + }, + title: function () { + return title === undefined ? '' : title + } + } + } + } + + return ( + { + if (elements && elements.length > 0) { + //toast("Click event") + console.log("Clicked: ", elements) + } + }} + /> + ) +} + +export default DashboardBarchart; diff --git a/frontend/src/components/Detection.jsx b/frontend/src/components/Detection.jsx new file mode 100644 index 00000000..c09d0c7a --- /dev/null +++ b/frontend/src/components/Detection.jsx @@ -0,0 +1,210 @@ +import React, { useState } from "react"; +import { + Container, + Box, + TextField, + Switch, + Typography, + Button, + CircularProgress, + Paper, +} from "@mui/material"; + +import { toast } from "react-toastify"; +import theme from '../theme.jsx'; +import DetectionRuleCard from "../components/DetectionRuleCard.jsx"; + +const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isTenzirActive) => { + + if (!isTenzirActive) { + toast("connect to siem first for global enable/disable to work"); + return; + } + + const action = folderDisabled ? "enable_folder" : "disable_folder"; + const url = `${globalUrl}/api/v1/detections/${action}`; + + fetch(url, { + method: "PUT", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === true) { + if (action === "enable_folder") setFolderDisabled(false); + else setFolderDisabled(true); + } else { + //toast(`failed to disable rule`); + } + }) + ) + .catch((error) => { + console.log(`Error in ${action} the rule: `, error); + toast(`An error occurred while ${action} the rule`); + }); +}; + +const Detection = (props) => { + const { globalUrl, ruleInfo, folderDisabled, setFolderDisabled, isTenzirActive } = props; + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(false); + + const handleConnectClick = () => { + if (!isTenzirActive) { + setLoading(true); + const url = `${globalUrl}/api/v1/detections/siem/connect`; + + fetch(url, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === true) { + setTimeout(() => { + setLoading(false); + window.location.reload(); + }, 15000); + } else { + setLoading(false); + toast("Failed to connect to SIEM"); + } + }) + ) + .catch((error) => { + setLoading(false); + console.log(`Error in connecting to SIEM: `, error); + toast("An error occurred while connecting to SIEM"); + }); + } else { + console.log("Already connected to SIEM"); + } + }; + + const filteredRules = ruleInfo?.filter((rule) => + rule.title.toLowerCase().includes(searchQuery.toLowerCase()) || + rule.description.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( + + + + + Sigma Detection Rules + + + + + + setSearchQuery(e.target.value)} + /> + {/* + { + uploadFiles(event.target.files); + }} + /> */} + + + + Global disable/enable + + + handleDirectoryChange(folderDisabled, setFolderDisabled, globalUrl, isTenzirActive) + } + disabled={!isTenzirActive} + /> + + + + {filteredRules?.length > 0 ? + filteredRules.map((card) => { + console.log("RULE CARD: ", card); + + return ( + + ) + }) + : null } + + + + ); +}; + +export default Detection; diff --git a/frontend/src/components/DetectionExplorer.jsx b/frontend/src/components/DetectionExplorer.jsx new file mode 100644 index 00000000..c81bee26 --- /dev/null +++ b/frontend/src/components/DetectionExplorer.jsx @@ -0,0 +1,399 @@ +import React, { useState, useEffect, } from "react"; +import { + Container, + Box, + TextField, + Switch, + Typography, + Button, + CircularProgress, + Paper, + Divider, + IconButton, +} from "@mui/material"; + +import { + OpenInNew as OpenInNewIcon, +} from "@mui/icons-material" + +import { toast } from "react-toastify"; +import theme from '../theme.jsx'; +import DetectionRuleCard from "../components/DetectionRuleCard.jsx"; +import { + green, + red, + grey, +} from "../views/AngularWorkflow.jsx" + +import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx" + +const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isDetectionActive) => { + if (!isDetectionActive) { + toast.warn("Connect to siem first for global enable/disable to work"); + return; + } + + const action = folderDisabled ? "enable_folder" : "disable_folder"; + const url = `${globalUrl}/api/v1/detections/${action}`; + + fetch(url, { + method: "PUT", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === true) { + if (action === "enable_folder") setFolderDisabled(false); + else setFolderDisabled(true); + } else { + //toast(`failed to disable rule`); + } + }) + ) + .catch((error) => { + console.log(`Error in ${action} the rule: `, error); + toast(`An error occurred while ${action} the rule`); + }); +}; + +const DetectionExplorer = (props) => { + const { globalUrl, userdata, ruleInfo, folderDisabled, setFolderDisabled, detectionInfo, importDetectionFromUrl, rulesLoading, isDetectionActive, setIsDetectionActive, ruleMapping, setRuleMapping, } = props; + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(false); + + const [workflow, setWorkflow] = useState({}) + const [detectionWorkflowId, setDetectionWorkflowId] = useState("") + const [isDetectionValid, setIsDetectionValid] = useState(false) + const [availableDetection, setAvailableDetection] = React.useState([]); + + const loadUsecases = () => { + const url = `${globalUrl}/api/v1/workflows/usecases` + fetch(url, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson.success === false) { + return + } + + if (responseJson.length == 0) { + return + } + + for (var usecaseCategory in responseJson) { + const category = responseJson[usecaseCategory] + if (!category.name.toLowerCase().includes("respond") && !category.name.toLowerCase().includes("response")) { + continue + } + + setAvailableDetection(category.list) + break + } + }) + ) + .catch((error) => { + console.log(`Error in loading usecases: `, error); + //toast(`An error occurred while loading usecases`); + }) + } + + const loadWorkflow = (workflowId) => { + const url = `${globalUrl}/api/v1/workflows/${workflowId}` + fetch(url, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson.id === workflowId) { + setWorkflow(responseJson) + } else { + toast(`Failed to load workflow ${workflowId}`); + } + })) + .catch((error) => { + console.log(`Error in loading workflow ${workflowId}: `, error); + toast(`An error occurred while loading workflow ${workflowId}`); + }) + } + + const handleConnectClick = () => { + if (detectionWorkflowId !== "") { + // FIXME: Show the Usecase UI for how to fix the workflow(s) + // Instead loading full workflow and showing it directly? Hmm + //toast.warn("Please reload the UI to load the detection status") + return + } + + if (isDetectionActive) { + return + } + + if (detectionInfo.category === undefined || detectionInfo.category === null) { + toast.warn("Detection category not found. Please try again or contact support@shuffler.io if you think this is a bug.") + return + } + + setLoading(true); + const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`; + + fetch(url, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === true) { + setLoading(false) + + if (setIsDetectionActive !== undefined) { + setIsDetectionActive(true) + } + + if (responseJson.workflow_id !== undefined && responseJson.workflow_id !== null) { + setDetectionWorkflowId(responseJson.workflow_id) + + loadWorkflow(responseJson.workflow_id) + } + + if (responseJson.workflow_valid !== undefined && responseJson.workflow_valid !== null) { + setIsDetectionValid(responseJson.workflow_valid) + } + } else { + if (responseJson.reason !== undefined && responseJson.reason !== null) { + toast(responseJson.reason) + } else { + toast(`Failed to connect to ${detectionInfo?.category}`); + } + + if (responseJson.action !== undefined && responseJson.actio !== null && responseJson.action.length > 0) { + //if (responseJson.action === "environment_create") { + // navigate("/admin?tab=environments") + //} + } + + setLoading(false); + } + }) + ) + .catch((error) => { + setLoading(false); + console.log(`Error in connecting to ${detectionInfo?.category}: `, error); + toast(`An error occurred while connecting to ${detectionInfo?.category}`); + }); + } + + + useEffect(() => { + loadUsecases() + }, []) + + useEffect(() => { + handleConnectClick() + }, [detectionInfo]) + + const filteredRules = ruleInfo === "default" ? [] : ruleInfo?.filter((rule) => + rule.title.toLowerCase().includes(searchQuery.toLowerCase()) || + rule.description.toLowerCase().includes(searchQuery.toLowerCase()) + ) + + return ( + + + + + {detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rules)`} + + + {workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ? +
+
+ +
+ + { + window.open(`/workflows/${workflow.id}`, "_blank") + }} + > + + +
+ + : + + } +
+ {filteredRules?.length > 0 ? + + + setSearchQuery(e.target.value)} + /> + + + + Global disable/enable + + + handleDirectoryChange(folderDisabled, setFolderDisabled, globalUrl, isDetectionActive) + } + /> + + + : null} + + + + {filteredRules?.length > 0 ? + + ruleMapping !== undefined && ruleMapping !== null && ruleMapping.value !== undefined && ruleMapping.value !== null ? + filteredRules.map((rule, index) => { + return ( +
+ +
+ ) + }) + : null + : +
+ {rulesLoading === true ? + +
+ + Downloading rules, please wait... +
+
+ : +
+ + No rules loaded yet + + +
+ } +
+ } +
+
+
+ ); +}; + +export default DetectionExplorer; diff --git a/frontend/src/components/DetectionRuleCard.jsx b/frontend/src/components/DetectionRuleCard.jsx index f5a30f67..4d60b152 100644 --- a/frontend/src/components/DetectionRuleCard.jsx +++ b/frontend/src/components/DetectionRuleCard.jsx @@ -1,22 +1,57 @@ -import React from "react"; +import React, { useState, useEffect, } from "react"; import { Card, CardContent, IconButton, Typography, Switch, + Tooltip, + Select, + MenuItem, + Divider, + FormLabel, } from "@mui/material"; -import EditIcon from "@mui/icons-material/Edit"; + +import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; +import { + Edit as EditIcon, +} from "@mui/icons-material"; import { toast } from "react-toastify"; import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import theme from '../theme.jsx'; -const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, isTenzirActive, ...otherProps }) => { + +const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, isTenzirActive, availableDetection, ruleMapping, setRuleMapping, ...otherProps }) => { const [openCodeEditor, setOpenCodeEditor] = React.useState(false); const [fileData, setFileData] = React.useState(""); const [isEnabled, setIsEnabled] = React.useState(otherProps.is_enabled); + const [filteredBarchart, setFilteredBarchart] = React.useState(null) + + const [responseValue, setResponseValue] = React.useState("No response action") const isCloud = ["localhost:3002", "shuffler.io"].includes(window.location.host); + console.log("Rulemapping: ", ruleMapping) + useEffect(() => { + + //const url = `${globalUrl}/api/v1/stats/app_executions_test2` + //const resp = LoadStats(globalUrl, ruleName) + //const resp = LoadStats(globalUrl, "app_executions_test2") + const resp = LoadStats(globalUrl, "app_executions_cloud") + resp.then((data) => { + if (data === undefined) { + setFilteredBarchart([]) + } else { + setFilteredBarchart(data) + } + }) + + if (ruleMapping !== undefined && ruleMapping !== null && ruleMapping.value !== undefined && ruleMapping.value !== null) { + console.log("FIX MAPPING FROM ruleMapping.value: ", ruleMapping) + } + }, []) + + console.log("Response Value: ", responseValue) + const handleSwitchChange = (event) => { if (folderDisabled) { toast.warn("Enable the directory to enable individual rules"); @@ -32,7 +67,8 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i toggleRule(file_id, !newIsEnabled, globalUrl, () => { setIsEnabled(newIsEnabled); }) - }; + } + const UpdateText = (text) => { fetch(`${globalUrl}/api/v1/files/${file_id}/edit`, { @@ -64,32 +100,121 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i - +
- {ruleName} + {ruleName.replaceAll("_", " ")} ({filteredBarchart === null || filteredBarchart.total === undefined ? 0 : filteredBarchart.total})
- openEditBar(file_id, setOpenCodeEditor, setFileData, globalUrl)}> - - - + + + + + + openEditBar(file_id, setOpenCodeEditor, setFileData, globalUrl)}> + + + + + +
+ +
+ {filteredBarchart === null ? null : + + } +
+ + {/* {description} + */} { - const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, } = props + const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, } = props const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove @@ -79,15 +81,30 @@ 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 [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : []) + const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : []) const [inputMarkdown, setInputMarkdown] = React.useState(workflow.input_markdown !== undefined && workflow.input_markdown !== null ? workflow.input_markdown : "") - const [outputMarkdown, setOutputMarkdown] = React.useState(workflow.output_markdown !== undefined && workflow.output_markdown !== null ? workflow.output_markdown : "") + const [scrollDone, setScrollDone] = React.useState(false) + const [selectedYieldActions, setSelectedYieldActions] = React.useState(workflow.output_yields !== undefined && workflow.output_yields !== null ? JSON.parse(JSON.stringify(workflow.output_yields)) : []) const classes = useStyles(); + if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) { + setTimeout(() => { + const foundScroll = document.getElementById(scrollTo) + if (foundScroll !== null) { + // Smooth scroll + foundScroll.scrollIntoView({ behavior: "smooth" }) + } + + }, 200) + setScrollDone(true) + + } + // Gets the generated workflow - const getGeneratedWorkflow = (workflow_id) => { - fetch(globalUrl + "/api/v1/workflows/" + workflow_id, { + const getGeneratedWorkflow = (workflow_id) => { + const url = `${globalUrl}/api/v1/workflows/${workflow_id}` + fetch(url, { method: "GET", headers: { "Content-Type": "application/json", @@ -95,54 +112,55 @@ const EditWorkflow = (props) => { }, credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 when getting workflow"); + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 when getting workflow"); + } + + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.id === workflow_id) { + console.log("GOT WORKFLOW: ", responseJson) + if (name === "") { + innerWorkflow.name = responseJson.name + setName(responseJson.name) } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.id === workflow_id) { - console.log("GOT WORKFLOW: ", responseJson) - if (name === "") { - innerWorkflow.name = responseJson.name - setName(responseJson.name) - } - - if (description === "") { - innerWorkflow.description = responseJson.description - setDescription(description) - } - - if (newWorkflowTags === []) { - innerWorkflow.tags = responseJson.tags - setNewWorkflowTags(responseJson.tags) - } - - if (selectedUsecases === []) { - selectedUsecases = responseJson.usecase_ids - } - - innerWorkflow.id = responseJson.id - innerWorkflow.blogpost = responseJson.blogpost - innerWorkflow.actions = responseJson.actions - innerWorkflow.triggers = responseJson.triggers - innerWorkflow.branches = responseJson.branches - innerWorkflow.comments = responseJson.comments - innerWorkflow.workflow_variables = responseJson.workflow_variables - innerWorkflow.execution_variables = responseJson.execution_variables - - - setInnerWorkflow(innerWorkflow) - setUpdate(Math.random()) + if (description === "") { + innerWorkflow.description = responseJson.description + setDescription(description) } - }) - .catch((error) => { - //toast(error.toString()); - console.log("Get workflow error: ", error.toString()); - }) - } + + if (newWorkflowTags === []) { + innerWorkflow.tags = responseJson.tags + setNewWorkflowTags(responseJson.tags) + } + + if (selectedUsecases === []) { + selectedUsecases = responseJson.usecase_ids + } + + innerWorkflow.id = responseJson.id + innerWorkflow.blogpost = responseJson.blogpost + innerWorkflow.actions = responseJson.actions + innerWorkflow.triggers = responseJson.triggers + innerWorkflow.branches = responseJson.branches + innerWorkflow.comments = responseJson.comments + innerWorkflow.workflow_variables = responseJson.workflow_variables + innerWorkflow.execution_variables = responseJson.execution_variables + + + setInnerWorkflow(innerWorkflow) + setUpdate(Math.random()) + } + }) + .catch((error) => { + //toast(error.toString()); + console.log("Get workflow error: ", error.toString()); + }) + } if (foundWorkflowId.length > 0) { getGeneratedWorkflow(foundWorkflowId) @@ -162,6 +180,7 @@ const EditWorkflow = (props) => { return ( { setModalOpen(false); @@ -186,38 +205,42 @@ const EditWorkflow = (props) => { {newWorkflow ? "New" : "Editing"} workflow + {newWorkflow === true ? null :
- - - - + + + + + +
} +
- Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more + Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more + {/*
+ */} {showUpload === true ?
@@ -247,7 +270,7 @@ const EditWorkflow = (props) => {
-
+
{/*
+
+ : null} +
@@ -1047,7 +1163,7 @@ const EditWorkflow = (props) => { : null} - {newWorkflow === true && name.length > 2 ? + {/*newWorkflow === true && name.length > 2 ?
{ onlyResults={true} />
- : null} + : null*/} ) diff --git a/frontend/src/components/FixWorkflowValidationErrors.jsx b/frontend/src/components/FixWorkflowValidationErrors.jsx new file mode 100644 index 00000000..e223b2a1 --- /dev/null +++ b/frontend/src/components/FixWorkflowValidationErrors.jsx @@ -0,0 +1,687 @@ +import React, { useState, useEffect } from "react"; + +import { toast } from "react-toastify" +import theme from '../theme.jsx'; +import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; +import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; + +import { + Tooltip, + Typography, + Button, + Divider, + + MenuItem, + Select, + Chip, + TextField, + CircularProgress, +} from "@mui/material" + +import { + CheckCircleOutline as CheckCircleOutlineIcon, + ErrorOutline as ErrorOutlineIcon, +} from "@mui/icons-material" + +import { + green, + red, +} from "../views/AngularWorkflow.jsx" + +const FixWorkflowValidationErrors = (props) => { + const { globalUrl, workflow, setWorkflow, setUpdateParent, } = props; + + const [appsLoading, setAppsLoading] = useState(false) + const [apps, setApps] = useState([]) + const [appAuth, setAppAuth] = useState([]) + const [_, setUpdate] = useState(0) + + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; + + if (workflow === undefined || workflow === null) { + console.error("Workflow is undefined") + return null + } + + if (workflow.validation === undefined || workflow.validation === null) { + console.error("Workflow validation is undefined") + return null + } + + if (workflow.validation.valid === true) { + console.error("Workflow is valid - nothing to do for errors") + return null + } + + if (setWorkflow === undefined || setWorkflow === null) { + console.error("No setWorkflow") + return null + } + + const fetchApps = () => { + if (appsLoading === true) { + return + } + + setAppsLoading(true) + + const url = `${globalUrl}/api/v1/apps` + fetch(url,{ + method: "GET", + credentials: "include" + }) + .then(response => response.json()) + .then(data => { + setAppsLoading(false) + if (data.success === false) { + return + } + + setApps(data) + }) + .catch(error => { + setAppsLoading(false) + console.error("Error: ", error) + }) + } + + // Save the workflow as well + const saveWorkflow = (workflow) => { + if (workflow.id === undefined || workflow.id === null) { + toast("Workflow ID is missing during save. Please try again") + return + } + + const url = `${globalUrl}/api/v1/workflows/${workflow.id}` + fetch(url, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(workflow), + }) + .then(response => response.json()) + .then(data => { + if (data.success === false) { + toast("Failed to save workflow") + return + } + }) + .catch(error => { + toast("Failed to save workflow: " + error.toString()) + }) + } + + const fetchAuthentication = (reset, updateAction, closeMenu, action_id) => { + if (appsLoading === true) { + return + } + + const url = `${globalUrl}/api/v1/apps/authentication` + fetch(url,{ + method: "GET", + credentials: "include" + }) + .then(response => response.json()) + .then(data => { + if (data.success === false) { + return + } + + const authlist = data.data + setAppAuth(authlist) + if (updateAction === true) { + console.log("Updating action: ", action_id) + + // Find the action in the workflow and set auth for it + var foundActionIndex = -1 + for (var i = 0; i < workflow.actions.length; i++) { + if (workflow.actions[i].id === action_id) { + foundActionIndex = i + break + } + } + + if (foundActionIndex === -1) { + console.error("Failed to find action in workflow") + return + } + + const appId = workflow.actions[foundActionIndex].app_id + var lastauth = -1 + for (var authKey in authlist) { + if (authlist[authKey].app_id !== appId) { + continue + } + + if (authlist[authKey].created > lastauth) { + lastauth = authlist[authKey].created + } else { + continue + } + + console.log("FOUND AUTH: ", authlist[authKey]) + workflow.actions[foundActionIndex].authentication_id = authlist[authKey].id + } + + + if (setWorkflow !== undefined) { + setWorkflow(workflow) + } + } + }) + .catch(error => { + console.error("Auth loading error: ", error) + }) + } + + if (apps !== undefined && apps !== null && apps.length === 0 && appsLoading === false) { + fetchApps() + fetchAuthentication() + } + + const setSelectedAction = (action) => { + if (workflow === undefined || workflow === null) { + return null + } + + if (workflow.actions === undefined || workflow.actions === null || workflow.actions.length === 0) { + return null + } + + if (setWorkflow === undefined || setWorkflow === null) { + return null + } + + for (var i = 0; i < workflow.actions.length; i++) { + if (workflow.actions[i].id === action.id) { + workflow.actions[i] = action + + // Update any action with the same app_id to have same auth + for (var j = 0; j < workflow.actions.length; j++) { + if (workflow.actions[j].app_id === action.app_id) { + workflow.actions[j].authentication_id = action.authentication_id + workflow.actions[j].selectedAuthentication = action.selectedAuthentication + } + } + + break + } + } + + setWorkflow(workflow) + } + + const ErrorItem = (props) => { + const { apps, error, index } = props + + const [validating, setValidating] = useState(false) + const [actionRunInfo, setActionRunInfo] = useState({}) + if (error === undefined || error === null) { + return null + } + + if (apps === undefined || apps === null || apps.length === 0) { + return null + } + + const validateApp = (app, action) => { + if (validating) { + return + } + + setValidating(true) + + // FIXME: Run execution: + // 1. Should set app authentication validation + if (isCloud) { + action.environment = "Cloud" + } else { + action.environment = "Shuffle" + } + + /* + setExecutionResult({ + valid: false, + result: baseResult, + }) + setExecuting(true); + */ + + setActionRunInfo({}) + const url = `${globalUrl}/api/v1/apps/${app.id}/run?validation=true` + fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(action), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + setValidating(false) + + setActionRunInfo(responseJson) + + //console.log("RESPONSE: ", responseJson) + if ( + responseJson.success === true && + responseJson.result !== null && + responseJson.result !== undefined && + responseJson.result.length > 0 + ) { + //toast(" + } + }) + .catch((error) => { + toast("Execution error: " + error.toString()); + setValidating(false) + }) + } + + var authReturn = null + var validationReturn = null + var foundApp = { + "name": "", + "id": "", + } + var foundAction = { + "name": "", + "label": "", + "id": "", + "app_id": "", + "app_name": "", + } + + var selectedImage = null + var resolveButton = null + if (error.app_id !== undefined && error.app_id !== null) { + for (var i = 0; i < apps.length; i++) { + if (apps[i].id === error.app_id) { + foundApp = apps[i] + break + } + } + + if (!foundApp) { + toast("Couldn't find relevant app. Is it activated?") + return "Failed to find app" + } + + selectedImage = + + } + + if (error.action_id !== undefined && error.action_id !== null && workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { + for (var i = 0; i < workflow.actions.length; i++) { + if (workflow.actions[i].id === error.action_id) { + foundAction = workflow.actions[i] + break + } + } + } + + const validationIcon = Object.getOwnPropertyNames(actionRunInfo).length === 0 ? null : + + {actionRunInfo.result} + + } + placement="bottom" + > + {actionRunInfo.validation.valid === true ? + + : + + } + + + const authenticationType = foundApp.authentication + if (error.type === "configuration" || error.type === "authentication") { + // FIXME: Check the error + if (appAuth === undefined || appAuth === null) { + return "Loading auth" + } + + var relevantAuthentication = [] + var foundAuth = {} + for (var key in appAuth) { + if (appAuth[key].app.id !== error.app_id) { + continue + } + + foundAuth = appAuth[key] + relevantAuthentication.push(appAuth[key]) + } + + if (foundAction.selectedAuthentication === undefined || foundAction.selectedAuthentication === null) { + foundAction.selectedAuthentication = {} + } + + console.log("FOUNDACTION: ", foundAction, foundAuth) + + var authFound = false + if (foundAuth.id !== undefined && foundAuth.id !== null && foundAuth.id.length > 0) { + var authGroups = [] + // Choose from a dropdown + authReturn = + } + + // FIXME: Validate the CURRENT authentication that has been chosen? + if (foundApp.authentication === undefined || foundApp.authentication === null) { + toast("Authentication error: No authentication found") + authReturn = "Failed to find auth" + } + + if (authReturn === null && foundApp.authentication.type === "oauth2" || foundApp.authentication.type === "oauth2-app") { + authReturn = + + } else if (authReturn === null) { + authReturn = "Other auth - Not implemented" + + } + + validationReturn = authReturn === null || foundAuth.id === undefined || foundAuth.id === null || foundAuth.id.length === 0 || !authFound ? null : + + + //resolveButton = !(Object.getOwnPropertyNames(actionRunInfo).length === 0 || actionRunInfo.validation.valid === true) ? null : + resolveButton = + + } + + + return ( +
+ {authReturn} +
+ {validationReturn} + + {resolveButton} +
+ ) + } + + console.log("Workflow validation: ", workflow.validation) + return ( +
+ {workflow.errors !== undefined && workflow.errors !== null ? +
+ General errors: {workflow.errors.length} + {workflow.errors.map((error, index) => { + return ( +
+ - {error} +
+ ) + })} +
+ : null} + + + + + {workflow.validation.errors !== undefined && workflow.validation.errors !== null ? +
+ Validation errors: {workflow.validation.errors.length} + {workflow.validation.errors.map((error, index) => { + return ( +
+ +
+ ) + })} +
+ : null} + + + Apps loaded: {apps.length} + +
+ ) +} + +export default FixWorkflowValidationErrors diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 820b8253..bd2a1cba 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -2,7 +2,6 @@ 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, diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 3dc07460..7432caf3 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -28,7 +28,7 @@ import { Dialog, DialogTitle, } from "@mui/material"; -import { makeStyles } from '@mui/styles'; +import { makeStyles } from "@mui/styles"; import { Close as CloseIcon, @@ -132,6 +132,8 @@ const Header = (props) => { isMobile, serverside, billingInfo, + + notifications, } = props; const [isHeader, setIsHeader] = React.useState(false); const [modalOpen, setModalOpen] = useState(false); @@ -140,7 +142,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) + const [showTopbar, setShowTopbar] = useState(false) // Set to true to show top bar const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO" let navigate = useNavigate(); const classes = useStyles(); @@ -169,15 +171,13 @@ const Header = (props) => { setTooltipOpen(true); }; - const topbar_var = "topbar_closed4" - + const topbar_var = "topbar_closed5" useEffect(() => { + // Manually setShowTopbar(true) to show topbar by default const topbar = localStorage.getItem(topbar_var) if (topbar === "true") { setShowTopbar(false) - } else { - setShowTopbar(true) - } + } }, []) const hoverColor = "#f85a3e"; @@ -296,38 +296,52 @@ 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(); }) .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) { + 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 } + if (responseJson["reason"] === "SSO_REDIRECT") { + toast.info("Redirecting to SSO login page as SSO is required for this organization.") setTimeout(() => { - toast.info("Redirecting to SSO login page as SSO is required for this organization.") - window.location.href = responseJson["url"] - return - }, 2000) + toast.info( + "Redirecting to SSO login page as SSO is required for this organization." + ); + window.location.href = responseJson["url"]; + return; + }, 2000); } else { + toast("Successfully changed active organization - refreshing!"); setTimeout(() => { - window.location.reload() + window.location.reload(); }, 2000); } - toast("Successfully changed active organization - refreshing!"); } else { - if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + 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."); + toast( + "Failed changing org. Try again or contact support@shuffler.io if this persists." + ); } } }) @@ -416,37 +430,19 @@ const Header = (props) => { - - { - handleClose(); - }} - > - Notifications - - - - - { - handleClose(); - }} - > - About - - - {/* - - { - handleClose(); - }} - > - Get Started - - - */} + + { + handleClose(); + }} + > + Notifications ({ + notifications === undefined || notifications === null ? 0 : + notifications?.filter((notification) => notification.read === false).length + }) + + { @@ -457,19 +453,17 @@ const Header = (props) => { - {userdata?.public_username === undefined || userdata?.public_username === null || userdata?.public_username.length <= 0 ? null : - - { - handleClose(); - }} - > - Creator page - - - } - + + + { + handleClose(); + }} + > + About + + { @@ -483,7 +477,7 @@ const Header = (props) => { - Version: 1.4.0 + Version: 1.4.5 @@ -910,7 +904,6 @@ const Header = (props) => { }} > {avatarMenu} - {/*notificationMenu*/} {supportMenu} {logoCheck} diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 45324bd6..61d5cda0 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -167,7 +167,7 @@ const AuthenticationOauth2 = (props) => { //console.log("APP: ", selectedApp) if (selectedApp.name.toLowerCase() == "outlook_graph" || selectedApp.name.toLowerCase() == "outlook_office365") { handleOauth2Request( - "efe4c3fe-84a1-4821-a84f-23a6cfe8e72d", + "fd55c175-aa30-4fa6-b303-09a29fb3f750", "", "https://graph.microsoft.com", ["Mail.ReadWrite", "Mail.Send", "offline_access"], @@ -460,16 +460,16 @@ const AuthenticationOauth2 = (props) => { } - if ( - authenticationType.refresh_uri !== undefined && - authenticationType.refresh_uri !== null && - authenticationType.refresh_uri.length > 0 - ) { - state += `%26refresh_uri%3d${authenticationType.refresh_uri}`; + if (authenticationType.refresh_uri !== undefined && authenticationType.refresh_uri !== null && authenticationType.refresh_uri.length > 0) { + state += `%26refresh_uri%3d${authenticationType.refresh_uri}` } else { - state += `%26refresh_uri%3d${authentication_url}`; + state += `%26refresh_uri%3d${authentication_url}` } + if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { + state += `%26org_id%3d${workflow.org_id}` + } + // FIXME: Should this be =consent? var defaultPrompt = "login" if (prompt !== undefined && prompt !== null && prompt.length > 0) { @@ -524,7 +524,12 @@ const AuthenticationOauth2 = (props) => { //alert('"Secure Payment" window closed!'); if (getAppAuthentication !== undefined) { - getAppAuthentication(true, true, true); + // This should be orgId, not action Id as to load auth properly + if (workflow !== undefined && workflow !== null && workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { + getAppAuthentication(true, true, true, workflow.org_id) + } else { + getAppAuthentication(true, true, true) + } } toast("Authentication successful!") @@ -538,7 +543,7 @@ const AuthenticationOauth2 = (props) => { setFinalized(true) } } else { - console.log("Not closed") + //console.log("Not closed") } }, 1000); //do { @@ -739,7 +744,7 @@ const AuthenticationOauth2 = (props) => { - 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 - } + Oauth2 requires a client ID and secret to authenticate, defined in the remote system. Your redirect URL is {window.location.origin}/set_authentication -  { ); - const toggleBetweenRequiredOrOptional = (event) => { - setSSORequired(event.target.checked); - }; + const toggleBetweenRequiredOrOptional = (event) => { + if ( + ssoEntrypoint === "" && + openidAuthorization === "" && + openidToken === "" + ) { + if (!SSORequired) { + toast.error( + "Please fill in fields for either OpenID connect or SSO before continuing. " + ); + return; + } + } else { + toast.info("Toggled SSO. Remember to save."); + } + + setSSORequired(event.target.checked); + }; + + const HandleTestSSO = () => { + const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`; + const data = { + org_id: selectedOrganization?.id, + sso_test: true, + }; + + fetch(url, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify(data), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + if (response.status !== 200) { + toast.error( + "Failed to test sso. Please try again later or contact support@shuffler.io if issue persist." + ); + return; + } + return response.json(); + }) + .then((responjson) => { + if (responjson["reason"] === "SSO_REDIRECT") { + setTimeout(() => { + toast.info( + "Redirecting to SSO login page as SSO is required for this organization." + ); + window.location.href = responjson["url"]; + return; + }, 2000); + } else { + toast.error( + "No SSO found for this org. Please set up sso for this org." + ); + } + }) + .catch((err) => { + console.log("error for sso test is: ", err); + }) + } return (
@@ -664,398 +726,476 @@ const OrgHeaderexpanded = (props) => { SSO Configuration -
- Make SAML SSO or OpenID Authentication Required or Optional for Your Organization. -
- - {SSORequired ? 'Required' : 'Optional'} -
-
- - OpenID connect - - - - Client ID - 0 - } - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The OpenID client ID from the identity provider" - value={openidClientId} - onChange={(e) => { - setOpenidClientId(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - Client Secret (optional) - 0 - } - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE" - value={openidClientSecret} - onChange={(e) => { - setOpenidClientSecret(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - - - Authorization URL - { - setOpenidAuthorization(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - Token URL - { - setOpenidToken(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - {/* } */} - {/*isCloud ? null : */} - - SAML SSO (v1.1) - - - - SSO Entrypoint (IdP) - 0 - } - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The entrypoint URL from your provider" - value={ssoEntrypoint} - onChange={(e) => { - setSsoEntrypoint(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - - SSO Certificate (X509) - { - setSsoCertificate(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - - {isCloud ? - - IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso - - : null} - - {isCloud ? null : ( - - - App Download URL - { - setAppDownloadUrl(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} - {isCloud ? null : ( - - - App Download Branch - { - setAppDownloadBranch(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} - {isCloud ? null : ( - - - Workflow Download URL - { - setWorkflowDownloadUrl(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} - {isCloud ? null : ( - - - Workflow Download Branch - { - setWorkflowDownloadBranch(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - - - )} +
+ + Make SAML SSO or OpenID Authentication Required or Optional for Your + Organization. + +
+ + {SSORequired ? "Required" : "Optional"} +
+
+
+ + You can test your SSO configuration by clicking the button below. + Before testing, ensure you have set Open ID Connect or SAML SSO + credentials. + + 0 || + ssoCertificate.length > 0 || + openidAuthorization.length > 0 || + openidClientId.length > 0 + ) + ? "Please ensure all SSO credentials are set before testing." + : "" + } + > + + + + +
+
+ + + OpenID connect + + + + + Client ID + 0 + } + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The OpenID client ID from the identity provider" + value={openidClientId} + onChange={(e) => { + setOpenidClientId(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + Client Secret (optional) + 0 + } + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE" + value={openidClientSecret} + onChange={(e) => { + setOpenidClientSecret(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + + + Authorization URL + { + setOpenidAuthorization(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + Token URL + { + setOpenidToken(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + {/* } */} + {/*isCloud ? null : */} + + + SAML SSO (v1.1) + + + + + SSO Entrypoint (IdP) + 0 + } + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The entrypoint URL from your provider" + value={ssoEntrypoint} + onChange={(e) => { + setSsoEntrypoint(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + + SSO Certificate (X509) + { + setSsoCertificate(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + + {isCloud ? ( + + IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso + + ) : null} + + {isCloud ? null : ( + + + App Download URL + { + setAppDownloadUrl(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} + {isCloud ? null : ( + + + App Download Branch + { + setAppDownloadBranch(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} + {isCloud ? null : ( + + + Workflow Download URL + { + setWorkflowDownloadUrl(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} + {isCloud ? null : ( + + + Workflow Download Branch + { + setWorkflowDownloadBranch(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + + )} -
- {orgSaveButton} -
- {/* +
+ {orgSaveButton} +
+ {/* {expanded ? @@ -1064,9 +1204,9 @@ const OrgHeaderexpanded = (props) => { } */} - -
- ) -} + +
+ ); +}; export default OrgHeaderexpanded; diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index fb462566..e1869125 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -90,9 +90,10 @@ import { Circle as CircleIcon, SquareFoot as SquareFootIcon, Storage as StorageIcon, + Check as CheckIcon, } from '@mui/icons-material'; -const useStyles = makeStyles({ +export const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important", }, @@ -206,7 +207,6 @@ const ParsedAction = (props) => { } }, [expansionModalOpen]) - useEffect(() => { if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) { @@ -223,20 +223,6 @@ const ParsedAction = (props) => { } }, []) - /* - useEffect(() => { - setParamValues(selectedAction.parameters.map((param) => { - return { - name: param.name, - value: param.value, - } - })) - },[ - selectedAction, selectedApp,setNewSelectedAction, workflow, - ]) - */ - - useEffect(() => { if (selectedAction.parameters === null || selectedAction.parameters === undefined) { return @@ -1500,92 +1486,7 @@ const ParsedAction = (props) => { - {/* - {}} - > - - - - - - - */} - {/* - { - //setAuthenticationModalOpen(true); - console.log("Should enable/disable magic!") - console.log("Action: ", selectedAction) - if (selectedAction.run_magic_output === undefined) { - selectedAction.run_magic_output = true - } else { - if (selectedAction.run_magic_output === true) { - selectedAction.run_magic_output = false - } else { - selectedAction.run_magic_output = true - } - } - setSelectedAction(selectedAction) - setUpdate(Math.random()); - }} - > - - - - - */} - {/* - { - }} - > - - - - - - - */} { aiSubmit("Fill based on previous values", undefined, undefined, selectedAction) //} setAutocompleting(true) + + setTimeout(() => { + setAutocompleting(false) + }, 3000) }} > {
)} + {selectedApp.name !== undefined && selectedAction.authentication !== null && selectedAction.authentication !== undefined && @@ -1998,7 +1904,7 @@ const ParsedAction = (props) => { for (let [key,keyval] in Object.entries(selectedAction.parameters)) { if (selectedAction.parameters[key].configuration === false) { - console.log("FIELDSKIP: ", selectedAction.parameters[key].name) + //console.log("FIELDSKIP: ", selectedAction.parameters[key].name) continue } @@ -2017,8 +1923,8 @@ const ParsedAction = (props) => { selectedAction.parameters[key].value = "" } } - setSelectedAction(selectedAction); - setUpdate(Math.random()); + setSelectedAction(selectedAction) + setUpdate(Math.random()) } else if (e.target.value === "authgroups") { if (authGroups !== undefined && authGroups !== null && authGroups.length === 0) { @@ -2085,7 +1991,18 @@ const ParsedAction = (props) => { }} value={data} > - {data.last_modified === true ? + + {data?.validation?.valid === true ? + + + + : null } + {data?.last_modified === true ? { color="secondary" /> : null} - {data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ? + {/*data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ? - : null} + : null*/} {data.label} ); @@ -2120,14 +2037,6 @@ const ParsedAction = (props) => { - {/* - - - curaction.authentication = authenticationOptions - if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "") - */} { value={selectedAction} classes={{ inputRoot: classes.inputRoot }} groupBy={(option) => { + // FIXME: Sorting // Most popular // Is categorized // Uncategorized @@ -2364,7 +2274,6 @@ const ParsedAction = (props) => { }, }} 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)) @@ -2449,22 +2358,6 @@ const ParsedAction = (props) => { extraUrl = descSplit[descSplit.length-1] } - //for (let [line,lineval] in Object.entries(descSplit)) { - // if (descSplit[line].includes("http") && descSplit[line].includes("://")) { - // const urlsplit = descSplit[line].split("/") - // try { - // extraUrl = "/"+urlsplit.slice(3, urlsplit.length).join("/") - // } catch (e) { - // //console.log("Failed - running with -1") - // extraUrl = "/"+urlsplit.slice(3, urlsplit.length-1).join("/") - // } - - - // //console.log("NO BASEURL TOO!! Why missing last one in certain scenarios (sevco)?", extraUrl, urlsplit, descSplit[line]) - // //break - // } - //} - if (extraUrl.length > 0) { if (extraUrl.includes(" ")) { extraUrl = extraUrl.split(" ")[0] @@ -2492,6 +2385,7 @@ const ParsedAction = (props) => { ); }} renderInput={(params) => { + if (params.inputProps?.value) { const prefixes = ["Post", "Put", "Patch"]; for (let prefix of prefixes) { @@ -2512,84 +2406,86 @@ const ParsedAction = (props) => { } - const actionDescription = ( - - - - {params.inputProps.value} - - { - event.preventDefault(); - event.stopPropagation(); - }} - - onClick={() => { - setHiddenDescription(true) - const inputElement = document.getElementById(uiBox); - if (inputElement) { - inputElement.focus(); - } - }}> - - - - - - - Description: {selectedAction?.description} - - - - ); - - return ( - - + + {params.inputProps.value} + + { + event.preventDefault(); + event.stopPropagation(); + }} + + onClick={() => { + setHiddenDescription(true) + const inputElement = document.getElementById(uiBox); + if (inputElement) { + inputElement.focus(); + } + }} + > + + + + + + + Description: {selectedAction?.description} + + + + ) + */ - data-lpignore="true" - autocomplete="off" - dataLPIgnore="true" - autoComplete="off" - - color="primary" - id="checkbox-search" - variant="body1" - style={{ - backgroundColor: theme.palette.inputColor, - borderRadius: theme.palette.borderRadius, + return ( + - - ); - }} - /> + > + + + ); + }} + /> ) : null} {/*setNewSelectedAction !== undefined ? @@ -2939,58 +2835,24 @@ const ParsedAction = (props) => { } } - /* - if ( - (selectedAction.auth_not_required !== undefined && !selectedAction.auth_not_required) && - selectedActionParameters[count] !== undefined && - selectedActionParameters[count] !== null && - selectedActionParameters[count].value !== undefined && - selectedAction.parameters[count] !== undefined && - selectedAction.parameters[count] !== null && - selectedAction.parameters[count].value !== undefined && - selectedAction.selectedAuthentication !== undefined && - selectedAction.selectedAuthentication.fields !== undefined && - selectedAction.selectedAuthentication.fields[data.name] !== - undefined - ) { - */ - - /* - if (selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields[data.name] !== undefined) { - - // This sets the placeholder in the frontend. (Replaced in backend) - selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name]; - selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name]; - setSelectedAction(selectedAction); - //setUpdate(Math.random()) - - if (authWritten) { - return null - } - - authWritten = true - return ( - - Authentication fields are hidden - - ) - } - */ + if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) { + //selectedAction.parameters = selectedActionParameters + console.log("PARAM BUG: ", selectedAction) + } //!selectedAction.auth_not_required && if (selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields[data.name] !== undefined) { + // This sets the placeholder in the frontend. (Replaced in backend) - selectedActionParameters[count].value = - selectedAction.selectedAuthentication.fields[data.name]; - selectedAction.parameters[count].value = - selectedAction.selectedAuthentication.fields[data.name]; + if (selectedActionParameters[count] !== undefined) { + selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name] + } + + if (selectedAction.parameters[count] !== undefined) { + selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name] + } + setSelectedAction(selectedAction); //setUpdate(Math.random()) @@ -3064,7 +2926,7 @@ const ParsedAction = (props) => { if (data.value.length === 0) { if (data.name.toLowerCase() === "headers") { - console.log("Should show headers field instead with + and -!") + //console.log("Should show headers field instead with + and -!") // Check if file ID exists // @@ -3360,13 +3222,6 @@ const ParsedAction = (props) => { { setUiBox("closed") - - /* - const inputElement = document.getElementById(uiBox); - if (inputElement) { - inputElement.focus(); - } - */ }} > @@ -4286,20 +4141,12 @@ const ParsedAction = (props) => { /*
*/ } - const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}` + const buttonTitle = `Authenticate API ${selectedApp.name.replaceAll("_", " ")}` const hasAutocomplete = data?.autocompleted === true if (data.variant === undefined || data.variant === null) { data.variant = "STATIC_VALUE" } - /* - if (data?.configuration === true) { - if (data?.name === "url" && authenticationType?.type === "oauth2-app") { - data.configuration = false - } - } - */ - return (
{/* {hideBodyButton} */} diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 0663ee93..e66afe8f 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -393,7 +393,10 @@ const Priorities = (props) => { return (
-

Notifications

+

Notifications ({ + notifications?.filter((notification) => showRead === true || notification.read === false).length + })

+ Notifications help you find potential problems with your workflows and apps.  { const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props; - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); let navigate = useNavigate(); if (window.location.pathname === "/workflows") { diff --git a/frontend/src/components/RecentWorkflow.jsx b/frontend/src/components/RecentWorkflow.jsx new file mode 100644 index 00000000..3bd804b4 --- /dev/null +++ b/frontend/src/components/RecentWorkflow.jsx @@ -0,0 +1,157 @@ +import React from "react" + +import { + Avatar, + Box, + Button, + Typography, + Tooltip, +} from "@mui/material" +import { useNavigate } from "react-router"; +import theme from "../theme.jsx"; + +import { + Lock as LockIcon, +} from '@mui/icons-material'; + +// onclickHandler = function override from parent onclick +const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflowId, }) => { + + const navigate = useNavigate(); + + const [hovered, setHovered] = React.useState(false) + + if (workflow === undefined || workflow === null) { + console.log("No workflow") + return null + } + + /* + * Note for @Lalit: + * + * When you want to make a list of something that is complex, + * make a component. This way, you can easily manage + * the logic, and we can actually reuse it. This component is used + * multiple places, so do make sure to not break it randomly. + */ + + const expandLeftNav = leftNavOpen === true || leftNavOpen === undefined ? true : false + + // Check if workflow.input_markdown has an image in it + // If it does, show it as the main thing + // + var relevantImageUrl = "" + if (workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown !== "") { + // Look for tag or ![alt](src) markdown + // html > markdown + const imgTag = workflow.input_markdown.match(/]+>/g) + + if (imgTag !== null) { + const src = imgTag[0].match(/src="([^"]+)"/) + if (src !== null) { + relevantImageUrl = src[1] + } + } else { + const markdownTag = workflow.input_markdown.match(/!\[.*\]\(.*\)/g) + + if (markdownTag !== null) { + const src = markdownTag[0].match(/\(([^)]+)\)/) + if (src !== null) { + relevantImageUrl = src[1] + } + } + } + } + + return ( +
setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + +
+ ) +} + +export default RecentWorkflow diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx index c390666a..6e619652 100644 --- a/frontend/src/components/SearchData.jsx +++ b/frontend/src/components/SearchData.jsx @@ -47,7 +47,7 @@ const chipStyle = { const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const SearchData = props => { - const { serverside, globalUrl, userdata, setModalOpen, modalOpen } = props + const { serverside, globalUrl, userdata, searchBarModalOpen, setSearchBarModalOpen } = props let navigate = useNavigate(); const borderRadius = 3 const node = useRef() @@ -56,8 +56,8 @@ const SearchData = props => { const [value, setValue] = useState(""); const handleLinkClick = () => { - if (modalOpen) { - setModalOpen(false); // Assuming setModalOpen is defined correctly + if (searchBarModalOpen) { + setSearchBarModalOpen(false); // Assuming setModalOpen is defined correctly } else { console.log("Condition not met, staying on the same page"); } @@ -71,7 +71,7 @@ const SearchData = props => { // return null //} - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); // if (window.location.pathname === "/docs" || window.location.pathname === "/apps" || window.location.pathname === "/usecases" ) { // setModalOpen(false) // } @@ -95,7 +95,7 @@ const SearchData = props => { const trimmedValue = inputValue.trim(); if (trimmedValue !== '') { navigate(`/search?q=${trimmedValue}`, { state: trimmedValue, replace: true }); - setModalOpen(false); + setSearchBarModalOpen(false); } } }; @@ -249,7 +249,7 @@ const SearchData = props => { { //console.log("CLICK") setSearchOpen(true) - setModalOpen(false) + setSearchBarModalOpen(false) aa('init', { appId: searchClient.appId, apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"] @@ -498,7 +498,7 @@ const SearchData = props => { return ( { setSearchOpen(true) - setModalOpen(false) + setSearchBarModalOpen(false) aa('init', { appId: searchClient.appId, apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"] @@ -702,7 +702,7 @@ const SearchData = props => { console.log("CLICK") setSearchOpen(true) - setModalOpen(false) + setSearchBarModalOpen(false) }}> { setMouseHoverIndex(index) diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 6b3c50ae..b3a89011 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -1,9 +1,11 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useContext } from 'react'; import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import SearchBox from "../components/SearchData.jsx"; +import { Context } from '../context/contextApi.jsx'; + import { Chip, IconButton, @@ -45,20 +47,22 @@ const chipStyle = { const SearchField = props => { const { serverside, userdata, isMobile, isLoaded, globalUrl, isHeader, isLoggedIn, small, rounded } = props + const {searchBarModalOpen, setSearchBarModalOpen} = useContext(Context); + let navigate = useNavigate(); const borderRadius = 3 const node = useRef() const [searchOpen, setSearchOpen] = useState(false) - const [modalOpen, setModalOpen] = React.useState(false); + // const [modalOpen, setModalOpen] = React.useState(false); const [oldPath, setOldPath] = useState("") const [value, setValue] = useState(""); useEffect(() => { Mousetrap.bind(['command+k', 'ctrl+k'], () => { - setModalOpen(true); + setSearchBarModalOpen(true); return false; // Prevent the default action }); Mousetrap.bind(['esc'], () => { - setModalOpen(false); + setSearchBarModalOpen(false); return false; // Prevent the default action }); @@ -72,9 +76,9 @@ const SearchField = props => { // console.log("key:", dataValue.key), //console.log("value:",dataValue.value), { - setModalOpen(false); + setSearchBarModalOpen(false); }} PaperProps={{ style: { @@ -92,12 +96,12 @@ const SearchField = props => { {isHeader ?
Search for Docs, Apps, Workflows and more
: null} - + @@ -155,7 +159,7 @@ const SearchField = props => { color="primary" placeholder="Search Apps, Workflows, Docs..." onClick={(event) => { - setModalOpen(true) + setSearchBarModalOpen(true) }} limit={5} /> diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 69f2f429..84896f82 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -44,7 +44,7 @@ import { import { validateJson } from "../views/Workflows.jsx"; -import ReactJson from "react-json-view"; +import ReactJson from "react-json-view-ssr"; import PaperComponent from "../components/PaperComponent.jsx"; import { padding, textAlign } from '@mui/system'; diff --git a/frontend/src/components/UsecaseSearch.jsx b/frontend/src/components/UsecaseSearch.jsx index 474fcce8..19aa2e53 100644 --- a/frontend/src/components/UsecaseSearch.jsx +++ b/frontend/src/components/UsecaseSearch.jsx @@ -349,7 +349,7 @@ const UsecaseSearch = (props) => { const [selectedAction, setSelectedAction] = React.useState({}); const [firstRequest, setFirstRequest] = React.useState(true); - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); //const alert = useAlert() useEffect(() => { diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index 47c040db..8dc43d5f 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -161,7 +161,7 @@ const WelcomeForm = (props) => { const [clickdiff, setclickdiff] = useState(0); const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); //const alert = useAlert(); let navigate = useNavigate(); diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index 791b8835..5de17156 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -28,7 +28,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52 const AppGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs //const [apps, setApps] = React.useState([]); diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 13a6735f..a8949db5 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -47,7 +47,7 @@ const WorkflowTemplatePopup = (props) => { const [requestSent, setRequestSent] = React.useState(false) - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); let navigate = useNavigate(); useEffect(() => { if (modalOpen !== true) { diff --git a/frontend/src/components/WorkflowTemplatePopup2.jsx b/frontend/src/components/WorkflowTemplatePopup2.jsx new file mode 100644 index 00000000..ddd0a47f --- /dev/null +++ b/frontend/src/components/WorkflowTemplatePopup2.jsx @@ -0,0 +1,901 @@ +import React, { useState, useEffect } from "react"; + +import { toast } from "react-toastify" +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, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Drawer, + CircularProgress, + Fade, + IconButton, + Tooltip, +} from "@mui/material"; + +import { + Check as CheckIcon, + TrendingFlat as TrendingFlatIcon, + Close as CloseIcon, + East as EastIcon, + Interests as InterestsIcon, +} from '@mui/icons-material'; + +import { + green, + yellow, + red, + grey, +} from "../views/AngularWorkflow.jsx" + +import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx"; +import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; +import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx"; +import FixWorkflowValidationErrors from "../components/FixWorkflowValidationErrors.jsx"; + +const WorkflowTemplatePopup = (props) => { + const { + userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage, getAppFramework, showTryit, shownColor, workflowBuilt, usecaseDetails, + + isModalOpenDefault, + setIsClicked, + inputWorkflowId, + } = props; + + const [isActive, setIsActive] = useState(workflowBuilt === true); + const [isHovered, setIsHovered] = useState(false); + const [modalOpen, setModalOpen] = useState(isModalOpenDefault === true ? true : false) + const [errorMessage, setErrorMessage] = useState(""); + const [workflowLoading, setWorkflowLoading] = useState(false) + const [showLoginButton, setShowLoginButton] = useState(false); + 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 [appSetupDone, setAppSetupDone] = React.useState(false) + + const [requestSent, setRequestSent] = React.useState(false) + const [showTryitOut, setShowTryitout] = React.useState(showTryit === true ? true : false) + + const [loadingWorkflow, setLoadingWorkflow] = React.useState(false) + const [workflow, setWorkflow] = useState({}); + const [_, setUpdate] = useState(0) + + const fetchWorkflow = (id) => { + if (id === undefined || id === null || id === "") { + return + } + + if (loadingWorkflow === true) { + return + } + + setLoadingWorkflow(true) + const url = `${globalUrl}/api/v1/workflows/${id}` + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + setLoadingWorkflow(false) + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + console.log("Error in workflow loading for ID ", id) + } else { + setWorkflow(responseJson) + } + }) + .catch((error) => { + console.log("err in framework: ", error.toString()); + setLoadingWorkflow(false) + }) + + + } + + if (inputWorkflowId !== undefined && inputWorkflowId !== null && inputWorkflowId !== "" && workflow.id !== inputWorkflowId) { + fetchWorkflow(inputWorkflowId) + } + + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + let navigate = useNavigate(); + useEffect(() => { + if (modalOpen !== true) { + if (workflowLoading === true) { + setWorkflowLoading(false) + } + + //console.log("Modal is not open, so we are not doing anything.") + return + } + + if (workflowLoading !== true) { + //console.log("Workflow loading is false, so we can try to get the workflow.") + return + } + + console.log("DEBUG: Skipped direct generation without Try it for now.") + + /* + if (!srcapp.includes(":default") && !dstapp.includes(":default")) { + if (appSetupDone === false && setAppSetupDone !== undefined) { + setAppSetupDone(true) + } + + getGeneratedWorkflow() + } + + if (missingSource !== undefined && missingDestination !== undefined) { + if (appSetupDone === false && setAppSetupDone !== undefined) { + setAppSetupDone(true) + } + } + + if (getAppFramework !== undefined) { + setTimeout(() => { + getAppFramework() + }, 500) + } + */ + }, [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") + + /* + setTimeout(() => { + navigate("/workflows/" + workflow.id) + }, 2000) + */ + } + }, [configurationFinished, workflow]) + + const imageSize = 32 + const defaultBorder = "1px solid rgba(255,255,255,0.6)" + const imagestyleWrapper = { + height: imageSize, + width: imageSize, + borderRadius: imageSize, + border: isHomePage ? null : defaultBorder, + overflow: "hidden", + display: "flex", + + backgroundColor: theme.palette.inputColor, + } + + const imagestyleWrapperDefault = { + height: imageSize, + width: imageSize, + borderRadius: imageSize, + border: isHomePage ? null : defaultBorder, + overflow: "hidden", + display: "flex", + + backgroundColor: theme.palette.inputColor, + } + + const imagestyle = { + height: imageSize, + width: imageSize, + borderRadius: imageSize, + //border: isHomePage ? null : defaultBorder, + overflow: "hidden", + + backgroundColor: theme.palette.inputColor, + } + + const imagestyleDefault = { + display: "block", + marginLeft: 9, + marginTop: 9, + height: imageSize, + width: "auto", + + backgroundColor: theme.palette.inputColor, + } + + if (modalOpen === false && (title === undefined || title === null || title === "")) { + if (setIsClicked !== undefined) { + setIsClicked(false) + } + + console.log("No title for workflow template popup!"); + return null + } + + + const loadAppAuth = () => { + // Check if it exists, and has keys + // + if (userdata === undefined || userdata === null || Object.keys(userdata).length === 0) { + setErrorMessage("You need to be logged in to try the pre-built Workflow Templates.") + setShowLoginButton(true) + + // Send the user to the login screen after 3 seconds + setTimeout(() => { + // Make it cancel if the state modalOpen changes + if (modalOpen === false) { + return + } + + navigate("/login?view=" + window.location.pathname + window.location.search) + }, 4500) + + return + } + + + fetch(`${globalUrl}/api/v1/apps/authentication`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for setting app auth :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + toast("Failed to get app auth: " + responseJson.reason); + return + } + + var newauth = []; + for (let authkey in responseJson.data) { + if (responseJson.data[authkey].defined === false) { + continue; + } + + newauth.push(responseJson.data[authkey]); + } + + setAppAuthentication(newauth); + }) + .catch((error) => { + //toast(error.toString()); + console.log("New auth error: ", error.toString()); + }); + } + + // Can create and set workflows + const reloadWorkflow = (workflow_id) => { + + const new_url = `${globalUrl}/api/v1/workflows/${workflow_id}` + return fetch(new_url, { + 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; + } + //setSubmitLoading(false); + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Error setting workflow: ", responseJson.reason) + } else { + toast("Error setting workflow.") + } + + return + } else if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "") { + setWorkflow(responseJson) + } + + return responseJson; + }) + .catch((error) => { + toast("Failed reloading configured workflow: ", error.toString()); + }); + }; + + // Can create and set workflows + const saveWorkflow = (workflowdata) => { + + const new_url = `${globalUrl}/api/v1/workflows?set_auth=true` + return fetch(new_url, { + method: "POST", + 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; + } + //setSubmitLoading(false); + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Error setting workflow: ", responseJson.reason) + } else { + toast("Error setting workflow.") + } + + return + } + + // In case it got a new id, this is to make sure it loads with the correct config + if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "") { + reloadWorkflow(responseJson.id) + } + + return responseJson; + }) + .catch((error) => { + toast("Failed generating workflow: ", error.toString()); + }); + }; + + + const getGeneratedWorkflow = () => { + // POST + // https://shuffler.io/api/v1/workflows/merge + // destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",…} + // id: "" + // middle:[] + // name: "Email analysis" + // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name + if (requestSent === true) { + return + } + + console.log("SRCAPP: ", srcapp, "DSTAPP: ", dstapp) + if (srcapp === undefined || srcapp === null) { + srcapp = "" + } + + if ((srcapp !== undefined && srcapp !== null && srcapp.includes(":default")) || (dstapp !== undefined && dstapp !== null && dstapp.includes(":default"))) { + toast("You need to select both a source and destination app before generating this workflow.") + + if (srcapp !== undefined && srcapp !== null && srcapp.includes(":default")) { + setMissingSource({ + "type": srcapp.split(":")[0], + }) + } + + if (dstapp !== undefined && dstapp !== null && dstapp.includes(":default")) { + setMissingDestination({ + "type": dstapp.split(":")[0], + }) + } + + return + } + + setWorkflowLoading(true) + + const newsrcapp = srcapp + const newdstapp = dstapp + + const mergedata = { + name: title, + id: "", + source: { + app_name: newsrcapp, + }, + middle: [], + destination: { + app_name: newdstapp, + }, + } + + setRequestSent(true) + const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge` + fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(mergedata), + }) + .then((response) => { + if (response.status !== 200) { + //console.log("Status not 200 for framework!"); + setRequestSent(false) + } + + setWorkflowLoading(false) + return response.json(); + }) + .then((responseJson) => { + if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "" && responseJson.name !== undefined && responseJson.name !== null && responseJson.name !== "") { + console.log("Success in workflow template (prebuilt): ", responseJson); + setWorkflow(responseJson) + + // Sets it in the database properly + saveWorkflow(responseJson) + return + } + + if (responseJson.success === false) { + //console.log("Error in workflow template: ", responseJson.error); + setRequestSent(false) + + const defaultMessage = "Error: Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io if you want manual help building this usecase until the AI system is handled." + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") { + setErrorMessage(defaultMessage + "\n\n" + responseJson.reason) + } else { + setErrorMessage(defaultMessage) + } + + setIsActive(true) + //setTimeout(() => { + // setModalOpen(false) + //}, 5000) + } else { + console.log("Success in workflow template: ", responseJson); + setIsActive(true) + if (responseJson.workflow_id === "") { + console.log("Failed to build workflow for these tools. Closing in 3 seconds.") + return + } + + fetchWorkflow(responseJson.workflow_id) + } + }) + .catch((error) => { + console.log("err in framework: ", error.toString()); + setRequestSent(false) + setWorkflowLoading(false) + }) + } + + if (modalOpen === true && !srcapp?.includes(":default") && !dstapp?.includes(":default")) { + if (appSetupDone === false && setAppSetupDone !== undefined) { + setAppSetupDone(true) + } + + // No autoruns anymore without clicking "Try it" + if (workflow.id === undefined && workflowLoading === false && errorMessage === "") { + //getGeneratedWorkflow() + } + } + + const isFinished = () => { + // Look for configuration fields being done in the current modal + // 1. Start by finding the modal + const template = document.getElementById("workflow-template") + if (template === null || template == undefined) { + return true + } + + // Find item in template with id app-config + const appconfig = template.getElementsByClassName("app-config") + if (appconfig === null || appconfig == undefined) { + return true + } + + return false + } + + const ModalView = () => { + if (modalOpen === false) { + return null + } + + const divHeight = 500 + const divWidth = 500 + + return ( + { + setModalOpen(false); + + if (setIsClicked !== undefined) { + setIsClicked(false) + } + }} + PaperProps={{ + style: { + backgroundColor: "black", + color: "white", + minWidth: isHomePage ? null : isMobile ? 300 : 850, + maxWidth: isHomePage ? null : isMobile ? 300 : 850, + paddingTop: isMobile ? null : 75, + itemAlign: "center", + }, + }} + > + { + setModalOpen(false); + }} + > + + + + + Configure Workflow + + + {title === undefined || title === null || title === "" ? null : + + + Selected Workflow: + +
+ + +
+
+ } + +
+ {/* Fix the timeline when errors are fixed.. how? */} + + + +
+ + {workflowLoading === true ? +
+ Generating the Workflow... + + +
+ : +
+ {usecaseDetails === undefined ? null : + + {usecaseDetails?.description} + + } + + {errorMessage !== "" ? errorMessage : ""} + + {showLoginButton ? + + + Sign up + + + + : + !showTryitOut && !isActive ? + + : null} +
+ } + + {!isLoggedIn ? null : +
+ {(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ? + + {"Find relevant Apps for this Usecase"} + + : null} + + {(missingSource !== undefined) ? +
+ +
+ : null} + + {(missingDestination !== undefined) ? +
+ +
+ : null} +
+ } + + + +
+
+ ) + } + + if (isModalOpenDefault === true) { + return + } + + var parsedTitle = title !== undefined && title !== null ? title : "" + const maxlength = 50 + if (title !== undefined && title !== null && title.length > maxlength) { + parsedTitle = title.substring(0, maxlength) + "..." + } + + parsedTitle = parsedTitle.replaceAll("_", " ") + + const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : "" + + const boxHeight = 104 + const highlightColor = shownColor !== undefined && shownColor !== null && shownColor !== "" ? shownColor : "#f85a3e" + + var hasInterest = false + if (userdata.interests !== undefined && userdata.interests !== null && userdata.interests.length > 0) { + const comparisonTitle = title === undefined || title === null ? "" : title.trim().toLowerCase().replaceAll(" ", "_") + for (var interestkey in userdata.interests) { + if (userdata.interests[interestkey].name === undefined || userdata.interests[interestkey].name === null || userdata.interests[interestkey].name === "") { + continue + } + + if (modalOpen) { + console.log("COMPARE: ", userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_"), comparisonTitle) + } + + if (userdata.interests[interestkey].name.trim().toLowerCase().replaceAll(" ", "_") === comparisonTitle) { + if (modalOpen) { + console.log("FOUND: ", comparisonTitle) + } + + hasInterest = true + break + } + } + } + + const borderStyle = isHomePage ? null : isHovered && isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? `1px solid ${highlightColor}` : "1px solid rgba(33, 33, 33, 1)" + + return ( +
+ + +
{ + setIsHovered(true) + + setShowTryitout(true) + }} + onMouseLeave={() => { + setIsHovered(false) + + if (showTryit !== true) { + setShowTryitout(false) + } + }} + onClick={() => { + if (visualOnly === true) { + console.log("Not showing more than visuals.") + return + } + + if (!isLoggedIn) { + loadAppAuth() + setModalOpen(true) + } else if (isLoggedIn && errorMessage !== "") { + toast.error("Already failed to generate a workflow for this usecase. Please try again later or contact support@shuffler.io.") + + setModalOpen(true) + } else if (isActive) { + // toast.success("Workflow already generated. Please try another workflow template!") + + // FIXME: Remove these? + loadAppAuth() + setModalOpen(true) + //getGeneratedWorkflow() + } else { + setModalOpen(true) + //setWorkflowLoading(false) + } + }} + > + +
+ {shownColor !== undefined && shownColor !== null && shownColor !== "" ? +
+ : null} + +
+
+
+ {img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ? + +
+ +
+
+ : +
+ } +
+ + + {img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ? + +
+
+ +
+
+
+ : +
+ } + +
+
+ + {parsedTitle} + +
+
+
+ +
+ {isActive === true && errorMessage === "" ? + + + + : ""} + + {!isActive && hasInterest === true ? + + + + : null} +
+ + + {showTryitOut && !isActive ? + + + + : null} +
+ + +
+ ) +} + +export default WorkflowTemplatePopup diff --git a/frontend/src/components/WorkflowValidationTimeline.jsx b/frontend/src/components/WorkflowValidationTimeline.jsx index 04eb2314..5e20c8d9 100644 --- a/frontend/src/components/WorkflowValidationTimeline.jsx +++ b/frontend/src/components/WorkflowValidationTimeline.jsx @@ -23,6 +23,7 @@ import { grey, } from "../views/AngularWorkflow.jsx" +import WorkflowTemplatePopup2 from "../components/WorkflowTemplatePopup2.jsx" import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import theme from "../theme.jsx"; const itemHeight = 24 @@ -63,7 +64,7 @@ export const getParentNodes = (workflow, action) => { currentnode = workflow.triggers.find((element) => element.id === allkeys[parentkey]) if (currentnode === undefined) { - console.log("Could not find parent node for: ", allkeys[parentkey]) + //console.log("Could not find parent node for: ", allkeys[parentkey]) continue } } @@ -128,11 +129,13 @@ export const getParentNodes = (workflow, action) => { } const WorkflowValidationTimeline = (props) => { - const { workflow, originalWorkflow, apps, getParents, execution} = props + const { globalUrl, userdata, workflow, originalWorkflow, apps, getParents, execution, showHoverColor, } = props + + const [hovering, setHovering] = useState(false) + const [decidedColor, setDecidedColor] = useState(grey) + const [isClicked, setIsClicked] = useState(false) const showMiddle = false - - if (workflow === undefined || workflow === null) { return null } @@ -146,13 +149,11 @@ const WorkflowValidationTimeline = (props) => { } if (workflow.triggers === undefined || workflow.triggers === null) { - workflow.triggers = [] - + workflow.triggers = [] } if (workflow.branches === undefined || workflow.branches === null) { - workflow.branches = [] - + workflow.branches = [] } var results = [] @@ -260,6 +261,10 @@ const WorkflowValidationTimeline = (props) => { relevantactions.push(...newactions) } + if (relevantactions.length <= 1) { + return null + } + // Sort according to how many parents a node has. MAY be wrong~ relevantactions.sort((a, b) => { if (a.order === undefined) { @@ -279,15 +284,81 @@ const WorkflowValidationTimeline = (props) => { var skipped = false var previousTools = false + var scheduleNotStarted = false + + if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.validation_ran === false) { + console.log("Validation didn't run. Why?") + return null + } + + if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.errors !== undefined && workflow.validation.errors !== null && workflow.validation.errors.length > 0) { + var newErrors = [] + for (var key in workflow.validation.errors) { + const error = workflow.validation.errors[key] + if (error.type === "SCHEDULE") { + scheduleNotStarted = true + continue + } + + newErrors.push(error) + } + + workflow.validation.errors = newErrors + } // Use this variable to control visualization //const showMiddle = false // border: workflow.validation.valid ? `2px solid ${green}` : "1px solid rgba(255,255,255,0.4)", var middleError = "" var startBranchColor = "" + var middleBranchColor = "" + + + const showHoverForClick = showHoverColor === true ? true : false return ( -
+
{ + if (isClicked === false) { + setHovering(true) + } + }} + onMouseLeave={() => { + if (isClicked === false) { + setHovering(false) + } + }} + onClick={() => { + if (showHoverForClick === true) { + setIsClicked(true) + } + }} + > + + {isClicked === false ? null : + + } +
+ + {scheduleNotStarted === true ? + null + : null} + {relevantactions.map((action, index) => { action.result = {} if (results !== undefined) { @@ -309,8 +380,10 @@ const WorkflowValidationTimeline = (props) => { const validate = validateJson(action.result.result) if (validate.valid) { if (validate.result.success === true) { + nodecolor = green branchcolor = green } else { + nodecolor = grey branchcolor = grey } } @@ -319,9 +392,12 @@ const WorkflowValidationTimeline = (props) => { } else if (action.status === "SKIPPED") { branchcolor = grey } else { + // FIXME: How do we handle this? if (action.status === undefined) { - branchcolor = green + nodecolor = grey + branchcolor = grey } else { + nodecolor = red branchcolor = red } } @@ -389,29 +465,64 @@ const WorkflowValidationTimeline = (props) => { } } + var appgroup = [] + if (action.app_name === "shuffle-subflow") { + if (action.status === "SUCCESS") { + nodecolor = green + branchcolor = green + } + + if (workflow.validation.subflow_apps !== undefined && workflow.validation.subflow_apps !== null && workflow.validation.subflow_apps.length > 0) { + nodecolor = red + branchcolor = red + + for (var subflowkey in workflow.validation.subflow_apps) { + const subflowApp = workflow.validation.subflow_apps[subflowkey] + founderror += "- " + subflowApp.error+"\n" + + if (subflowApp.error === action.id) { + appgroup.push(subflowApp) + } + } + } + } + if (!showMiddle && relevantactions.length > 2 && index > 0 && index === relevantactions.length - 2) { if (founderror.length > 0) { middleError += founderror+"\n" + + middleBranchColor = branchcolor } if (index === relevantactions.length-2 && relevantactions.length > 2) { const selectedIcon = middleError.length > 0 ? - + + {middleError} + + }> : null - return ( - selectedIcon - ) + return selectedIcon } else { return null } } + // Returns for anything non-middle + if (relevantactions.length > 2 && index >= 1 && index < relevantactions.length - 2) { + if (founderror.length > 0) { + middleError += founderror+"\n" + } + + return null + } + if (skipped && !lastitem) { nodecolor = grey branchcolor = grey @@ -423,28 +534,14 @@ const WorkflowValidationTimeline = (props) => { branchcolor = nodecolor } - var appgroup = [] if (action.trigger_type === "WEBHOOK") { nodecolor = green branchcolor = green - } else if (action.app_name === "shuffle-subflow") { - if (action.status === "SUCCESS") { - nodecolor = green - branchcolor = green - } - - for (var subflowkey in workflow.validation.subflow_apps) { - const subflowApp = workflow.validation.subflow_apps[subflowkey] - if (subflowApp.error === action.id) { - appgroup.push(subflowApp) - } - } - - } + } + var flex = index !== 0 && index !== relevantactions.length - 1 ? 1 : 3 - if (nodecolor === green) { branchcolor = green } else if (nodecolor === yellow) { @@ -455,12 +552,29 @@ const WorkflowValidationTimeline = (props) => { if (index === 0) { startBranchColor = branchcolor + } else if (index !== 0 && index !== relevantactions.length - 1) { + // FIXME: This doesn't work yet + middleBranchColor = branchcolor } - if (lastitem && middleError.length === 0) { - branchcolor = startBranchColor + if (lastitem) { + if (middleError.length === 0) { + branchcolor = startBranchColor + } else { + //branchcolor = middleBranchColor + } + + if (founderror === "") { + nodecolor = green + } } + // FIXME: This could mean the workflow hasn't ran yet + if (workflow.validation.valid === false && (workflow.validation.errors === undefined || workflow.validation.errors === null || workflow.validation.errors.length == 0) && (workflow.validation.subflow_apps === undefined || workflow.validation.subflow_apps === null || workflow.validation.subflow_apps.length == 0)) { + nodecolor = grey + branchcolor = grey + } + const branchTooltip = branchcolor === yellow ? "Check nodes for errors" : "" const appname = action.app_name.replaceAll('_', ' ').slice(0, 16) @@ -488,6 +602,14 @@ const WorkflowValidationTimeline = (props) => { console.log("MISSING IMAGE: ", appname, image, action) } + if (decidedColor === grey && nodecolor === green) { + setDecidedColor(red) + } + + if (decidedColor !== red && nodecolor === red) { + setDecidedColor(red) + } + return (
{lastitem ? @@ -528,7 +650,7 @@ const WorkflowValidationTimeline = (props) => { : - {founderror.length > 0 ? founderror : `App: ${appname}`} + {founderror.length > 0 ? founderror : `App: ${appname} - Action: ${action.label}`} } placement="top"> diff --git a/frontend/src/context/contextApi.jsx b/frontend/src/context/contextApi.jsx new file mode 100644 index 00000000..b5506a49 --- /dev/null +++ b/frontend/src/context/contextApi.jsx @@ -0,0 +1,23 @@ + +import { createContext, useState } from 'react'; + +export const Context = createContext(); + +export const AppContext =(props) => { + + // Left side bar global states + const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); + const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false); + + + return ( + + {props.children} + + ) +} diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index 6f2455b3..101d8c44 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -467,13 +467,66 @@ const data = [ "font-size": "0px", }, }, - { - selector: "node:selected", - css: { - "border-color": "#f86a3e", - "border-width": "7px", - }, - }, + { + selector: "node:selected", + css: { + "border-color": "#f86a3e", + "border-width": "7px", + }, + }, + { + selector: `node[buttonType="condition-drag"]`, + css: { + "width": "5px", + "height": "5px", + "background-color": "#f85a3e", + }, + }, + { + selector: `node[name="switch"]`, + css: { + label: function(element) { + // Load from the actual element + var nodeheight = 400 + var conditions = [{ + "name": "Condition 1", + "check": "X equals Y", + }, + { + "name": "Condition 2", + "check": "X2 equals Y2", + }, + { + "name": "Condition 3", + "check": "X3 equals Y3", + }] + + conditions.push({ + "name": "Else", + "check": "If all else fails", + }) + + const newlines = nodeheight / conditions.length + console.log("Newlines: ", newlines) + + const label = conditions.map((condition) => { + return `${condition.name}\n\n\n` + }).join("\n") + + return label + }, + color: "white", + "border-color": "#f85a3e", + "background-color": "#1f1f1f", + "font-size": "19px", + "text-margin-x": "-110px", + "text-wrap": "wrap", + shape: "roundrectangle", + width: "100", + height: "300", + + }, + }, ]; //{ diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 8acec204..ed520498 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -6274,7 +6274,7 @@ If you're interested, please let me know a time that works for you, or set up a style={{ minWidth: 50, maxWidth: 50 }} /> ) : ( Not running
) : ( - environment.running_ip.split(":")[0] + environment.running_ip ) ) : ( "N/A" diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index d95c0d9b..d5146d66 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -7,10 +7,10 @@ import { useInterval } from "react-powerhooks"; import { makeStyles, } from "@mui/styles"; import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx" -import { v4 as uuidv4 } from "uuid"; +import { v4 as uuidv4, v5 as uuidv5, validate as isUUID, } from "uuid"; import { useNavigate, Link, useParams } from "react-router-dom"; import { useBeforeunload } from "react-beforeunload" -import ReactJson from "react-json-view"; +import ReactJson from "react-json-view-ssr"; import { NestedMenuItem } from 'mui-nested-menu'; import Markdown from "react-markdown"; //import { useAlert @@ -141,7 +141,7 @@ import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx" -import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; +import { validateJson, collapseField, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; @@ -685,7 +685,6 @@ const releaseToConnectLabel = "Release to Connect" } if (loadedApps.includes(appId)) { - console.log("App already loaded: ", appId) return } @@ -703,7 +702,6 @@ const releaseToConnectLabel = "Release to Connect" 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 @@ -854,7 +852,7 @@ const releaseToConnectLabel = "Release to Connect" } useEffect(() => { - if (workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 && workflow.id === originalWorkflow.id) { + if (workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 && workflow.id === originalWorkflow.id && workflow.parentorg_workflow === "") { setOriginalWorkflow(workflow) } @@ -911,7 +909,7 @@ const releaseToConnectLabel = "Release to Connect" return false }, - preview: false, + preview: true, toggleOffOnLeave: true, loopAllowed: function (node) { return false; @@ -1004,13 +1002,19 @@ const releaseToConnectLabel = "Release to Connect" }, [authenticationModalOpen]) const listOrgCache = (orgId) => { + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + if (orgId !== undefined && orgId !== null && orgId.length > 0) { + headers["Org-Id"] = orgId + } + fetch(`${globalUrl}/api/v1/orgs/${orgId}/list_cache`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", + method: "GET", + headers: headers, + credentials: "include", }) .then((response) => { if (response.status !== 200) { @@ -1238,12 +1242,18 @@ const releaseToConnectLabel = "Release to Connect" }; const setNewAppAuth = (appAuthData, refresh) => { + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { + headers["Org-Id"] = workflow.org_id + } + fetch(globalUrl + "/api/v1/apps/authentication", { method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: headers, body: JSON.stringify(appAuthData), credentials: "include", }) @@ -1873,28 +1883,47 @@ const releaseToConnectLabel = "Release to Connect" continue; } - var type = cyelements[cyelementsKey].data()["type"]; + var type = cyelements[cyelementsKey].data()["type"] if (type === undefined) { - if ( - cyelements[cyelementsKey].data().source === undefined || - cyelements[cyelementsKey].data().target === undefined - ) { - continue; + if (cyelements[cyelementsKey].data().source === undefined || cyelements[cyelementsKey].data().target === undefined) { + continue } + // Get the parent item + var source_attachment = "" + const branchSource = cy.getElementById(cyelements[cyelementsKey].data().source) + if (branchSource === undefined || branchSource === null) { + } else { + const branchSourceData = branchSource.data() + if (branchSourceData !== undefined && branchSourceData !== null && branchSourceData.attachedTo !== undefined) { + source_attachment = branchSourceData.attachedTo + + // Check if it's the 'else' or not based on uuidv5 + const else_attachment = uuidv5(source_attachment, uuidv5.URL) + if (else_attachment === branchSourceData.id) { + source_attachment = source_attachment+"-else" + } + + console.log("Source parent: ", source_attachment) + } + } + var parsedElement = { id: cyelements[cyelementsKey].data().id, source_id: cyelements[cyelementsKey].data().source, destination_id: cyelements[cyelementsKey].data().target, conditions: cyelements[cyelementsKey].data().conditions, decorator: cyelements[cyelementsKey].data().decorator, - }; + + source_parent: source_attachment, + } if (parsedElement.decorator) { - newVBranches.push(parsedElement); + newVBranches.push(parsedElement) } else { - newBranches.push(parsedElement); + newBranches.push(parsedElement) } + } else { if (type === "ACTION") { const cyelement = cyelements[cyelementsKey].data(); @@ -2086,7 +2115,6 @@ const releaseToConnectLabel = "Release to Connect" credentials: "include", }) .then((response) => { - setSavingState(0); if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); } else { @@ -2106,11 +2134,13 @@ const releaseToConnectLabel = "Release to Connect" if (executionArgument !== undefined && startNode !== undefined) { //console.log("Running execution AFTER saving"); + setSavingState(0); executeWorkflow(executionArgument, startNode, true); return; } if (!responseJson.success) { + setSavingState(0); console.log(responseJson); if (responseJson.reason !== undefined && responseJson.reason !== null) { toast("Failed to save: " + responseJson.reason); @@ -2118,6 +2148,7 @@ const releaseToConnectLabel = "Release to Connect" toast("Failed to save. Please contact your support@shuffler.io or your local admin if this is unexpected.") } } else { + setSavingState(1); sendStreamRequest({ "item": "workflow", @@ -2156,7 +2187,6 @@ const releaseToConnectLabel = "Release to Connect" setWorkflow(workflow); } - setSavingState(1); setTimeout(() => { setSavingState(0); }, 1500); @@ -2170,7 +2200,7 @@ const releaseToConnectLabel = "Release to Connect" toast.warn("Failed to save the workflow. Is the network down?") }); - if (originalWorkflow.id === undefined || originalWorkflow.id === null || originalWorkflow.id.length === 0 || useworkflow.id === originalWorkflow.id) { + if (originalWorkflow.id === undefined || originalWorkflow.id === null || originalWorkflow.id.length === 0 || useworkflow.id === originalWorkflow.id && workflow.parentorg_workflow === "") { setOriginalWorkflow(useworkflow) } @@ -2370,13 +2400,20 @@ const releaseToConnectLabel = "Release to Connect" // // - const getAuthGroups = () => { + const getAuthGroups = (orgId) => { + setAuthGroups([]) + var headers = { + "content-type": "application/json", + "accept": "application/json", + } + + if (orgId !== undefined && orgId !== null && orgId.length > 0) { + headers["Org-Id"] = orgId + } + fetch(globalUrl + "/api/v1/authentication/groups", { method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: headers, credentials: "include", }) .then((response) => { @@ -2399,13 +2436,19 @@ const releaseToConnectLabel = "Release to Connect" }) } - const getAppAuthentication = (reset, updateAction, closeMenu) => { + const getAppAuthentication = (reset, updateAction, closeMenu, orgId) => { + var headers = { + "content-type": "application/json", + "accept": "application/json", + } + + if (orgId !== undefined && orgId !== null && orgId.length > 0) { + headers["Org-Id"] = orgId + } + fetch(globalUrl + "/api/v1/apps/authentication", { method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: headers, credentials: "include", }) .then((response) => { @@ -2418,7 +2461,7 @@ const releaseToConnectLabel = "Release to Connect" .then((responseJson) => { var shouldClose = false if (responseJson.success) { - getAuthGroups() + getAuthGroups(orgId) var newauth = []; for (let authkey in responseJson.data) { @@ -2485,13 +2528,11 @@ const releaseToConnectLabel = "Release to Connect" } } - selectedAction.authentication = authenticationOptions; - if ( - selectedAction.selectedAuthentication === null || - selectedAction.selectedAuthentication === undefined || - selectedAction.selectedAuthentication.length === "" - ) { - selectedAction.selectedAuthentication = {}; + console.log("auth options: ", authenticationOptions) + + selectedAction.authentication = authenticationOptions + if (selectedAction.selectedAuthentication === null || selectedAction.selectedAuthentication === undefined || selectedAction.selectedAuthentication.length === "") { + selectedAction.selectedAuthentication = {} } if (appUpdates === true) { @@ -2518,7 +2559,7 @@ const releaseToConnectLabel = "Release to Connect" } } else { - setAppAuthentication([]); + setAppAuthentication([]) shouldClose = true } @@ -2685,13 +2726,19 @@ const releaseToConnectLabel = "Release to Connect" }) } - const getFiles = () => { + const getFiles = (orgId) => { + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + if (orgId !== undefined && orgId !== null && orgId.length > 0) { + headers["Org-Id"] = orgId + } + fetch(globalUrl + "/api/v1/files", { method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: headers, credentials: "include", }) .then((response) => { @@ -3317,12 +3364,18 @@ const releaseToConnectLabel = "Release to Connect" }; const getChildWorkflows = (parentWorkflowId) => { + //toast("Loading child workflows 1 (should be 2)") + + /* if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) { return } + */ const orgId = originalWorkflow.org_id === undefined || originalWorkflow.org_id === null || originalWorkflow.org_id === "" ? "" : originalWorkflow.org_id + //toast("Loading child workflows 2: " + orgId) + fetch(`${globalUrl}/api/v1/workflows/${parentWorkflowId}/child_workflows`, { method: "GET", headers: { @@ -3376,12 +3429,13 @@ const releaseToConnectLabel = "Release to Connect" setTimeout(() => { window.location.pathname = "/workflows"; }, 2000); -} else if (sessionToken !== null && workflow_id === "3abdfb21-b40f-4e50-b855-ac0d62f83cbe") { - toast(`Injecting session token and reloading workflow..`) - setTimeout(() => { - setCookie("session_token", sessionToken, { path: "/" }); - window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe"; - }, 2000); + + } else if (sessionToken !== null && workflow_id === "3abdfb21-b40f-4e50-b855-ac0d62f83cbe") { + toast(`Injecting session token and reloading workflow..`) + setTimeout(() => { + setCookie("session_token", sessionToken, { path: "/" }); + window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe"; + }, 2000) } } } @@ -3392,11 +3446,6 @@ const releaseToConnectLabel = "Release to Connect" }) .then((responseJson) => { // Load as JSON - // - // - //console.log("Got workflow TXT: ", responseText) - //const responseJson = JSON.parse(responseText) - //console.log("Got workflow JSON: ", responseJson) if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id.length > 0 && responseJson.id !== workflow_id) { toast("Workflow ID mismatch. Redirecting to your workflow") navigate(`/workflows/${responseJson.id}`) @@ -3406,7 +3455,6 @@ const releaseToConnectLabel = "Release to Connect" setDistributedFromParent(responseJson.parentorg_workflow) } - if (responseJson.childorg_workflow_ids !== undefined && responseJson.childorg_workflow_ids !== null && responseJson.childorg_workflow_ids.length > 0) { getChildWorkflows(responseJson.id) } @@ -3435,6 +3483,13 @@ const releaseToConnectLabel = "Release to Connect" if (responseJson.org_id !== undefined && responseJson.org_id !== null) { listOrgCache(responseJson.org_id) } + + if (responseJson.sharing !== undefined && responseJson.sharing !== null && (responseJson.sharing === "form" || responseJson.sharing === "forms")) { + if (responseJson.actions === undefined || responseJson.actions === null || responseJson.actions.length === 0) { + navigate("/forms/" + responseJson.id) + toast("Redirecting to Form from Workflow") + } + } // Wait for this to finish fetchRecommendations(responseJson) @@ -3636,7 +3691,10 @@ const releaseToConnectLabel = "Release to Connect" cy.on("add", "node", (e) => onNodeAdded(e)); cy.on("add", "edge", (e) => onEdgeAdded(e)); } else { - setOriginalWorkflow(responseJson) + if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id.length > 0 && responseJson.parentorg_workflow === "") { + setOriginalWorkflow(responseJson) + } + setWorkflow(responseJson); setWorkflowDone(true); @@ -5220,7 +5278,10 @@ const releaseToConnectLabel = "Release to Connect" authenticationOptions[latestindex].last_modified = true } - curaction.authentication = authenticationOptions; + + console.log("auth options 2: ", authenticationOptions) + + curaction.authentication = authenticationOptions if ( curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || @@ -5229,7 +5290,9 @@ const releaseToConnectLabel = "Release to Connect" curaction.selectedAuthentication = {}; } } else { - curaction.authentication = []; + console.log("auth option 3") + + curaction.authentication = [] curaction.authentication_id = ""; curaction.selectedAuthentication = {}; } @@ -5430,7 +5493,7 @@ const releaseToConnectLabel = "Release to Connect" } setRightSideBarOpen(true); - setLastSaved(false); + //setLastSaved(false); setScrollConfig({ top: 0, left: 0, @@ -5771,7 +5834,6 @@ const releaseToConnectLabel = "Release to Connect" setLastSaved(false); const edge = event.target.data(); - console.log("edge added: ", edge) if (edge.source === undefined && edge.target === undefined) { console.log("Edge added without source or target") @@ -5787,9 +5849,15 @@ const releaseToConnectLabel = "Release to Connect" const sourcenode = cy.getElementById(edge.source) const destinationnode = cy.getElementById(edge.target) + if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) { console.log("Source or destination node is undefined or null: ", sourcenode, destinationnode) } else { + if (sourcenode.data("name") === "switch") { + event.target.remove() + return + } + console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data()) if (sourcenode.data("type") === "TRIGGER") { if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") { @@ -5827,7 +5895,7 @@ const releaseToConnectLabel = "Release to Connect" var targetnode = workflow.triggers.findIndex( (data) => data.id === edge.target - ); + ) if (targetnode !== -1) { if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") { console.log("User Input or Shuffle Workflow") @@ -6526,17 +6594,24 @@ const releaseToConnectLabel = "Release to Connect" document.addEventListener("paste", handlePaste); }; - const getEnvironments = () => { + const getEnvironments = (orgId) => { + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + if (orgId !== undefined && orgId !== null && orgId.length > 0) { + headers["Org-Id"] = orgId + } + fetch(globalUrl + "/api/v1/getenvironments", { method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: headers, credentials: "include", }) .then((response) => { if (response.status !== 200) { + console.log("Status not 200 for envs :O!"); if (isCloud) { setEnvironments([{ Name: "Cloud", Type: "cloud" }]); @@ -6642,9 +6717,13 @@ const releaseToConnectLabel = "Release to Connect" } } - return; + return } + if (nodedata.name === "switch") { + return + } + // console.log("nodedata", nodedata); // console.log("nodedata.app_name: ", nodedata.app_name); if (nodedata.app_name !== undefined) { @@ -6654,10 +6733,12 @@ const releaseToConnectLabel = "Release to Connect" for (var nodekey in allNodes) { const currentNode = allNodes[nodekey]; // console.log("Current node: ", currentNode); - if ( - currentNode.data.isButton && - currentNode.data.attachedTo !== nodedata.id - ) { + if (currentNode.data.isButton && currentNode.data.attachedTo !== nodedata.id) { + + if (currentNode.data.buttonType === "condition-drag") { + continue + } + cy.getElementById(currentNode.data.id).remove(); } @@ -6760,6 +6841,146 @@ const releaseToConnectLabel = "Release to Connect" // Maybe it shouldn't be onclick? } + const addConditionDraggers = (event, allElements, branches) => { + const nodedata = event.target.data() + const position = event.target.position() + + var conditions = [] + const foundParam = nodedata.parameters.find((param) => param.name.toLowerCase() === "conditions") + + try { + conditions = JSON.parse(foundParam.value) + } catch (e) { + //toast("Failed parsing conditions: ", e) + } + + // Test conditions + if (conditions === undefined || conditions === null || typeof conditions !== "object") { + return + } + + // Look for if it has the "Else" condition or not + const elseindex = conditions.findIndex((condition) => condition.name.toLowerCase() === "else") + const parentId = nodedata.id + + // Force following of Else at the least + const newId = uuidv5(parentId, uuidv5.URL) + if (elseindex === -1) { + conditions.push({ + name: "Else", + check: "Else", + id: newId, + parent_source: parentId, + }) + } else { + conditions[elseindex].id = newId + } + + // 4 conditions (with else) = 300px -> 75px each + const parentHeight = (conditions.length*75)*0.75 + + + var startheight = -parentHeight/2 + var newnodes = [] + for (let conditionkey in conditions) { + var circleId = conditions[conditionkey].id === undefined ? (newNodeId = uuidv4()) : conditions[conditionkey].id + + // Check if circleId is a valid uuid or not + if (circleId === undefined || circleId === null) { + circleId = uuidv4() + } + + if (!isUUID(circleId)) { + if (conditions[circleId].name !== undefined && conditions[circleId].name !== null) { + circleId = uuidv5(conditions[circleId].name, uuidv5.URL) + } else { + circleId = uuidv4() + conditions[conditionkey].name = circleId + conditions[conditionkey].id = circleId + } + } + + // Check if circleId already exists as a node + if (cy !== undefined && cy !== null) { + const existingNode = cy.getElementById(circleId) + if (existingNode !== undefined && existingNode !== null && existingNode.length > 0) { + continue + } + } + + // 1. Create "small" nodes at each point along the section based on the amount of conditions + // 2. Make these conditions have edgehandles + // 3. Make these conditions have a "drag" handle + const px = position.x + 65 + const py = position.y + startheight + + console.log("Y height: ", startheight) + + const node = { + group: "nodes", + data: { + name: conditions[conditionkey].name, + id: circleId, + buttonType: "condition-drag", + attachedTo: nodedata.id, + is_valid: true, + }, + position: { + x: px, + y: py, + }, + locked: true, + } + + newnodes.push(node) + + // Check if ANY of the incoming branches has the id as source + if (branches !== undefined && branches !== null && branches.length > 0) { + for (let branchkey in branches) { + const branch = branches[branchkey] + if (branch.source_id !== circleId) { + continue + } + + const branchid = uuidv4() + newnodes.push({ + group: "edges", + data: { + id: branchid, + _id: branchid, + + source: circleId, + target: branch.destination_id, + label: branch.label, + conditions: branch.conditions, + hasErrors: branch.has_errors, + decorator: false, + parent_source: parentId, + } + }) + } + } + + startheight = startheight + parentHeight/(conditions.length-1) + } + + if (cy !== undefined && cy !== null) { + cy.add(newnodes) + } else { + var newelements = elements + if (allElements !== undefined) { + newelements = allElements + } + + for (let nodekey in newnodes) { + newelements.push(newnodes[nodekey]) + } + + console.log("ELEMENTS: ", newelements) + setElements(newelements) + } + } + const addCopyButton = (event) => { var parentNode = cy.$("#" + event.target.data("id")); if (parentNode.data("isButton") || parentNode.data("buttonId")) return; @@ -6810,9 +7031,9 @@ const releaseToConnectLabel = "Release to Connect" 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)+"_") { + const parentlabel = parentNode.data("label")?.toLowerCase().replace(" ", "_") + const parentname = parentNode.data("app_name")?.toLowerCase().replace(" ", "_") + if (!parentlabel?.startsWith(parentname)+"_") { return } @@ -7188,10 +7409,11 @@ const releaseToConnectLabel = "Release to Connect" } } - return; + return } + //var parentNode = cy.$("#" + event.target.data("id")); //if (parentNode.data("isButton") || parentNode.data("buttonId")) return; @@ -7244,7 +7466,12 @@ const releaseToConnectLabel = "Release to Connect" // console.log("CURRENT NODE: ", currentNode) if ((currentNode.data.buttonType === "ACTIONSUGGESTION" || currentNode.data.isButton || currentNode.data.isSuggestion) && currentNode.data.attachedTo !== nodedata.id) { - cy.getElementById(currentNode.data.id).remove(); + + if (currentNode.data.buttonType === "condition-drag") { + continue + } + + cy.getElementById(currentNode.data.id).remove() } /*if ( @@ -7259,8 +7486,13 @@ const releaseToConnectLabel = "Release to Connect" } } + if (nodedata.name === "switch") { + addConditionDraggers(event) + return + } + if (!found) { - addDeleteButton(event); + addDeleteButton(event) if (nodedata.type === "TRIGGER") { if (nodedata.trigger_type === "SUBFLOW" || nodedata.trigger_type === "USERINPUT") { @@ -7268,25 +7500,29 @@ const releaseToConnectLabel = "Release to Connect" } else { // Check how many executions from the source addRunCountButton(event); - } - } else { - addCopyButton(event); - addStartnodeButton(event); - } + } + } else { - // autocomplete - // right click - // suggestions - addActionSuggestions(nodedata, event); - - if (workflow.actions.length < 4) { - addSuggestionButtons(nodedata, event); - } else { - //console.log("Too many actions to suggest (for now)") - } + addCopyButton(event); + addStartnodeButton(event); } + + // autocomplete + // right click + // suggestions + addActionSuggestions(nodedata, event); + + if (workflow.actions.length < 4) { + addSuggestionButtons(nodedata, event); + } else { + //console.log("Too many actions to suggest (for now)") + } + } } + if (nodedata.name === "switch") { + return + } var parsedStyle = { "border-width": "7px", @@ -7625,9 +7861,10 @@ const releaseToConnectLabel = "Release to Connect" node.data.example = example; - return node; - }); + return node + }) + // What are these again? Where are they used? const decoratorNodes = inputworkflow.actions.map((action) => { if (!action.isStartNode) { if (action.app_name === "Testing") { @@ -7639,11 +7876,20 @@ const releaseToConnectLabel = "Release to Connect" } } + if (action.id === undefined || action.id === null) { + return null + } + + if (action.position === undefined || action.position === null || action.position.x === undefined || action.position.x === null || action.position.y === undefined || action.position.y === null) { + return null + } + const iconInfo = GetIconInfo(action); const svg_pin = ``; const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); const offset = action.isStartNode ? 36 : 44; + const decoratorNode = { position: { x: action.position.x + offset, @@ -7659,9 +7905,9 @@ const releaseToConnectLabel = "Release to Connect" imageColor: iconInfo.iconBackgroundColor, attachedTo: action.id, }, - }; - return decoratorNode; - }); + } + return decoratorNode + }) const foundtriggers = inputworkflow.triggers.map((trigger) => { @@ -7756,7 +8002,7 @@ const releaseToConnectLabel = "Release to Connect" hasErrors: branch.has_errors, decorator: false, parent_controlled: parentcontrolled, - }; + } // This is an attempt at prettier edges. The numbers are weird to work with. // Bezier curves @@ -7846,6 +8092,28 @@ const releaseToConnectLabel = "Release to Connect" } else { setElements(insertedNodes); } + + const additionalNodes = inputworkflow.actions.map((action) => { + // Looking for: el.data("name") != "switch" + if (action.name !== "switch") { + return null + } + + addConditionDraggers({ + target: { + // Run data() function + data: function() { + return action + }, + position: function() { + return action.position + } + } + }, + insertedNodes, + inputworkflow.branches, + ) + }) } const removeNode = (nodeId) => { @@ -8146,6 +8414,7 @@ const releaseToConnectLabel = "Release to Connect" handleNodes: (el) => { if (el.isNode() && el.data("buttonType") != "ACTIONSUGGESTION" && + el.data("name") != "switch" && !el.data("isButton") && !el.data("isDescriptor") && !el.data("isSuggestion") && @@ -8352,7 +8621,6 @@ const releaseToConnectLabel = "Release to Connect" 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 @@ -8663,7 +8931,7 @@ const releaseToConnectLabel = "Release to Connect" variant="outlined" onClick={() => { setVariablesModalOpen(true); - setLastSaved(false); + setLastSaved(false) setVariableInfo({ "name": "", @@ -9122,6 +9390,7 @@ const releaseToConnectLabel = "Release to Connect" } } } else { + newAppData.authentication = []; newAppData.authentication_id = ""; newAppData.selectedAuthentication = {}; @@ -9256,11 +9525,9 @@ const releaseToConnectLabel = "Release to Connect" } var parsedEnvironments = - environments === null || environments === [] - ? "cloud" - : environments[defaultEnvironmentIndex] === undefined - ? "cloud" - : environments[defaultEnvironmentIndex].Name + environments === undefined || environments === null || environments === [] + ? isCloud ? "cloud" : "Shuffle" : environments[defaultEnvironmentIndex] === undefined + ? isCloud ? "cloud" : "Shuffle" : environments[defaultEnvironmentIndex].Name // Basic automatic auth mapping var authId = "" @@ -9441,7 +9708,6 @@ const releaseToConnectLabel = "Release to Connect" setHover(true) if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) { - console.log("HOVERING: ", app.id) loadAppConfig(app.id, false) } @@ -10264,6 +10530,10 @@ const releaseToConnectLabel = "Release to Connect" // description // ACTION select const selectedNameChange = (appActionName) => { + if (appActionName === undefined || appActionName === null) { + return + } + appActionName = appActionName.replaceAll("(", ""); appActionName = appActionName.replaceAll(")", ""); appActionName = appActionName.replaceAll("]", ""); @@ -11488,12 +11758,12 @@ const releaseToConnectLabel = "Release to Connect" }; setConditionsModalOpen(false); - if (selectedEdge.conditions === undefined) { + if (selectedEdge.conditions === undefined || selectedEdge.conditions === null) { selectedEdge.conditions = [data]; } else { const curedgeindex = selectedEdge.conditions.findIndex( (data) => data.source.id === sourceValue.id - ); + ) if (curedgeindex < 0) { selectedEdge.conditions.push(data); } else { @@ -12565,7 +12835,6 @@ const releaseToConnectLabel = "Release to Connect" // Sets the startnode if (e.target.value.id !== workflow.id && e.target.value.id.length > 0 ) { - console.log("WORKFLOW: ", e.target.value); const startnode = e?.target?.value?.actions?.find((action) => action.id === e.target.value.start); @@ -13424,7 +13693,7 @@ const releaseToConnectLabel = "Release to Connect" } - return "TMP"; + return "Default"; } const newname = ( @@ -15831,7 +16100,7 @@ const releaseToConnectLabel = "Release to Connect" }} />
- Execution argument: + Runtime Argument:
{ - window.location.reload(); - }, 2000); + if (responseJson["reason"] === "SSO_REDIRECT") { + setTimeout(() => { + toast.info("Redirecting to SSO login page as SSO is required for this organization.") + window.location.href = responseJson["url"] + return + }, 2000) + } else { + setTimeout(() => { + window.location.reload(); + }, 2000); + } toast("Successfully changed active organisation - refreshing!"); } else { @@ -16104,70 +16381,95 @@ const releaseToConnectLabel = "Release to Connect" style={{maxHeight: 50, maxWidth: 250, }} labelId="suborg-changer" value={workflow.org_id} + disabled={savingState !== 0} onChange={(e) => { if (workflow.org_id === e.target.value) { console.log("Same org selected. No change.") return + } else { + //if (savingState === 0) { + // saveWorkflow(workflow, undefined, undefined, undefined) + //} } - if (e.target.value === originalWorkflow.org_id) { - console.log("Original org selected. No change.") - - updateCurrentWorkflow(originalWorkflow) - return + // Unselect in cy + if (cy !== undefined && cy !== null) { + cy.nodes().unselect() + cy.edges().unselect() } - // Should look through childorg workflow - if (originalWorkflow.childorg_workflow_ids === undefined || originalWorkflow.childorg_workflow_ids === null || originalWorkflow.childorg_workflow_ids.length === 0) { - console.log("In childorg no exist. Suborgworkflows: ", suborgWorkflows) + ReactDOM.unstable_batchedUpdates(() => { + getEnvironments(e.target.value) + getAppAuthentication(undefined, undefined, undefined, e.target.value) + getFiles(e.target.value) + listOrgCache(e.target.value) - if (suborgWorkflows !== undefined && suborgWorkflows !== null && suborgWorkflows.length > 0) { - var found = false - for (var suborgkey in suborgWorkflows) { - const suborgWorkflow = suborgWorkflows[suborgkey] - if (suborgWorkflow.org_id === e.target.value) { - found = true - updateCurrentWorkflow(suborgWorkflow) - break + if (e.target.value === originalWorkflow.org_id) { + console.log("Original org selected. No change.") + + updateCurrentWorkflow(originalWorkflow) + return + } else { + // Load environments, auth, auth groups + //toast("Loading correct info for suborg") + } + + + // Should look through childorg workflow + console.log("Original: ", originalWorkflow) + if (originalWorkflow.childorg_workflow_ids === undefined || originalWorkflow.childorg_workflow_ids === null || originalWorkflow.childorg_workflow_ids.length === 0) { + console.log("In childorg doesn't exist. Suborgworkflows: ", suborgWorkflows) + + if (suborgWorkflows !== undefined && suborgWorkflows !== null && suborgWorkflows.length > 0) { + var found = false + for (var suborgkey in suborgWorkflows) { + const suborgWorkflow = suborgWorkflows[suborgkey] + if (suborgWorkflow.org_id === e.target.value) { + found = true + updateCurrentWorkflow(suborgWorkflow) + break + } } - } - if (!found) { - console.log("No workflow found out of suborg workflows.") - - saveWorkflow(originalWorkflow, undefined, undefined, e.target.value) + if (!found) { + toast("(3) Creating new workflow for this org. Please wait a second while we duplicate.") + //console.log("No workflow found out of suborg workflows.") + + //saveWorkflow(originalWorkflow, undefined, undefined, e.target.value) + } + } else { + console.log("Suborgworkflows: ", suborgWorkflows) + toast("(1) Loading NEW workflow for this org (?). Please wait a second.") + saveWorkflow(originalWorkflow, undefined, undefined, e.target.value) } } else { - //toast("(1) Creating new workflow for this org. Please wait a second while we duplicate.") - saveWorkflow(originalWorkflow, undefined, undefined, e.target.value) - } - } else { - console.log("In childorg EXIST!") + console.log("In childorg EXIST!") - var workflowFound = false - for (var childorgidkey in originalWorkflow.childorg_workflow_ids) { - const childworkflowid = originalWorkflow.childorg_workflow_ids[childorgidkey] - for (var suborgWorkflowKey in suborgWorkflows) { - const suborgWorkflow = suborgWorkflows[suborgWorkflowKey] - if (suborgWorkflow.org_id === e.target.value) { - workflowFound = true + var workflowFound = false + for (var childorgidkey in originalWorkflow.childorg_workflow_ids) { + const childworkflowid = originalWorkflow.childorg_workflow_ids[childorgidkey] + for (var suborgWorkflowKey in suborgWorkflows) { + const suborgWorkflow = suborgWorkflows[suborgWorkflowKey] + if (suborgWorkflow.org_id === e.target.value) { + workflowFound = true - updateCurrentWorkflow(suborgWorkflow) + updateCurrentWorkflow(suborgWorkflow) + break + } + } + + if (workflowFound) { break } } - if (workflowFound) { - break + if (!workflowFound) { + console.log("No workflow found.") + toast("(2) Creating new workflow for this org. Please wait a few seconds while we prepare it for you.") + //saveWorkflow(originalWorkflow, undefined, undefined, e.target.value) } } - - if (!workflowFound) { - console.log("No workflow found.") - //toast("(2) Creating new workflow for this org. Please wait a few seconds while we prepare it for you.") - saveWorkflow(originalWorkflow, undefined, undefined, e.target.value) - } - } + }) }} label="Suborg Distribution" fullWidth @@ -16242,7 +16544,6 @@ const releaseToConnectLabel = "Release to Connect" } - {authGroups !== undefined && authGroups !== null && authGroups.length > 0 ? { - setLastSaved(false) + //setLastSaved(false) setSelectedAction({}); setSelectedApp({}) setWorkflow(inputworkflow) + if (inputworkflow !== undefined && inputworkflow !== null && inputworkflow.id !== undefined && inputworkflow.id !== null) { + getRevisionHistory(inputworkflow.id) + getWorkflowExecution(inputworkflow.id) + } + // Update props match key if (inputworkflow.parentorg_workflow !== undefined && inputworkflow.parentorg_workflow !== null && inputworkflow.parentorg_workflow !== "") { setDistributedFromParent(inputworkflow.parentorg_workflow) @@ -17272,7 +17577,7 @@ const releaseToConnectLabel = "Release to Connect" @@ -17283,7 +17588,6 @@ const releaseToConnectLabel = "Release to Connect" variant={"outlined"} onClick={() => { setShowWorkflowRevisions(true) - //setOriginalWorkflow(workflow) }} > @@ -17662,7 +17966,8 @@ const releaseToConnectLabel = "Release to Connect"
*/} - {userdata.avatar !== undefined && (userdata.avatar === creatorProfile.github_avatar || allowList.includes(userdata.public_username)) ? + + {userdata.support === true || (userdata.avatar !== undefined && (userdata.avatar === creatorProfile.github_avatar || allowList.includes(userdata.public_username))) ?
You can see these buttons because you may have the correct access rights as a creator to help modify this workflow. @@ -17859,7 +18164,10 @@ const releaseToConnectLabel = "Release to Connect" src={validate.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={true} + collapsed={false} + shouldCollapse={(jsonField) => { + return collapseField(jsonField) + }} iconStyle={theme.palette.jsonIconStyle} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} displayArrayKey={false} @@ -18200,12 +18508,12 @@ const releaseToConnectLabel = "Release to Connect" theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={parsedCollapse} + shouldCollapse={(jsonField) => { + return collapseField(jsonField) + }} iconStyle={theme.palette.jsonIconStyle} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} displayArrayKey={false} - shouldCollapse={(field) => { - console.log("FIELD: ", field) - }} enableClipboard={(copy) => { handleReactJsonClipboard(copy); }} @@ -18876,12 +19184,36 @@ const releaseToConnectLabel = "Release to Connect" - ) : null} + ) : + + + + + + } {isCloud ? @@ -18992,14 +19324,14 @@ const releaseToConnectLabel = "Release to Connect" ) : - executionData.execution_source === "questions" || executionData.execution_source === "web" ? + executionData.execution_source === "questions" || executionData.execution_source === "web" || executionData.execution_source === "form" || executionData.execution_source === "forms" ? - Questions + Form : executionData.execution_source @@ -19492,7 +19824,10 @@ const releaseToConnectLabel = "Release to Connect" src={validate.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={true} + collapsed={false} + shouldCollapse={(jsonField) => { + return collapseField(jsonField) + }} iconStyle={theme.palette.jsonIconStyle} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} displayArrayKey={false} @@ -19652,6 +19987,9 @@ const releaseToConnectLabel = "Release to Connect" theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={data.value.length < 10000 ? false : true} + shouldCollapse={(jsonField) => { + return collapseField(jsonField) + }} iconStyle={theme.palette.jsonIconStyle} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} displayArrayKey={false} @@ -20072,6 +20410,9 @@ const releaseToConnectLabel = "Release to Connect" theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} collapsed={selectedResult.result.length < 10000 ? false : true} + shouldCollapse={(jsonField) => { + return collapseField(jsonField) + }} iconStyle={theme.palette.jsonIconStyle} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} displayArrayKey={false} @@ -20791,13 +21132,13 @@ const releaseToConnectLabel = "Release to Connect" } } - console.log("Action: ", selectedAction); selectedAction.authentication_id = authenticationOption.id; selectedAction.selectedAuthentication = authenticationOption; - if ( - selectedAction.authentication === undefined || - selectedAction.authentication === null - ) { + + console.log("auth option 4: ", authenticationOption) + + if (selectedAction.authentication === undefined || selectedAction.authentication === null) { + selectedAction.authentication = [authenticationOption] } else { @@ -20808,7 +21149,7 @@ const releaseToConnectLabel = "Release to Connect" } } - setSelectedAction(selectedAction); + setSelectedAction(selectedAction) var newAuthOption = JSON.parse(JSON.stringify(authenticationOption)); var newFields = []; @@ -20821,21 +21162,18 @@ const releaseToConnectLabel = "Release to Connect" }); } - newAuthOption.fields = newFields; - setNewAppAuth(newAuthOption); + newAuthOption.fields = newFields + setNewAppAuth(newAuthOption) if (configureWorkflowModalOpen) { - setSelectedAction({}); + setSelectedAction({}) } - setUpdate(authenticationOption.id); - }; + setUpdate(authenticationOption.id) + } - if ( - authenticationOption.label === null || - authenticationOption.label === undefined - ) { - authenticationOption.label = selectedApp.name + " authentication"; + if (authenticationOption.label === null || authenticationOption.label === undefined) { + authenticationOption.label = selectedApp.name + " authentication"; } return ( @@ -20857,7 +21195,7 @@ const releaseToConnectLabel = "Release to Connect"
These are required fields for authenticating with {selectedApp.name}
- Name - what is this used for? + Label for you to remember
+ + + + + - - {selectedApp.description} - +
+ + {selectedApp.description} + +
- There is no Shuffle-specific documentation for this app yet. Documentation is written custom for each app, and is a community effort. Hope to see your contribution + There is no Shuffle-specific documentation for this app yet outside of the general description above. Documentation is written for each api, and is a community effort. We hope to see your contribution!
: null} + - Version History (Beta) + Version History -
+
@@ -21972,8 +22351,6 @@ const releaseToConnectLabel = "Release to Connect" const changeActionParameterCodeMirror = (event, count, data, actionlist) => { // Check if event.target.value is an array. If it is, split with comma - console.log("1 - SELECTED ACTION: ", selectedAction) - console.log("1 - DATA: ", data) if (data.startsWith("${") && data.endsWith("}")) { // PARAM FIX - Gonna use the ID field, even though it's a hack diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index c9e0b218..4ae3c702 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -544,7 +544,7 @@ const AppCreator = (defaultprops) => { }; - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); useEffect(() => { if (window.location.pathname.includes("apps/edit")) { @@ -897,8 +897,31 @@ const AppCreator = (defaultprops) => { }; if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) { + console.log("LABEL: ", methodvalue["x-label"]) + + var correctlabel = "" + const labels = methodvalue["x-label"].split(",") + for (let labelkey in labels) { + var label = labels[labelkey].trim() + if (label.toLowerCase() === "no label") { + continue + } + + // Remove quotes and escapes + label = label.replace(/['"]+/g, '') + label = label.replace(/\\/g, '') + + //label = label.replace("_", " ", -1) + //label = label.charAt(0).toUpperCase() + label.slice(1) + + correctlabel = label + break + } + + console.log("LABEL: ", correctlabel) // FIX: Map labels only if they're actually in the category list - newaction.action_label = methodvalue["x-label"] + //newaction.action_label = methodvalue["x-label"] + newaction.action_label = correctlabel } if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) { @@ -3124,15 +3147,14 @@ const AppCreator = (defaultprops) => { Scopes for Oauth2 { required style={{ marginTop: 0, backgroundColor: inputColor }} fullWidth={true} - placeholder="Field Name (key, NOT your actual API-key)" + placeholder="The Key to use as the header/query - NOT your actual API-key" type="name" id="standard-required" margin="normal" @@ -4441,7 +4463,7 @@ const AppCreator = (defaultprops) => { setUpdate(Math.random()) } }} - value={data.action_label} + value={data?.action_label?.replace(" ", "_").toLowerCase()} style={{ border: data.action_label === undefined || data.action_label === "No Label" ? "" : `2px solid ${bgColor}`, borderRadius: theme.shape.borderRadius, @@ -4463,7 +4485,7 @@ const AppCreator = (defaultprops) => { return ( diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 1c1ca152..1b14fe20 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -3035,7 +3035,7 @@ const Apps = (props) => { height: "50px", }} variant="contained" - disabled={openApi.length === 0 || appValidation.length > 0} + disabled={openApi.length === 0 || appValidation.length > 0 || validation} color="primary" onClick={() => { setOpenApiError(""); diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index 94b113f3..57b761a3 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -11,6 +11,7 @@ import { useNavigate, Link, useParams } from "react-router-dom"; //import { useAlert import { ToastContainer, toast } from "react-toastify" import Draggable from "react-draggable"; +import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; import { Autocomplete, @@ -18,6 +19,8 @@ import { TextField, IconButton, Button, + Select, + MenuItem, Typography, Grid, Paper, @@ -146,28 +149,8 @@ const inputdata = [ const LineChartWrapper = ({keys, height, width}) => { const [hovered, setHovered] = useState(""); - //console.log("Date: ", new Date("2019-11-14T08:00:00.000Z")) - console.log("Keys: ", keys) var inputdata = keys.data - /* - const inputdata = [{ - "key": "Intel", - "data": [ - { key: new Date('11/22/2019'), data: 3, metadata: {color: "orange", "name": "Intel"}}, - { key: new Date('11/24/2019'), data: 8, metadata: {color: "orange", "name": "Intel"}}, - { key: new Date('11/29/2019'), data: 2, metadata: {color: "orange", "name": "Intel"}}, - ]}, - { - "key": "Popper", - "data": [ - { key: new Date('11/24/2019'), data: 9, }, - { key: new Date('11/29/2019'), data: 3, }, - ] - } - ] - */ - return (
@@ -211,7 +194,6 @@ const LineChartWrapper = ({keys, height, width}) => { offset: '5px, 5px' }} content={(data, color) => { - console.log("DATA: ", data) const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No" return ( @@ -350,12 +332,35 @@ const Dashboard = (props) => { const [frameworkData, setFrameworkData] = useState(undefined); const [widgetData, setWidgetData] = useState([]); + const [newWidgetData, setNewWidgetData] = useState([]); + + const [, setUpdate] = useState(0); let navigate = useNavigate(); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + useEffect(() => { + const widgetnames = ["app_executions_cloud"] + for (let widgetkey in widgetnames) { + const widgetName = widgetnames[widgetkey] + + console.log("NAME: ", widgetName) + + const resp = LoadStats(globalUrl, widgetName) + if (resp !== undefined) { + resp.then((data) => { + console.log("Got data in parent: ", data) + if (data === undefined) { + } else { + newWidgetData.push(data) + setNewWidgetData(newWidgetData) + } + }) + } + } + }, []) useEffect(() => { if (selectedUsecaseCategory.length === 0) { @@ -368,6 +373,7 @@ const Dashboard = (props) => { } }, [selectedUsecaseCategory]) + const checkSelectedParams = () => { const urlSearchParams = new URLSearchParams(window.location.search) const params = Object.fromEntries(urlSearchParams.entries()) @@ -409,23 +415,22 @@ const Dashboard = (props) => { }, [usecases]) const getWidget = (dashboard, widget) => { - fetch(`${globalUrl}/api/v1/dashboards/${dashboard}/widgets/${widget}`, { - 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!"); - } + fetch(`${globalUrl}/api/v1/dashboards/${dashboard}/widgets/${widget}`, { + 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) => { - console.log("Resp: ", responseJson) + return response.json(); + }) + .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { //toast("Failed loading: " + responseJson.reason) @@ -441,14 +446,12 @@ const Dashboard = (props) => { } const foundWidget = widgetData.findIndex(data => data.title === widget) - console.log("Found: ", foundWidget) if (foundWidget !== undefined && foundWidget !== null && foundWidget >= 0) { widgetData[foundWidget] = tmpdata } else { widgetData.push(tmpdata) } - console.log("Data: ", widgetData) setWidgetData(widgetData) } }) @@ -480,7 +483,7 @@ const Dashboard = (props) => { useEffect(() => { getWidget("main", "Overall") getWidget("main", "Overall2") - }, []); + }, []) const fetchdata = (stats_id) => { fetch(globalUrl + "/api/v1/stats/" + stats_id, { @@ -646,7 +649,6 @@ const Dashboard = (props) => { stats["workflow_executions"].data !== undefined ) { setStatsRan(true); - //console.log("NEW DATA?: ", stats) console.log("SET WORKFLOW: ", stats["workflow_executions"]); //var curday = startDate.getDate() @@ -729,6 +731,100 @@ const Dashboard = (props) => {
) : null; + const WidgetController = (props) => { + const { data, index, availableStats, } = props + const [hovering, setHovering] = useState(false) + + const newname = data.key !== undefined ? data.key.replaceAll("_", " ") : "" + + console.log("KEYDATA: ", data) + + const loadNewStats = (newkey) => { + const resp = LoadStats(globalUrl, newkey) + if (resp !== undefined) { + resp.then((respdata) => { + if (respdata === undefined || respdata === null) { + toast("Failed to laod data. Please try again, or contact support@shuffler.io if this persists.") + } else { + newWidgetData[index] = respdata + setNewWidgetData(newWidgetData) + + setUpdate(Math.random()) + } + }) + } + } + + return ( + + { + setHovering(true) + }} + onMouseLeave={() => { + setHovering(false) + }} + > +
+ + {newname} + + + {data.available_keys === undefined || data.available_keys === null || data.available_keys.length === 0 ? null : + + } +
+ +
+
+ ) + } + const data = (
@@ -739,12 +835,25 @@ const Dashboard = (props) => { : null}
- {widgetData === undefined || widgetData === null || widgetData === [] || widgetData.length === 0 ? null : + {/*widgetData === undefined || widgetData === null || widgetData === [] || widgetData.length === 0 ? null : + */} + + {newWidgetData === undefined || newWidgetData === null || newWidgetData === [] ? null : + newWidgetData.map((data, index) => { + + return ( + + ) + }) }
); diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 9536dad7..e05a7d90 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useRef, 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 ReactJson from "react-json-view-ssr"; import { isMobile } from "react-device-detect"; import { BrowserView, MobileView } from "react-device-detect"; import { useParams, useNavigate, Link } from "react-router-dom"; @@ -152,9 +152,21 @@ export const OuterLink = (props) => { export const Img = (props) => { + // Find parent container and check width + + var height = "auto" + var width = 750 + if (props.height !== undefined && props.height !== null) { + height = props.height + } + + if (props.width !== undefined && props.width !== null) { + width = props.width + } + return( {props.alt} diff --git a/frontend/src/views/RunWorkflow.jsx b/frontend/src/views/RunWorkflow.jsx index 8dbc019a..9d741300 100644 --- a/frontend/src/views/RunWorkflow.jsx +++ b/frontend/src/views/RunWorkflow.jsx @@ -2,20 +2,24 @@ import React, {useState, useEffect} from 'react'; import ReactDOM from "react-dom" +import ReactJson from "react-json-view-ssr"; import { green, yellow, red, grey} from "./AngularWorkflow.jsx"; import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx"; import { useNavigate, Link, useParams } from "react-router-dom"; -import { validateJson, GetIconInfo } from "./Workflows.jsx"; +import { validateJson, collapseField, GetIconInfo } from "./Workflows.jsx"; import EditWorkflow from "../components/EditWorkflow.jsx" -import { ToastContainer, toast } from "react-toastify" +import { toast } from "react-toastify" import { makeStyles } from '@mui/material/styles'; import { useInterval } from "react-powerhooks"; import { isMobile } from "react-device-detect"; import Markdown from "react-markdown"; import theme from '../theme.jsx'; +import rehypeRaw from "rehype-raw"; +import RecentWorkflow from "../components/RecentWorkflow.jsx"; import { - Tooltip, + Tooltip, + Select, IconButton, CircularProgress, TextField, @@ -28,11 +32,17 @@ import { Dialog, DialogTitle, DialogContent, + MenuItem, } from '@mui/material'; import { Preview as PreviewIcon, ContentCopy as ContentCopyIcon, + ArrowBack as ArrowBackIcon, + ArrowForward as ArrowForwardIcon, + Lock as LockIcon, + LockOpen as LockOpenIcon, + OpenInNew as OpenInNewIcon, } from '@mui/icons-material'; const hrefStyle = { @@ -45,7 +55,7 @@ const bodyDivStyle = { width: isMobile? "100%":"500px", position: "relative", - marginTop: 25, + paddingBottom: 250, } @@ -68,12 +78,31 @@ const RunWorkflow = (defaultprops) => { const [foundSourcenode, setFoundSourcenode] = React.useState(undefined); const [editWorkflowModalOpen, setEditWorkflowModalOpen] = React.useState(false) const [sharingOpen, setSharingOpen] = React.useState(false) + const [realtimeMarkdown, setRealtimeMarkdown] = React.useState("") + const [forms, setForms] = React.useState([]) + + const IframeWrapper = (props) => { + var propsCopy = JSON.parse(JSON.stringify(props)) + propsCopy.width = 400 + propsCopy.height = 225 + + return