Merge pull request #1309 from Shuffle/1.4.0

1.3.2 release
This commit is contained in:
Frikky
2024-01-31 10:58:46 +01:00
committed by GitHub
39 changed files with 3602 additions and 1425 deletions
+7 -4
View File
@@ -46,15 +46,17 @@ OUTER_HOSTNAME=shuffle-backend
DB_LOCATION=./shuffle-database
DOCKER_API_VERSION=1.40
# Proxy configurations. SHUFFLE_PASS_WORKER_PROXY must be FALSE to not pass the proxy information to sub-apps.
# PS: It will skip proxy for
# Orborus/Proxy configurations
HTTP_PROXY=
HTTPS_PROXY=
SHUFFLE_PASS_WORKER_PROXY=TRUE
SHUFFLE_PASS_APP_PROXY=FALSE
SHUFFLE_PASS_WORKER_PROXY=TRUE # Decides if proxy configurations should be passed to workers or not
SHUFFLE_PASS_APP_PROXY=TRUE # Decides if proxy configurations should be passed to apps or not. Requires SHUFFLE_PASS_WORKER_PROXY=true
SHUFFLE_INTERNAL_HTTP_PROXY= # Used to differentiate proxies for shuffle internal vs external traffic
SHUFFLE_INTERNAL_HTTPS_PROXY=
TZ=Europe/Amsterdam # Timezone-handler in Orborus, Worker and Apps
ORBORUS_CONTAINER_NAME= # Used to FIND the containername. cgroup v2: issue 501
SHUFFLE_ORBORUS_STARTUP_DELAY= # Used for setting up a startup delay for Orborus
SHUFFLE_SKIPSSL_VERIFY=true
IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not
SHUFFLE_BASE_IMAGE_NAME=shuffle
@@ -79,6 +81,7 @@ SHUFFLE_DISABLE_RERUN_AND_ABORT=false
SHUFFLE_RERUN_SCHEDULE=300
SHUFFLE_WORKER_SERVER_URL= # Definition in case Worker & Orborus is talking to the wrong server
SHUFFLE_ORBORUS_PULL_TIME= # Definition in case Orborus is pulling too often/not often enough
SHUFFLE_MAX_EXECUTION_DEPTH= # Max recursion depth for subflows
# DATABASE CONFIGURATIONS
DATASTORE_EMULATOR_HOST=shuffle-database:8000
+5 -5
View File
@@ -19,23 +19,23 @@ jobs:
include:
- app: frontend
path: frontend
version: 1.3.1
version: 1.3.2
experimental: true
- app: backend
path: backend
version: 1.3.1
version: 1.3.2
experimental: true
- app: app_sdk
path: backend/app_sdk
version: 1.3.1
version: 1.3.2
experimental: true
- app: orborus
path: functions/onprem/orborus
version: 1.3.1
version: 1.3.2
experimental: true
- app: worker
path: functions/onprem/worker
version: 1.3.1
version: 1.3.2
experimental: true
steps:
- name: Checkout
+122 -40
View File
@@ -333,6 +333,16 @@ class AppBase:
if len(os.getenv("SHUFFLE_INTERNAL_NO_PROXY", "")) > 0:
self.proxy_config["no_proxy"] = os.getenv("SHUFFLE_INTERNAL_NO_PROXY", "")
try:
if self.proxy_config["http"].lower() == "noproxy":
self.proxy_config["http"] = ""
if self.proxy_config["https"].lower() == "noproxy":
self.proxy_config["https"] = ""
except Exception as e:
self.logger.info(f"[DEBUG] Failed setting proxy config: {e}. NOT important if running apps with webserver. This is NOT critical.")
self.logger.info(f"[DEBUG] Proxy config: {self.proxy_config}")
if isinstance(self.action, str):
try:
self.action = json.loads(self.action)
@@ -525,6 +535,7 @@ class AppBase:
try:
finished = False
ret = {}
for i in range (0, 10):
# Random sleeptime between 0 and 1 second, with 0.1 increments
sleeptime = float(random.randint(0, 10) / 10)
@@ -532,22 +543,23 @@ class AppBase:
try:
ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False, proxies=self.proxy_config)
self.logger.info(f"[DEBUG] Result: {ret.status_code} (break on 200 or 201)")
self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} (break on 200/201) & Response= {ret.text}. Action status: {action_result["status"]}""")
if ret.status_code == 200 or ret.status_code == 201:
finished = True
break
else:
self.logger.info(f"[ERROR] Bad resp {ret.status_code}: {ret.text}")
time.sleep(sleeptime)
# Proxyerrror
except requests.exceptions.ProxyError as e:
self.logger.info(f"[ERROR] Proxy error: {e}")
self.logger.info(f"[ERROR] Proxy error for url {url}: {e}")
self.proxy_config = {}
continue
except requests.exceptions.RequestException as e:
self.logger.info(f"[DEBUG] Request problem: {e}")
self.logger.info(f"[DEBUG] Request problem for url {url}: {e}")
time.sleep(sleeptime)
# Check if we have a read timeout. If we do, exit as we most likely sent the result without getting a good result
@@ -599,7 +611,6 @@ class AppBase:
self.send_result(action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams")
return
self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} & Response= {ret.text}. Action status: {action_result["status"]}""")
except requests.exceptions.ConnectionError as e:
self.logger.info(f"[DEBUG] Unexpected ConnectionError happened: {e}")
except TypeError as e:
@@ -692,7 +703,7 @@ class AppBase:
except (KeyError, NameError) as e:
self.logger.info(f"""Key/NameError in param handler for {param["name"]}: {e}""")
self.logger.info(f"[DEBUG] OUTER VALUE: {param_value}")
#self.logger.info(f"[DEBUG] OUTER VALUE: {param_value}")
if len(param_value) > 0:
md5 = hashlib.md5(param_value.encode('utf-8')).hexdigest()
values.append(md5)
@@ -1168,7 +1179,7 @@ class AppBase:
#ret = ret[0]
self.logger.info("[DEBUG] DONT make list of 1 into 0!!")
self.logger.info("Return from execution: %s" % ret)
#self.logger.info("Return from execution: %s" % ret)
if ret == None:
results.append("")
json_object = False
@@ -1192,11 +1203,12 @@ class AppBase:
except:
results.append(ret)
if len(results) == 1:
#results = results[0]
self.logger.info("DONT MAKE LIST FROM 1 TO 0!!")
#if len(results) == 1:
# #results = results[0]
# #self.logger.info("DONT MAKE LIST FROM 1 TO 0!!")
# pass
self.logger.info("\nLOOP: %s\nRESULTS: %s" % (loop_wrapper, results))
#self.logger.info("\nLOOP: %s\nRESULTS: %s" % (loop_wrapper, results))
return results
# Downloads all files from a namespace
@@ -1567,9 +1579,10 @@ class AppBase:
"execution_id": self.current_execution_id
}
self.logger.info("[ERROR] Before FULLEXEC stream result")
resultsurl = "%s/api/v1/streams/results" % (self.base_url)
#self.logger.info("[DEBUG] Before FULLEXEC stream result url '%s'" % (resultsurl))
ret = requests.post(
"%s/api/v1/streams/results" % (self.base_url),
resultsurl,
headers=headers,
json=tmpdata,
verify=False,
@@ -1591,7 +1604,7 @@ class AppBase:
continue
else:
self.logger.info("[ERROR] Error in app with status code %d for results (2). Crashing because results can't be handled" % ret.status_code)
self.logger.info("[ERROR] (fails: %d) Error in app with status code %d for results (2). Crashing because results can't be handled. Details: %s" % (i+1, ret.status_code, ret.text))
rettext = ret.text
failed = True
@@ -1612,11 +1625,21 @@ class AppBase:
self.logger.info("[ERROR] FullExec Connectionerror: %s" % e)
self.action_result["result"] = json.dumps({
"success": False,
"reason": f"Connection error during startup: {e}"
"reason": f"Connection error during startup (connection error): {e}"
})
self.send_result(self.action_result, headers, stream_path)
return
except Exception as e:
self.logger.info("[ERROR] FullExec Exception outer: %s" % e)
self.action_result["result"] = json.dumps({
"success": False,
"reason": f"Exception during startup of app (general error): {e}"
})
self.send_result(self.action_result, headers, stream_path)
return
else:
self.logger.info(f"[DEBUG] Setting execution to default value with type {type(self.full_execution)}")
try:
@@ -2008,7 +2031,9 @@ class AppBase:
outercnt = 0
# Loops over split values
splitcnt = -1
for value in parsersplit:
splitcnt += 1
#if " " in value:
# value = value.replace(" ", "_", -1)
@@ -2016,6 +2041,10 @@ class AppBase:
# Goes here if loop
if value == "#":
newvalue = []
if basejson == None:
return "", False
for innervalue in basejson:
# 1. Check the next item (message)
# 2. Call this function again
@@ -2049,9 +2078,9 @@ class AppBase:
# Means it's a single item -> continue
if seconditem == "":
print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson)))
if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end":
if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end":
firstitem = len(basejson)-1
elif firstitem.lower() == "min" or firstitem.lower() == "first":
elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
@@ -2075,9 +2104,9 @@ class AppBase:
firstitem = int(firstitem)
if isinstance(seconditem, str):
if seconditem.lower() == "max" or seconditem.lower() == "last" or firstitem.lower() == "end":
if str(seconditem).lower() == "max" or str(seconditem).lower() == "last" or str(firstitem).lower() == "end":
seconditem = len(basejson)-1
elif seconditem.lower() == "min" or seconditem.lower() == "first":
elif str(seconditem).lower() == "min" or str(seconditem).lower() == "first":
seconditem = 0
else:
seconditem = int(seconditem)
@@ -2113,6 +2142,12 @@ class AppBase:
if isinstance(basejson, list):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
try:
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
@@ -2132,22 +2167,67 @@ class AppBase:
elif " " in value:
value = value.replace(" ", "_", -1)
if isinstance(basejson, list):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value])
try:
print("[DEBUG] BASEJSON: %s" % basejson)
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
try:
if isinstance(basejson, list):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value])
try:
print("[DEBUG] BASEJSON: %s" % basejson)
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
return str(basejson[value]), False
except json.decoder.JSONDecodeError as e:
print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value])
return str(basejson[value]), False
except json.decoder.JSONDecodeError as e:
print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value])
return str(basejson[value]), False
else:
basejson = basejson[value]
else:
basejson = basejson[value]
except KeyError as e:
print("\n\n[WARNING] Running third dot notation fix that always find the correct value %s: %s" % (value, e))
try:
currentsplitcnt = splitcnt
recursed_value = value
handled = False
while True:
newvalue = parsersplit[currentsplitcnt+1]
if newvalue == "#" or newvalue == "":
break
recursed_value += "." + newvalue
found = False
for key, value in basejson.items():
if recursed_value.lower() in key.lower():
found = True
if found == False:
print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value)
break
if recursed_value in basejson:
print("[INFO] FOUND RECURSED VALUE: ", recursed_value)
basejson = basejson[recursed_value]
handled = True
break
currentsplitcnt += 1
if handled:
continue
break
except IndexError as e:
print("[DEBUG] INDEXERROR: ", parsersplit[outercnt])
break
outercnt += 1
@@ -2155,6 +2235,9 @@ class AppBase:
except KeyError as e:
print("[INFO] Lower keyerror: %s" % e)
return "", False
except Exception as e:
print("[WARNING] Exception: %s" % e)
return basejson, False
#return basejson
#return "KeyError: Couldn't find key: %s" % e
@@ -2682,7 +2765,7 @@ class AppBase:
#self.logger.info(f"\n\nType of value: {type(value)}")
if isinstance(value, str):
# Could we take it here?
self.logger.info(f"[DEBUG] Got value %s for parameter {paramname}" % value)
#self.logger.info(f"[DEBUG] Got value %s for parameter {paramname}" % value)
# Should check if there is are quotes infront of and after the to_be_replaced
# If there are, then we need to sanitize the value
# 1. Look for the to_be_replaced in the data
@@ -2694,7 +2777,6 @@ class AppBase:
# value = returnvalue
parameter["value"] = parameter["value"].replace(to_be_replaced, value)
elif isinstance(value, dict) or isinstance(value, list):
# Changed from JSON dump to str() 28.05.2021
@@ -3225,7 +3307,7 @@ class AppBase:
pass
self.logger.info(f"""HANDLING BODY: {action["parameters"][counter]["value"]}""")
#self.logger.info(f"""HANDLING BODY: {action["parameters"][counter]["value"]}""")
action["parameters"][counter]["value"] = recurse_cleanup_script(action["parameters"][counter]["value"])
#self.logger.info(action["parameters"])
@@ -3265,8 +3347,8 @@ class AppBase:
"exception": f"Value Error: {check}",
}))
if parameter["name"] == "body":
self.logger.info(f"[INFO] Should debug field with liquid and other checks as it's BODY: {value}")
#if parameter["name"] == "body":
# #self.logger.info(f"[INFO] Should debug field with liquid and other checks as it's BODY: {value}")
# Custom format for ${name[0,1,2,...]}$
#submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})"
@@ -3655,9 +3737,9 @@ class AppBase:
timeout_env = os.getenv("SHUFFLE_APP_SDK_TIMEOUT", timeout)
try:
timeout = int(timeout_env)
self.logger.info(f"[DEBUG] Timeout set to {timeout} seconds")
#self.logger.info(f"[DEBUG] Timeout set to {timeout} seconds")
except Exception as e:
self.logger.info(f"[WARNING] Failed parsing timeout to int: {e}")
self.logger.info(f"[ERROR] Failed parsing timeout to int: {e}")
#timeout = 30
+251
View File
@@ -0,0 +1,251 @@
## A test script of the recurse_json function
## to validate that it can handle the different types of data
## and follow the dot formation format
import re
import json
def recurse_json(basejson, parsersplit):
match = "#([0-9a-z]+):?-?([0-9a-z]+)?#?"
try:
outercnt = 0
# Loops over split values
splitcnt = -1
for value in parsersplit:
splitcnt += 1
#if " " in value:
# value = value.replace(" ", "_", -1)
actualitem = re.findall(match, value, re.MULTILINE)
# Goes here if loop
if value == "#":
newvalue = []
if basejson == None:
return "", False
for innervalue in basejson:
# 1. Check the next item (message)
# 2. Call this function again
try:
ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:])
except IndexError:
# Only in here if it's the last loop without anything in it?
ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:])
newvalue.append(ret)
# Magical way of returning which makes app sdk identify
# it as multi execution
return newvalue, True
# Checks specific regex like #1-2 for index 1-2 in a loop
elif len(actualitem) > 0:
is_loop = True
newvalue = []
firstitem = actualitem[0][0]
seconditem = actualitem[0][1]
if isinstance(firstitem, int):
firstitem = str(firstitem)
if isinstance(seconditem, int):
seconditem = str(seconditem)
#print("[DEBUG] ACTUAL PARSED: %s" % actualitem)
# Means it's a single item -> continue
if seconditem == "":
print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson)))
if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end":
firstitem = len(basejson)-1
elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
print(f"[DEBUG] Post lower checks with item {firstitem}")
tmpitem = basejson[int(firstitem)]
try:
newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:])
except IndexError:
newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:])
else:
print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem))
if isinstance(firstitem, str):
if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end":
firstitem = len(basejson)-1
elif firstitem.lower() == "min" or firstitem.lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
else:
firstitem = int(firstitem)
if isinstance(seconditem, str):
if str(seconditem).lower() == "max" or str(seconditem).lower() == "last" or str(firstitem).lower() == "end":
seconditem = len(basejson)-1
elif str(seconditem).lower() == "min" or str(seconditem).lower() == "first":
seconditem = 0
else:
seconditem = int(seconditem)
else:
seconditem = int(seconditem)
print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}")
newvalue = []
if int(seconditem) > len(basejson):
seconditem = len(basejson)
for i in range(int(firstitem), int(seconditem)+1):
# 1. Check the next item (message)
# 2. Call this function again
try:
ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:])
except IndexError:
print("[DEBUG] INDEXERROR: ", parsersplit[outercnt])
#ret = innervalue
ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:])
newvalue.append(ret)
return newvalue, is_loop
else:
print("IN ELSE WITH VALUE: %s" % value)
if len(value) == 0:
return basejson, False
try:
print("PRINT:", basejson)
if isinstance(basejson, list):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
try:
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
# Should we sanitize here?
print("[DEBUG] VALUE TO SANITIZE?: %s" % basejson[value])
return str(basejson[value]), False
except json.decoder.JSONDecodeError as e:
return str(basejson[value]), False
else:
basejson = basejson[value]
except KeyError as e:
print("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e))
if "_" in value:
value = value.replace("_", " ", -1)
elif " " in value:
value = value.replace(" ", "_", -1)
try:
if isinstance(basejson, list):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value])
try:
print("[DEBUG] BASEJSON: %s" % basejson)
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
return str(basejson[value]), False
except json.decoder.JSONDecodeError as e:
print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value])
return str(basejson[value]), False
else:
basejson = basejson[value]
except KeyError as e:
print("\n\n[WARNING] Running third dot notation fix %s: %s" % (value, e))
try:
currentsplitcnt = splitcnt
recursed_value = value
handled = False
while True:
newvalue = parsersplit[currentsplitcnt+1]
if newvalue == "#" or newvalue == "":
break
recursed_value += "." + newvalue
print("\n\nRECURSED: ", recursed_value)
found = False
for key, value in basejson.items():
if recursed_value.lower() in key.lower():
found = True
if found == False:
print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value)
break
if recursed_value in basejson:
print("[INFO] FOUND RECURSED VALUE: ", recursed_value)
basejson = basejson[recursed_value]
handled = True
break
currentsplitcnt += 1
if handled:
continue
break
except IndexError as e:
print("[DEBUG] INDEXERROR: ", parsersplit[outercnt])
break
outercnt += 1
except KeyError as e:
print("[INFO] Lower keyerror: %s" % e)
return "", False
except Exception as e:
print("[WARNING] Exception: %s" % e)
return basejson, False
#return basejson
#return "KeyError: Couldn't find key: %s" % e
return basejson, False
print("[INFO] Starting")
#input_data = "test"
#input_data = "test2.data"
#input_data = "test2.test3.data"
input_data = "test2.test5.data.hello"
parsersplit = input_data.split(".")
basejson = {
"test": "hello",
"test2": {
"data": "hello2",
"test3.data": "hello3",
"test4.data.testing": {
"value": "hello4"
},
"test5.data.hello": "wut"
},
}
ret, is_loop = recurse_json(basejson, parsersplit)
print("\n\nOUTPUT RET (%s): %s" % (input_data, ret))
+1
View File
@@ -821,6 +821,7 @@ func handleRemoteDownloadApp(resp http.ResponseWriter, ctx context.Context, user
return
}
defer newresp.Body.Close()
respBody, err := ioutil.ReadAll(newresp.Body)
if err != nil {
log.Printf("[ERROR] Failed setting respbody for workflow download: %s", err)
+18 -16
View File
@@ -13,13 +13,13 @@ require (
github.com/frikky/kin-openapi v0.42.0
github.com/fsouza/go-dockerclient v1.9.7
github.com/ghodss/yaml v1.0.0
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.7.0
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.11.0
github.com/gorilla/mux v1.8.0
github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.5.30
golang.org/x/crypto v0.14.0
github.com/shuffle/shuffle-shared v0.5.65
golang.org/x/crypto v0.16.0
google.golang.org/api v0.125.0
google.golang.org/grpc v1.55.0
gopkg.in/src-d/go-git.v4 v4.13.1
@@ -34,10 +34,11 @@ require (
cloud.google.com/go/compute v1.19.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.0.1 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/adrg/strutil v0.2.3 // indirect
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect
@@ -46,19 +47,20 @@ require (
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/containerd v1.6.18 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.2.2 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-github/v28 v28.1.1 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@@ -88,21 +90,21 @@ require (
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/src-d/gcfg v1.4.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opencensus.io v0.24.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
+159
View File
@@ -47,6 +47,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@@ -65,10 +67,14 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek=
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
@@ -81,6 +87,7 @@ github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jV
github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
@@ -108,6 +115,7 @@ github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQ
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y=
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 h1:9bAydALqAjBfPHd/eAiJBHnMZUYov8m2PkXVr+YGQeI=
github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA14J0sA3Hph4dt+AfCjPrYR13+vVodshQSM7km9qw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -138,6 +146,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -153,6 +163,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@@ -180,13 +192,19 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
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=
@@ -195,12 +213,17 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
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/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -253,6 +276,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -276,9 +301,11 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -329,11 +356,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/libgit2/git2go/v34 v34.0.0/go.mod h1:blVco2jDAw6YTXkErMMqzHLcAjKkwF0aWIRHBqiJkZ0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -341,6 +370,7 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
@@ -363,13 +393,44 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/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/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
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/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
@@ -388,6 +449,7 @@ github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtb
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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=
@@ -395,7 +457,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
@@ -422,12 +487,30 @@ github.com/shuffle/shuffle-shared v0.5.14 h1:d14u1e4k+qKgnf4Insq4x2S+0MMKlDqdyTT
github.com/shuffle/shuffle-shared v0.5.14/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.29 h1:n4vThl7v3mFVXbrIW71XREFdmZZo7mOBAWxnsdiNjDk=
github.com/shuffle/shuffle-shared v0.5.29/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.30 h1:ORWjQU3UJhdZY5mRsAoR2hvftNcJmiEekNKjbaMo7K8=
github.com/shuffle/shuffle-shared v0.5.30/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.31 h1:OV4IIfKWWFW66WjGvyXOmmsSz3p8pW9L1ge1mDo8ftM=
github.com/shuffle/shuffle-shared v0.5.31/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.44 h1:6WiFPIsij+IWvXY7vzVX7cUicb+PYOzhTbWF/gDmYeU=
github.com/shuffle/shuffle-shared v0.5.44/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.60 h1:R0BWYp/DlgyNPU0xVNSPhsWBnJ+LeVD3iLoDRZmtKMo=
github.com/shuffle/shuffle-shared v0.5.60/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0=
github.com/shuffle/shuffle-shared v0.5.61 h1:Fkd7pvk8ypwaio1VvMOmJ6PQF9xCfNYx8RWjPnXjn5E=
github.com/shuffle/shuffle-shared v0.5.61/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0=
github.com/shuffle/shuffle-shared v0.5.62 h1:L1la7++aqPLPsh1z3cuGIzH+NTuvBlmEU/T2mQvfEk8=
github.com/shuffle/shuffle-shared v0.5.62/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0=
github.com/shuffle/shuffle-shared v0.5.65 h1:x4ZM+e0LK21rRtG0xbtnUb+6qU08+zNBQ6azUyhn8ck=
github.com/shuffle/shuffle-shared v0.5.65/go.mod h1:oIZkx93Z7EvtiTXty7xO+ax63Fjz8MQvjXMxDN0Qws0=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE=
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -446,12 +529,14 @@ 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 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
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/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
@@ -463,6 +548,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
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=
@@ -475,6 +561,7 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
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=
@@ -483,14 +570,21 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -524,9 +618,16 @@ 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 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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=
@@ -562,16 +663,29 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
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-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -592,9 +706,12 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
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 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
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/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=
@@ -634,32 +751,57 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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=
@@ -670,10 +812,16 @@ 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 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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=
@@ -729,12 +877,21 @@ golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4X
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 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -851,6 +1008,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -910,6 +1068,7 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs=
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+68 -68
View File
@@ -22,11 +22,8 @@ import (
"net/url"
"os"
"os/exec"
"path/filepath"
// import httptest
"net/http/httptest"
"strings"
"time"
@@ -34,7 +31,7 @@ import (
"github.com/frikky/kin-openapi/openapi2conv"
"github.com/frikky/kin-openapi/openapi3"
"github.com/go-git/go-billy/v5"
//"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
@@ -46,10 +43,6 @@ import (
"golang.org/x/crypto/bcrypt"
"gopkg.in/yaml.v3"
// PROXY overrides
//"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
// githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
// Web
"github.com/gorilla/mux"
http2 "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
@@ -569,7 +562,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
currentOrg := user.ActiveOrg
if user.ActiveOrg.Id == "" {
log.Printf("[WARNING] There's no active org for the user %s. Checking if there's a single one to assing it to.", user.Username)
log.Printf("[WARNING] There's no active org for the user %s. Checking if there's a single one to assign it to.", user.Username)
orgs, err := shuffle.GetAllOrgs(ctx)
if err == nil && len(orgs) > 0 {
@@ -627,10 +620,46 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
err = createNewUser(data.Username, data.Password, role, apikey, currentOrg)
if err != nil {
log.Printf("[WARNING] Failed registering user: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return
if strings.Contains(err.Error(), "already exists") {
// Assign it to the org
log.Printf("[WARNING] User %s already exists. Assigning to org %s", data.Username, currentOrg.Name)
// Get the user
users, err := shuffle.FindUser(ctx, data.Username)
if err != nil || len(users) == 0 {
log.Printf("[WARNING] Failed finding user %s: %s", data.Username, err)
resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return
}
newUser := users[0]
if !shuffle.ArrayContains(newUser.Orgs, currentOrg.Id) {
newUser.Orgs = append(newUser.Orgs, currentOrg.Id)
}
if newUser.ActiveOrg.Id == "" || newUser.ActiveOrg.Name == "" {
newUser.ActiveOrg = currentOrg
}
err = shuffle.SetUser(ctx, &newUser, true)
if err != nil {
log.Printf("[WARNING] Failed updating the user %s: %s", data.Username, err)
resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return
}
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
log.Printf("[INFO] %s Successfully re-added to org %s (%s)", data.Username, currentOrg.Name, currentOrg.Id)
return
} else {
log.Printf("[WARNING] Failed registering user: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return
}
}
resp.WriteHeader(200)
@@ -3055,61 +3084,13 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
buildSwaggerApp(resp, body, user, false)
}
// Creates osfs from folderpath with a basepath as directory base
func createFs(basepath, pathname string) (billy.Filesystem, error) {
log.Printf("[INFO] MemFS base: %s, pathname: %s", basepath, pathname)
fs := memfs.New()
err := filepath.Walk(pathname,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if strings.Contains(path, ".git") {
return nil
}
// Fix the inner path here
newpath := strings.ReplaceAll(path, pathname, "")
fullpath := fmt.Sprintf("%s%s", basepath, newpath)
switch mode := info.Mode(); {
case mode.IsDir():
err = fs.MkdirAll(fullpath, 0644)
if err != nil {
log.Printf("Failed making folder: %s", err)
}
case mode.IsRegular():
srcData, err := ioutil.ReadFile(path)
if err != nil {
log.Printf("Src error: %s", err)
return err
}
dst, err := fs.Create(fullpath)
if err != nil {
log.Printf("Dst error: %s", err)
return err
}
_, err = dst.Write(srcData)
if err != nil {
log.Printf("Dst write error: %s", err)
return err
}
}
return nil
})
return fs, err
}
// Hotloads new apps from a folder
func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error {
basepath := "base"
fs, err := createFs(basepath, location)
fs, err := shuffle.CreateFs(basepath, location)
if err != nil {
log.Printf("Failed memfs creation - probably bad path: %s", err)
return errors.New(fmt.Sprintf("Failed to find directory %s", location))
@@ -3845,7 +3826,9 @@ func runInitEs(ctx context.Context) {
}
// FIXME: Have this for all envs in all orgs (loop and find).
if len(parsedApikey) > 0 {
if len(parsedApikey) == 0 {
log.Printf("[WARNING] No apikey found for cleanup. Skipping cleanup schedule.")
} else {
cleanupSchedule := 300
if len(os.Getenv("SHUFFLE_RERUN_SCHEDULE")) > 0 {
@@ -3860,8 +3843,25 @@ func runInitEs(ctx context.Context) {
}
}
environments := []string{"Shuffle"}
log.Printf("[DEBUG] Starting schedule setup for execution cleanup every %d seconds. Running first immediately.", cleanupSchedule)
environments := []string{defaultEnv}
// Comma separated list of RERUN environments
if len(os.Getenv("SHUFFLE_RERUN_ENVIRONMENTS")) > 0 {
foundenv := strings.Split(os.Getenv("SHUFFLE_RERUN_ENVIRONMENTS"), ",")
for i, env := range foundenv {
if len(env) == 0 {
continue
}
environments[i] = strings.TrimSpace(env)
if !shuffle.ArrayContains(environments, env) {
environments = append(foundenv, env)
}
}
}
log.Printf("[DEBUG] Starting schedule setup for execution cleanup every %d seconds. Running first immediately. Environments: %#v", cleanupSchedule, environments)
cleanupJob := func() func() {
return func() {
log.Printf("[INFO] Running schedule for cleaning up or re-running unfinished workflows in %d environments.", len(environments))
@@ -3931,8 +3931,6 @@ func runInitEs(ctx context.Context) {
} else {
_ = jobret
}
} else {
log.Printf("[DEBUG] Couldn't find a valid API-key, hence couldn't run cleanup")
}
// Getting apps to see if we should initialize a test
@@ -4947,10 +4945,12 @@ func initHandlers() {
r.HandleFunc("/api/v1/files", shuffle.HandleGetFiles).Methods("GET", "OPTIONS")
// Introduced in 0.9.21 to handle notifications for e.g. failed Workflow
r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/notifications/clear", shuffle.HandleClearNotifications).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS")
//r.HandleFunc("/api/v1/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/users/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/notifications/clear", shuffle.HandleClearNotifications).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS")
+26 -8
View File
@@ -106,7 +106,10 @@ func createSchedule(ctx context.Context, scheduleId, workflowId, name, startNode
}
log.Printf("[INFO] Starting frequency for execution: %d", newfrequency)
jobret, err := newscheduler.Every(newfrequency).Seconds().NotImmediately().Run(job)
//jobret, err := newscheduler.Every(newfrequency).Seconds().NotImmediately().Run(job)
jobret, err := newscheduler.Every(newfrequency).Seconds().Run(job)
if err != nil {
log.Printf("Failed to schedule workflow: %s", err)
return err
@@ -305,8 +308,13 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) {
environment = env.OrgId
}
if request.Method == "POST" {
if rand.Intn(1) == 0 {
// FIXME: Workflow stats disabled for now
// as it caused too many problems
// goal: track docker stuff once a minute and graph it
// For now: Disable this as it caused too many problems
if request.Method == "POST" && true == false {
//log.Printf("[DEBUG] POST to workflowqueue")
if rand.Intn(10) == 0 {
// Parse out body
body, err := ioutil.ReadAll(request.Body)
if err == nil {
@@ -924,13 +932,15 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) {
}
}
log.Printf("[DEBUG] Attempting to delete the workflow %s from the database...", fileId)
err = shuffle.DeleteKey(ctx, "workflow", fileId)
if err != nil {
log.Printf("[DEBUG]] Failed deleting key %s", fileId)
resp.WriteHeader(401)
log.Printf("[DEBUG] Failed deleting workflow key %s", fileId)
resp.WriteHeader(400)
resp.Write([]byte(`{"success": false, "reason": "Failed deleting key"}`))
return
}
log.Printf("[INFO] Should have deleted workflow %s (%s)", workflow.Name, fileId)
cacheKey := fmt.Sprintf("%s_workflows", user.Id)
@@ -1048,7 +1058,15 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
return shuffle.WorkflowExecution{}, fmt.Sprintf(`workflow %s is invalid`, workflow.ID), errors.New("Failed getting workflow")
}
workflowExecution, execInfo, _, err := shuffle.PrepareWorkflowExecution(ctx, workflow, request, 10)
maxExecutionDepth := 10
if os.Getenv("SHUFFLE_MAX_EXECUTION_DEPTH") != "" {
maxExecutionDepthNew, err := strconv.Atoi(os.Getenv("SHUFFLE_MAX_EXECUTION_DEPTH"))
if err == nil && maxExecutionDepthNew > 1 && maxExecutionDepthNew < 1000 {
maxExecutionDepth = maxExecutionDepthNew
}
}
workflowExecution, execInfo, _, err := shuffle.PrepareWorkflowExecution(ctx, workflow, request, int64(maxExecutionDepth))
if err != nil {
err = shuffle.SetWorkflowExecution(ctx, workflowExecution, true)
if err != nil {
@@ -1059,7 +1077,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
// Special for user input callbacks
return workflowExecution, fmt.Sprintf("%s", err), nil
} else {
log.Printf("[ERROR] Failed in prepareExecution: %s", err)
log.Printf("[ERROR] Failed in prepareExecution: '%s'", err)
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed starting workflow: %s", err), err
}
}
@@ -1884,7 +1902,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
}
}
log.Printf("[AUDIT] Starting execution of workflow '%s' by user %s (%s)!", fileId, user.Username, user.Id)
log.Printf("[AUDIT] Starting execution of workflow '%s' by user '%s' (%s). If this is empty, it's most likely a subflow!", fileId, user.Username, user.Id)
user.ActiveOrg.Users = []shuffle.UserMini{}
workflow.ExecutingOrg = user.ActiveOrg
+66 -46
View File
@@ -1,54 +1,64 @@
{
"name": "shuffler",
"homepage": "https://shuffler.io",
"version": "1.3.0",
"version": "1.3.3",
"private": true,
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@codemirror/commands": "^6.2.4",
"@emotion/is-prop-valid": "^1.1.1",
"@codemirror/lang-python": "^6.1.3",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@emotion/styled": "^11.6.0",
"@lezer/highlight": "^1.1.3",
"@metamask/detect-provider": "^1.2.0",
"@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0",
"@mui/styles": "^5.14.0",
"@mui/x-data-grid": "^5.17.11",
"@mui/x-date-pickers": "^6.11.1",
"@types/algoliasearch": "^3.34.11",
"@uiw/codemirror-theme-basic": "^4.21.20",
"@uiw/codemirror-theme-vscode": "^4.21.20",
"@uiw/codemirror-themes": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9",
"@use-it/interval": "^1.0.0",
"algoliasearch": "^4.13.1",
"calculate-size": "^1.1.1",
"class-transformer": "^0.4.0",
"@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",
"cpx": "^1.5.0",
"create-react-app": "^5.0.1",
"cytoscape": "^3.15.1",
"cytoscape-edgehandles": "^3.6.0",
"cytoscape-node-html-label": "^1.1.5",
"d3": "^7.1.1",
"dayjs": "^1.11.9",
"cytoscape": "^3.23.0",
"cytoscape-clipboard": "^2.2.1",
"cytoscape-cxtmenu": "^3.4.0",
"cytoscape-edgehandles": "^3.5.1",
"cytoscape-grid-guide": "~2.3.3",
"cytoscape-node-html-label": "^1.2.2",
"cytoscape-panzoom": "^2.5.3",
"cytoscape-undo-redo": "^1.3.3",
"d3": "~4.10.0",
"dayjs": "^1.11.10",
"dotenv": "^6.1.0",
"downshift": "^3.3.5",
"downshift": "^3.4.8",
"express": "^4.17.1",
"express-useragent": "^1.0.15",
"github-markdown-css": "^3.0.1",
"i18next": "^22.3.0",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-chained-backend": "^4.2.0",
"i18next-http-backend": "^2.1.1",
"i18next-localstorage-backend": "^4.1.0",
"i18next-xhr-backend": "^3.2.2",
"import": "0.0.6",
"interweave": "^11.2.0",
"jss": "^10.10.0",
"jss-camel-case": "^6.1.0",
"jss-default-unit": "^8.0.2",
"jss-global": "^3.0.0",
"jss-nested": "^6.0.1",
"jss-props-sort": "^6.0.0",
"jss-vendor-prefixer": "^8.0.1",
"is-plain-obj": "^4.1.0",
"json-bigint": "^1.0.0",
"match-sorter": "^6.3.1",
"md5-file": "^4.0.0",
"mdbreact": "^4.21.1",
"mime": "^3.0.0",
"moment": "^2.29.1",
"moment": "~2.29.4",
"mui-chips-input": "^2.1.3",
"mui-nested-menu": "^3.2.1",
"process": "^0.11.10",
"react": "^18.2.0",
"react-alert": "^7.0.3",
"react-alert-template-basic": "^1.0.0",
"react-alice-carousel": "^2.6.4",
"react-avatar-editor": "^11.1.0",
"react-beforeunload": "^2.2.1",
@@ -57,31 +67,40 @@
"react-cytoscapejs": "^2.0.0",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
"react-draggable": "^3.3.2",
"react-draggable": "^4.4.5",
"react-driftjs": "^1.2.2",
"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.4",
"reaviz": "^14.9.7",
"remark-gfm": "^3.0.1",
"remark-html": "^16.0.1",
"remark-images": "^4.0.0",
"remark-rehype": "^11.0.0",
"rsuite": "^5.23.0",
"search-insights": "^2.2.1",
"shellwords": "^0.1.1",
"shellwords": "^1.0.1",
"simplebar": "^4.2.3",
"styled-components": "^4.4.0",
"webpack": "^5.88.2",
"yaml": "^1.7.2",
"styled-components": "^4.4.1",
"sync-fetch": "^0.3.0",
"terser-webpack-plugin": "^4.2.3",
"yaml": "^1.10.0",
"yamljs": "^0.3.0",
"zone.js": "^0.13.1"
"zone.js": "~0.8.26"
},
"scripts": {
"start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start",
@@ -92,12 +111,7 @@
"lint_file": "eslint 'src/views/AngularWorkflow.jsx'"
},
"eslintConfig": {
"extends": "react-app",
"rules": {
"jsx-a11y/img-redundant-alt": "off",
"no-redeclare": "off",
"no-loop-func": "off"
}
"extends": "react-app"
},
"browserslist": [
">0.2%",
@@ -106,13 +120,19 @@
"not op_mini all"
],
"devDependencies": {
"@babel/core": "^7.15.8",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"babel-eslint": "^10.1.0",
"prettier": "2.4.1",
"@babel/cli": "^7.22.10",
"@babel/core": "^7.22.10",
"@babel/plugin-proposal-export-default-from": "^7.22.5",
"@babel/preset-flow": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"babel-loader": "^8.0.5",
"babel-plugin-css-modules-transform": "^1.6.2",
"babel-plugin-react-css-modules": "^5.2.6",
"babel-plugin-transform-imports": "^2.0.0",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"promise-window": "^1.2.1",
"react-16": "npm:react@16.13.1",
"react-dom-16": "npm:react-dom@16.13.1",
"react-error-overlay": "6.0.9"
"react-hot-loader": "^4.13.0",
"webpack-cli": "^5.1.4"
}
}
@@ -0,0 +1,6 @@
<svg width="131" height="28" viewBox="0 0 131 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 2L-1.62024e-08 16.6087L4.80057 16.6087L4.80057 6.87052L24 6.87052L24 2L0 2Z" fill="#FF8444"/>
<path d="M19.1994 11.3912L19.1994 21.1294L-1.08006e-08 21.1294L-1.62024e-08 25.9999L24 25.9999L24 11.3912L19.1994 11.3912Z" fill="#FF8444"/>
<path d="M14.608 11.3914L9.39062 11.3914L9.39062 16.6088L14.608 16.6088L14.608 11.3914Z" fill="#FF8444"/>
<path d="M47.752 5.92C49.176 5.92 50.408 6.192 51.448 6.736C52.488 7.264 53.368 8.048 54.088 9.088L52.36 10.744C51.752 9.816 51.064 9.152 50.296 8.752C49.544 8.336 48.648 8.128 47.608 8.128C46.84 8.128 46.208 8.232 45.712 8.44C45.216 8.648 44.848 8.928 44.608 9.28C44.384 9.616 44.272 10 44.272 10.432C44.272 10.928 44.44 11.36 44.776 11.728C45.128 12.096 45.776 12.384 46.72 12.592L49.936 13.312C51.472 13.648 52.56 14.16 53.2 14.848C53.84 15.536 54.16 16.408 54.16 17.464C54.16 18.44 53.896 19.288 53.368 20.008C52.84 20.728 52.104 21.28 51.16 21.664C50.232 22.048 49.136 22.24 47.872 22.24C46.752 22.24 45.744 22.096 44.848 21.808C43.952 21.52 43.168 21.128 42.496 20.632C41.824 20.136 41.272 19.568 40.84 18.928L42.616 17.152C42.952 17.712 43.376 18.216 43.888 18.664C44.4 19.096 44.992 19.432 45.664 19.672C46.352 19.912 47.112 20.032 47.944 20.032C48.68 20.032 49.312 19.944 49.84 19.768C50.384 19.592 50.792 19.336 51.064 19C51.352 18.648 51.496 18.232 51.496 17.752C51.496 17.288 51.336 16.88 51.016 16.528C50.712 16.176 50.136 15.904 49.288 15.712L45.808 14.92C44.848 14.712 44.056 14.416 43.432 14.032C42.808 13.648 42.344 13.184 42.04 12.64C41.736 12.08 41.584 11.456 41.584 10.768C41.584 9.872 41.824 9.064 42.304 8.344C42.8 7.608 43.512 7.024 44.44 6.592C45.368 6.144 46.472 5.92 47.752 5.92ZM57.3904 22V4.6H59.9584V11.92C60.3584 11.168 60.8944 10.624 61.5664 10.288C62.2544 9.936 63.0144 9.76 63.8464 9.76C64.6944 9.76 65.4544 9.928 66.1264 10.264C66.8144 10.584 67.3584 11.072 67.7584 11.728C68.1584 12.384 68.3584 13.216 68.3584 14.224V22H65.7904V15.04C65.7904 13.856 65.5424 13.032 65.0464 12.568C64.5664 12.104 63.9584 11.872 63.2224 11.872C62.7104 11.872 62.1984 12 61.6864 12.256C61.1904 12.496 60.7744 12.896 60.4384 13.456C60.1184 14 59.9584 14.736 59.9584 15.664V22H57.3904ZM76.5218 22.24C75.7378 22.24 75.0098 22.096 74.3378 21.808C73.6818 21.52 73.1538 21.056 72.7538 20.416C72.3538 19.776 72.1538 18.944 72.1538 17.92V10H74.7218V17.272C74.7218 18.36 74.9538 19.112 75.4178 19.528C75.8818 19.928 76.5298 20.128 77.3618 20.128C77.7618 20.128 78.1458 20.064 78.5138 19.936C78.8978 19.792 79.2418 19.576 79.5458 19.288C79.8498 18.984 80.0818 18.6 80.2418 18.136C80.4178 17.672 80.5058 17.128 80.5058 16.504V10H83.0738V22H80.8178L80.6978 20.056C80.2818 20.808 79.7218 21.36 79.0178 21.712C78.3138 22.064 77.4818 22.24 76.5218 22.24ZM92.1738 4.432C92.6698 4.432 93.1178 4.488 93.5178 4.6C93.9338 4.696 94.2458 4.832 94.4538 5.008L93.9738 6.856C93.7978 6.744 93.5978 6.664 93.3738 6.616C93.1498 6.552 92.8938 6.52 92.6058 6.52C91.9498 6.52 91.4298 6.672 91.0458 6.976C90.6778 7.28 90.4938 7.776 90.4938 8.464V10.216L90.5658 10.864V22H87.9978V8.272C87.9978 7.664 88.0858 7.12 88.2617 6.64C88.4538 6.16 88.7338 5.76 89.1018 5.44C89.4698 5.104 89.9098 4.856 90.4218 4.696C90.9498 4.52 91.5338 4.432 92.1738 4.432ZM94.3818 10V12.016H85.7418V10H94.3818ZM101.678 4.432C102.174 4.432 102.622 4.488 103.022 4.6C103.438 4.696 103.75 4.832 103.958 5.008L103.478 6.856C103.302 6.744 103.102 6.664 102.878 6.616C102.654 6.552 102.398 6.52 102.11 6.52C101.454 6.52 100.934 6.672 100.55 6.976C100.182 7.28 99.9978 7.776 99.9978 8.464V10.216L100.07 10.864V22H97.5018V8.272C97.5018 7.664 97.5898 7.12 97.7657 6.64C97.9578 6.16 98.2378 5.76 98.6058 5.44C98.9738 5.104 99.4138 4.856 99.9258 4.696C100.454 4.52 101.038 4.432 101.678 4.432ZM103.886 10V12.016H95.2458V10H103.886ZM108.662 4.6V18.64C108.662 19.232 108.766 19.64 108.974 19.864C109.182 20.088 109.526 20.2 110.006 20.2C110.294 20.2 110.534 20.184 110.726 20.152C110.934 20.104 111.19 20.024 111.494 19.912L111.206 21.832C110.934 21.96 110.622 22.056 110.27 22.12C109.918 22.2 109.566 22.24 109.214 22.24C108.142 22.24 107.35 21.968 106.838 21.424C106.342 20.864 106.094 20.008 106.094 18.856V4.6H108.662ZM118.967 22.24C117.767 22.24 116.711 21.992 115.799 21.496C114.887 21 114.175 20.288 113.663 19.36C113.167 18.416 112.919 17.296 112.919 16C112.919 14.704 113.167 13.592 113.663 12.664C114.175 11.72 114.879 11 115.775 10.504C116.671 10.008 117.687 9.76 118.823 9.76C119.991 9.76 120.983 10 121.799 10.48C122.615 10.96 123.239 11.616 123.671 12.448C124.103 13.28 124.319 14.224 124.319 15.28C124.319 15.568 124.311 15.84 124.295 16.096C124.279 16.352 124.255 16.576 124.223 16.768H114.575V14.8H123.095L121.823 15.184C121.823 14.096 121.551 13.264 121.007 12.688C120.463 12.096 119.719 11.8 118.775 11.8C118.087 11.8 117.487 11.96 116.975 12.28C116.463 12.6 116.071 13.08 115.799 13.72C115.527 14.344 115.391 15.112 115.391 16.024C115.391 16.92 115.535 17.68 115.823 18.304C116.111 18.928 116.519 19.4 117.047 19.72C117.575 20.04 118.199 20.2 118.919 20.2C119.719 20.2 120.367 20.048 120.863 19.744C121.359 19.44 121.751 19.016 122.039 18.472L124.079 19.432C123.791 20.008 123.399 20.512 122.903 20.944C122.423 21.36 121.847 21.68 121.175 21.904C120.503 22.128 119.767 22.24 118.967 22.24Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

+4 -10
View File
@@ -8,8 +8,8 @@ import Workflows from "./views/Workflows";
import GettingStarted from "./views/GettingStarted";
import AngularWorkflow from "./views/AngularWorkflow.jsx";
//import Header from "./components/NewHeader.jsx";
import Header from "./components/Header.jsx";
import Header from "./components/NewHeader.jsx";
//import Header from "./components/Header.jsx";
import theme from "./theme";
import Apps from "./views/Apps";
import AppCreator from "./views/AppCreator";
@@ -37,7 +37,6 @@ import UpdateAuthentication from "./views/UpdateAuthentication.jsx";
import FrameworkWrapper from "./views/FrameworkWrapper.jsx";
import ScrollToTop from "./components/ScrollToTop";
import AlertTemplate from "./components/AlertTemplate";
import { useAlert, positions, Provider } from "react-alert";
import { isMobile } from "react-device-detect";
import RuntimeDebugger from "./components/RuntimeDebugger.jsx"
@@ -160,10 +159,6 @@ const App = (message, props) => {
// Dumb for content load (per now), but good for making the site not suddenly reload parts (ajax thingies)
const options = {
timeout: 9000,
position: positions.BOTTOM_LEFT,
};
const handleFirstInteraction = (event) => {
console.log("First interaction: ", event)
@@ -245,6 +240,7 @@ const App = (message, props) => {
setCookie={setCookie}
cookies={cookies}
checkLogin={checkLogin}
notifications={notifications}
{...props}
/>
}
@@ -566,9 +562,7 @@ const App = (message, props) => {
<CssBaseline />
<CookiesProvider>
<BrowserRouter>
<Provider template={AlertTemplate} {...options}>
{includedData}
</Provider>
{includedData}
</BrowserRouter>
<ToastContainer
position="bottom-center"
+1
View File
@@ -1230,6 +1230,7 @@ const Billing = (props) => {
</Typography>
</div>
<BillingStats
isCloud={isCloud}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
userdata={userdata}
+327 -51
View File
@@ -1,7 +1,14 @@
import React, { useState, useEffect } from 'react';
import classNames from "classnames";
import theme from '../theme.jsx';
import classNames from "classnames";
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
import {
DatePicker,
DateTimePicker,
LocalizationProvider,
} from '@mui/x-date-pickers'
import {
Tooltip,
@@ -52,12 +59,14 @@ import {
LinearXAxisTickLabel,
} from 'reaviz';
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
const LineChartWrapper = ({keys, inputname, height, width}) => {
const [hovered, setHovered] = useState("");
const inputdata = keys.data === undefined ? keys : keys.data
return (
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, }}>
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
<Typography variant="h6" style={{marginBotton: 15, }}>
{inputname}
</Typography>
@@ -75,16 +84,174 @@ const LineChartWrapper = ({keys, inputname, height, width}) => {
const AppStats = (defaultprops) => {
const { globalUrl, selectedOrganization, userdata, } = defaultprops;
const { globalUrl, selectedOrganization, userdata, isCloud, } = defaultprops;
const [keys, setKeys] = useState([])
const [searches, setSearches] = useState([]);
const [clickData, setClickData] = useState(undefined);
const [conversionData, setConversionData] = useState(undefined);
const [statistics, setStatistics] = useState(undefined);
const [appRuns, setAppruns] = useState(undefined);
const [appRunCosts, setApprunCosts] = useState(undefined);
const [workflowRuns, setWorkflowRuns] = useState(undefined);
const [subflowRuns, setSubflowRuns] = useState(undefined);
const [endTime, setEndTime] = useState("")
const [startTime, setStartTime] = useState("")
const [statistics, setStatistics] = useState(undefined);
const [filteredStatistics, setFilteredStatistics] = useState(undefined);
const [apprunCost, setApprunCost] = useState(0)
const [monthToDateCost, setMonthToDateCost] = useState(0)
const [monthTotalCost, setMonthTotalCost] = useState(0)
const includedExecutions = selectedOrganization.sync_features.app_executions !== undefined ? selectedOrganization.sync_features.app_executions.limit : 0
// Cost in old contracts: 0.0009
// Old contracts also always included 150.000 executions
const invocationCost = includedExecutions === 150000 || includedExecutions === 250000 ? 0.0009 : typecost_single
const defaultAmount = 10000
useEffect(() => {
if (statistics === undefined || statistics === null) {
return
}
if (statistics["daily_statistics"] === undefined || statistics["daily_statistics"] === null) {
setFilteredStatistics(statistics)
return
}
// Calculate month to date cost
var mtd_cost = 0
for (let key in statistics["daily_statistics"]) {
const item = statistics["daily_statistics"][key]
if (item["date"] === undefined) {
continue
}
const date = new Date(item["date"])
const today = new Date()
if (date.getMonth() === today.getMonth()) {
mtd_cost += (item["app_executions"] * invocationCost)
}
}
if (isCloud && mtd_cost !== monthToDateCost) {
// Find how many days there have been in the current month
const today = new Date()
const daysInMonth = new Date(today.getFullYear(), today.getMonth()+1, 0).getDate()
// Find what day we are on
const day = today.getDate()
// Find how many days are left in the month
const daysLeft = daysInMonth - day
// Calculate the cost of the entire month
var monthTotalCost = mtd_cost/day*daysInMonth
monthTotalCost -= defaultAmount*invocationCost
monthTotalCost -= includedExecutions*invocationCost
// Remove included amount
//const defaultAmount = 10000
mtd_cost -= defaultAmount*invocationCost
mtd_cost -= includedExecutions*invocationCost
if (monthTotalCost > 0) {
setMonthTotalCost(monthTotalCost.toFixed(2))
}
if (mtd_cost > 0) {
setMonthToDateCost(mtd_cost.toFixed(2))
}
}
// Make a date at the 1st of the current month
var foundstarttime = (new Date())
foundstarttime.setDate(1)
if (startTime !== "" && startTime !== undefined && startTime !== null) {
foundstarttime = startTime
}
// Set to tomorrow by default
var foundendtime = (new Date())
foundendtime.setDate(foundendtime.getDate() + 1)
// Check if endtime is after the daily statistics["date"] string
if (endTime !== "" && endTime !== undefined && endTime !== null) {
foundendtime = endTime
}
// Check if start time is before the daily statistics["date"] string
var newlist = []
for (let key in statistics["daily_statistics"]) {
const item = statistics["daily_statistics"][key]
if (item["date"] === undefined) {
continue
}
const date = new Date(item["date"])
if (date >= foundstarttime) {
if (date <= foundendtime) {
newlist.push(item)
}
}
}
// If newlist is empty, set the timestamp to 1 year back and check if there are any statistics there
// If foundstarttime is more than 30 days back, don't do this
/*
if (newlist.length === 0 && foundstarttime.getDate() > 30) {
// Set the timestamp to be back
foundstarttime.setFullYear(foundstarttime.getFullYear() - 1)
setStartTime(foundstarttime)
console.log("IN HERE")
}
*/
var tmpstats = JSON.parse(JSON.stringify(statistics))
var workflowexecutions = 0
var appexecutions = 0
var estimatedcost = 0
if (newlist.length > 0) {
tmpstats["daily_statistics"] = newlist
for (let key in newlist) {
const item = newlist[key]
if (item["workflow_executions"] === undefined) {
continue
}
workflowexecutions += item["workflow_executions"]
appexecutions += item["app_executions"]
estimatedcost += (item["app_executions"] * invocationCost)
}
tmpstats["monthly_workflow_executions"] = workflowexecutions
tmpstats["monthly_app_executions"] = appexecutions
}
// Make estimatedcost have max 2 decimals
if (isCloud) {
// Exclude includedExecutions*month
// const includedExecutions = 150000
//estimatedcost -= (includedExecutions * invocationCost)
setApprunCost(estimatedcost.toFixed(2))
}
setFilteredStatistics(tmpstats)
handleDataSetting(tmpstats, "day")
}, [statistics, startTime, endTime])
const handleStartTimeChange = (date) => {
setStartTime(date)
}
const handleEndTimeChange = (date) => {
setEndTime(date)
}
const handleDataSetting = (inputdata, grouping) => {
if (inputdata === undefined || inputdata === null) {
return
@@ -95,8 +262,6 @@ const AppStats = (defaultprops) => {
return
}
console.log("Looking at daily data: ", inputdata)
var appRuns = {
"key": "App Runs",
"data": []
@@ -112,6 +277,11 @@ const AppStats = (defaultprops) => {
"data": []
}
var appcostRuns = {
"key": "Cost of App Runs",
"data": []
}
for (let key in dailyStats) {
// Always skips first one as it has accumulated data in it
if (key === 0) {
@@ -119,7 +289,6 @@ const AppStats = (defaultprops) => {
}
const item = dailyStats[key]
if (item["date"] === undefined) {
console.log("No date: ", item)
continue
@@ -131,6 +300,12 @@ const AppStats = (defaultprops) => {
key: new Date(item["date"]),
data: item["app_executions"]
})
// Add number
appcostRuns["data"].push({
key: new Date(item["date"]),
data: (item["app_executions"] * invocationCost).toFixed(2)
})
}
// Check if workflow_executions key in item
@@ -150,12 +325,16 @@ const AppStats = (defaultprops) => {
}
// Adds data for today
console.log("Inputdata: ", inputdata)
if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
appRuns["data"].push({
key: new Date(),
data: inputdata["daily_app_executions"]
})
appcostRuns["data"].push({
key: new Date(),
data: (inputdata["daily_app_executions"] * invocationCost).toFixed(2)
})
}
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
@@ -175,6 +354,7 @@ const AppStats = (defaultprops) => {
setSubflowRuns(subflowRuns)
setWorkflowRuns(workflowRuns)
setAppruns(appRuns)
setApprunCosts(appcostRuns)
}
const getStats = () => {
@@ -186,25 +366,25 @@ const AppStats = (defaultprops) => {
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!: ", response.status);
return;
}
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!: ", response.status);
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson["success"] === false) {
return
}
return response.json();
})
.then((responseJson) => {
if (responseJson["success"] === false) {
return
}
setStatistics(responseJson)
handleDataSetting(responseJson, "day")
})
.catch((error) => {
console.log("error: ", error)
});
setStatistics(responseJson)
handleDataSetting(responseJson, "day")
})
.catch((error) => {
console.log("error: ", error)
});
}
useEffect(() => {
@@ -215,40 +395,130 @@ const AppStats = (defaultprops) => {
textAlign: "center",
padding: 40,
margin: 5,
backgroundColor: theme.palette.surfaceColor,
backgroundColor: theme.palette.platformColor,
border: "1px solid rgba(255,255,255,0.3)",
maxWidth: 300,
}
const data = (
<div className="content" style={{width: "100%", margin: "auto", }}>
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }}>
All Stat widgets are monthly and gathered from <a
All shown statistics are gathered from <a
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
target="_blank"
style={{ textDecoration: "none", color: "#f85a3e",}}
>Your Organization Statistics. </a>
This is a feature to help give you more insight into Shuffle, and will be populating over time.
>Your Organization Statistics </a>
This is a feature to help give you more insight into Shuffle, and to understand your utilization of the Shuffle platform. <b>The billing tracker is in Beta, and is always calculated manually before being invoiced.</b>
</Typography>
{statistics !== undefined ?
<div style={{display: "flex", textAlign: "center",}}>
<Paper style={paperStyle}>
<Typography variant="h4">
{statistics.monthly_workflow_executions}
</Typography>
<Typography variant="h6">
Workflow Runs
</Typography>
</Paper>
<Paper style={paperStyle}>
<Typography variant="h4">
{statistics.monthly_app_executions}
</Typography>
<Typography variant="h6">
App Runs
</Typography>
</Paper>
</div>
: null}
<div style={{display: "flex", textAlign: "center",}}>
{filteredStatistics !== undefined ?
<div style={{flex: 1, display: "flex", textAlign: "center",}}>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}.
</Typography>
}>
<Paper style={paperStyle}>
<Typography variant="h4">
{selectedOrganization.lead_info.customer === false && selectedOrganization.lead_info.pov === false ?
0
:
apprunCost
}
</Typography>
<Typography variant="h6">
Period Cost
</Typography>
</Paper>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
App runs in the selected period
</Typography>
}>
<Paper style={paperStyle}>
<Typography variant="h4">
{filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions}
</Typography>
<Typography variant="h6">
App Runs
</Typography>
</Paper>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Workflow runs in the selected period
</Typography>
}>
<Paper style={paperStyle}>
<Typography variant="h4">
{filteredStatistics.monthly_workflow_executions === null || filteredStatistics.monthly_workflow_executions === undefined ? 0 : filteredStatistics.monthly_workflow_executions}
</Typography>
<Typography variant="h6">
Workflow Runs
</Typography>
</Paper>
</Tooltip>
<Tooltip title={
<Typography variant="body1" style={{padding: 10, }}>
Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}.
</Typography>
}>
<Paper style={{
textAlign: "center",
padding: 40,
margin: 5,
marginLeft: 90,
backgroundColor: theme.palette.platformColor,
border: "1px solid rgba(255,255,255,0.3)",
maxWidth: 300,
}}>
<Typography variant="h4">
${monthTotalCost}
</Typography>
<Typography variant="h6">
Estimated cost
</Typography>
</Paper>
</Tooltip>
</div>
: null}
<LocalizationProvider dateAdapter={AdapterDayjs} style={{flex: 1, }}>
<div style={{display: "flex", flexDirection: "column", }}>
<DateTimePicker
sx={{
marginTop: 1,
marginLeft: 1,
minWidth: 240,
maxWidth: 240,
}}
ampm={false}
label="Search from"
format="YYYY-MM-DD HH:mm:ss"
value={startTime}
onChange={handleStartTimeChange}
renderInput={(params) => <TextField {...params} />}
/>
<DateTimePicker
sx={{
marginTop: 1,
marginLeft: 1,
minWidth: 240,
maxWidth: 240,
}}
ampm={false}
label="Search until"
format="YYYY-MM-DD HH:mm:ss"
value={endTime}
onChange={handleEndTimeChange}
renderInput={(params) => <TextField {...params} />}
/>
</div>
</LocalizationProvider>
</div>
{appRuns === undefined ?
null
@@ -267,6 +537,12 @@ const AppStats = (defaultprops) => {
:
<LineChartWrapper keys={subflowRuns} height={300} width={"100%"} inputname={"Subflow Runs"}/>
}
{/*appRunCosts === undefined ?
null
:
<LineChartWrapper keys={appRunCosts} height={300} width={"100%"} inputname={"Apprun cost - Cost per day"}/>
*/}
</div>
)
+67 -49
View File
@@ -1,6 +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 {
Tooltip,
@@ -42,6 +43,7 @@ import {
Visibility as VisibilityIcon,
VisibilityOff as VisibilityOffIcon,
} from "@mui/icons-material";
import { validateJson, } from "../views/Workflows.jsx";
const scrollStyle1 = {
height: 100,
@@ -59,6 +61,7 @@ const scrollStyle2 = {
overflow: "scroll",
}
const CacheView = (props) => {
const { globalUrl, userdata, serverside, orgId } = props;
const [orgCache, setOrgCache] = React.useState("");
@@ -150,11 +153,22 @@ const CacheView = (props) => {
const deleteCache = (orgId, key) => {
toast("Attempting to delete Cache");
fetch(globalUrl + `/api/v1/orgs/${orgId}/cache/${key}`, {
method: "DELETE",
// method: "DELETE",
const method = "POST"
//const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/${key}`
const url = `${globalUrl}/api/v1/orgs/${orgId}/delete_cache`
const parsed = {
"org_id": orgId,
"key": key,
}
fetch(url, {
method: method,
headers: {
Accept: "application/json",
},
body: JSON.stringify(parsed),
credentials: "include",
})
.then((response) => {
@@ -334,7 +348,7 @@ const CacheView = (props) => {
return (
<div>
<div style={{paddingBottom: 250, }}>
{modalView}
<div style={{ marginTop: 20, marginBottom: 20 }}>
<h2 style={{ display: "inline" }}>Shuffle Datastore</h2>
@@ -380,19 +394,18 @@ const CacheView = (props) => {
<ListItem>
<ListItemText
primary="Key"
// style={{ minWidth: 150, maxWidth: 150 }}
style={{ minWidth: 250, maxWidth: 250, }}
/>
<ListItemText
primary="value"
// style={{ minWidth: 150, maxWidth: 150 }}
/>
<ListItemText
primary="Updated"
// style={{ minWidth: 150, maxWidth: 150 }}
primary="Value"
style={{ minWidth: 400, maxWidth: 400, overflowX: "auto", overflowY: "hidden", }}
/>
<ListItemText
primary="Actions"
// style={{ minWidth: 150, maxWidth: 150 }}
style={{ minWidth: 150, maxWidth: 150, marginLeft: 50, }}
/>
<ListItemText
primary="Updated"
/>
</ListItem>
{listCache === undefined || listCache === null
@@ -402,58 +415,56 @@ const CacheView = (props) => {
if (index % 2 === 0) {
bgColor = "#1f2023";
}
const validate = validateJson(data.value);
console.log("Past validate: ", validate);
return (
<ListItem key={index} style={{ backgroundColor: bgColor }}>
<ListItemText
style={{
maxWidth: 225,
minWidth: 225,
maxWidth: 250,
minWidth: 250,
overflow: "hidden",
}}
primary={data.key}
/>
<div style={scrollStyle1}>
<ListItemText
// style={{
// maxWidth: 225,
// maxHeight: 150,
// // overflow: "hidden",
// paddingLeft: "52px",
// overflow: "scroll",
// }}
style={scrollStyle2}
// style={{ maxWidth: 100, minWidth: 100 }}
// onMouseOver={() =>
// setShow((prevState) => ({ ...prevState, [data.value]: true }))
// }
// onMouseLeave={() =>
// setShow((prevState) => ({ ...prevState, [data.value]: false }))
// }
//primary={show[data.value] ? data.value : `${data.value.substring(0, 5)}...`}
primary={data.value}
/>
</div>
style={{
minWidth: 400,
maxWidth: 400,
overflowX: "auto",
overflowY: "hidden",
}}
primary={validate.valid ?
<ReactJson
src={validate.result}
theme={theme.palette.jsonTheme}
style={theme.palette.reactJsonStyle}
collapsed={true}
enableClipboard={(copy) => {
//handleReactJsonClipboard(copy);
}}
displayDataTypes={false}
onSelect={(select) => {
//HandleJsonCopy(showResult, select, data.action.label);
//console.log("SELECTED!: ", select);
}}
name={"value"}
/>
:
data.value
}
/>
<ListItemText
style={{
maxWidth: 225,
minWidth: 225,
overflow: "hidden",
marginLeft: "42px",
}}
primary={new Date(data.edited * 1000).toISOString()}
/>
<ListItemText
style={{
minWidth: 250,
maxWidth: 250,
overflow: "hidden",
paddingLeft: "155px",
maxWidth: 150,
minWidth: 150,
marginLeft: 50,
}}
primary=<span style={{ display: "inline" }}>
<Tooltip
title="Edit"
title="Edit item"
style={{}}
aria-label={"Edit"}
>
@@ -473,7 +484,7 @@ const CacheView = (props) => {
</span>
</Tooltip>
<Tooltip
title={"Delete Cache"}
title={"Delete item"}
style={{ marginLeft: 15, }}
aria-label={"Delete"}
>
@@ -493,6 +504,13 @@ const CacheView = (props) => {
</Tooltip>
</span>
/>
<ListItemText
style={{
maxWidth: 225,
minWidth: 225,
}}
primary={new Date(data.edited * 1000).toISOString()}
/>
</ListItem>
);
})}
+126 -59
View File
@@ -32,6 +32,8 @@ import {
import { FixName } from "../views/Apps.jsx";
import aa from 'search-insights'
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
// Handles workflow updates on first open to highlight the issues of the workflow
// Variables
// Action (exists, missing fields)
@@ -60,6 +62,8 @@ const ConfigureWorkflow = (props) => {
setAuthenticationType,
setAuthenticationModalOpen,
setConfigureWorkflowModalOpen,
setConfigurationFinished,
} = props;
const [requiredActions, setRequiredActions] = React.useState([]);
@@ -70,9 +74,19 @@ const ConfigureWorkflow = (props) => {
const [firstLoad, setFirstLoad] = React.useState("");
const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false);
const [loopRunning, setLoopRunning] = useState(false)
const [checkStarted, setCheckStarted] = React.useState(false);
useEffect(() => {
console.log("Required actions: ", requiredActions)
if (requiredActions.length === 0) {
console.log("No more actions? Set parent to done?")
if (setConfigurationFinished !== undefined) {
setConfigurationFinished(true)
}
}
}, [requiredActions])
const stop = () => {
setLoopRunning(false)
}
@@ -81,6 +95,7 @@ const ConfigureWorkflow = (props) => {
setLoopRunning(true)
}
useEffect(() => {
if (loopRunning) {
const intervalId = setInterval(() => {
@@ -159,7 +174,6 @@ const ConfigureWorkflow = (props) => {
return response.json();
})
.then((responseJson) => {
console.log("ACTION: ", responseJson);
if (
responseJson.actions !== undefined &&
responseJson.actions !== null
@@ -215,6 +229,9 @@ const ConfigureWorkflow = (props) => {
// without version match
const newappname = action.app_name.toLowerCase().replaceAll(" ", "_")
const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname)
if (app === undefined || app === null) {
const subapp = apps.find(app => app.name === action.app_name)
@@ -229,47 +246,51 @@ const ConfigureWorkflow = (props) => {
"required": true,
})
} else {
if (action.authentication_id === "" && app.authentication.required === true && action.parameters !== undefined && action.parameters !== null) {
// Check if configuration is filled or not
// Check if configuration is filled or not
var filled = true;
for (let [key,keyval] in Object.entries(action.parameters)) {
if (action.parameters[key].configuration) {
//console.log("Found config: ", action.parameters[key])
if (
action.parameters[key].value === null ||
action.parameters[key].value.length === 0
) {
if (action.parameters[key].value === null || action.parameters[key].value.length === 0) {
filled = false;
break;
}
}
}
if (app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app") {
filled = false
action.auth_type = "oauth2"
}
newaction.steps.push({
"title": "Authenticate app",
"type": "authenticate",
"required": true,
"auth_type": app.authentication.type,
})
if (!filled) {
newaction.must_authenticate = true;
newaction.action_ids.push(action.id);
}
} else if (action.authentication_id !== "" && app.authentication.required === true) {
console.log("Should verify authentication ID ", action.authentication_id)
} else if (action.authentication_id !== undefined && action.authentication_id !== null && action.authentication_id !== "" && app.authentication.required === true) {
console.log("FIXME: Should verify authentication ID ", action.authentication_id)
}
newaction.app = app;
}
if (
action.errors !== undefined &&
action.errors !== null &&
action.errors.length > 0
) {
if (newaction.errors !== undefined && newaction.errors !== null && newaction.errors.length > 0) {
//console.log("Node has errors!: ", action.errors)
}
//console.log(newaction.app_name,"AUTH: ", newaction.must_authenticate, " ACTIVATE: ", newaction.must_activate)
if (newaction.must_authenticate) {
var authenticationOptions = [];
for (let [key,keyval] in Object.entries(appAuthentication)) {
@@ -395,29 +416,29 @@ const ConfigureWorkflow = (props) => {
setRequiredActions(newactions);
}
if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) {
var newactions = []
for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) {
var newaction = requiredActions[actionkey];
const app = newaction.app;
if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) {
var newactions = []
for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) {
var newaction = requiredActions[actionkey];
const app = newaction.app;
for (let [key,keyval] in Object.entries(appAuthentication)) {
const auth = appAuthentication[key];
for (let [key,keyval] in Object.entries(appAuthentication)) {
const auth = appAuthentication[key];
// Does this account for all the different ones of the same?
if (auth.app.name === app.name && auth.active === true) {
newaction.auth_done = true;
break;
// Does this account for all the different ones of the same?
if (auth.app.name === app.name && auth.active === true) {
newaction.auth_done = true;
break;
}
}
newactions.push(newaction);
}
newactions.push(newaction);
}
setRequiredActions(newactions);
setPreviousAuth(appAuthentication);
// Set auth done to true
//"auth_done": false
setRequiredActions(newactions);
setPreviousAuth(appAuthentication);
// Set auth done to true
//"auth_done": false
}
const TriggerSection = (props) => {
@@ -610,14 +631,24 @@ const ConfigureWorkflow = (props) => {
const [authFields, setAuthFields] = useState([])
const [sensitiveFields, setSensitiveFields] = useState([])
//console.log("ACTION", action)
useEffect(() => {
if (finalized === true) {
setOpened(false)
setFilled(true)
}
}, [finalized])
if (authFields.length === 0 && opened === true) {
// Loop through fields of the action
var newfields = []
const params = action.action.parameters
var sensitiveIndexes = []
var index = 0
var sensitiveIndexes = []
const params = action.action.parameters
for (let key in params) {
const param = params[key]
@@ -732,9 +763,34 @@ const ConfigureWorkflow = (props) => {
<CheckIcon style={{color: theme.palette.green, marginLeft: 10, marginTop: 10, flex: 1, }} />
: null}
</div>
{opened ?
<div style={{padding: 12, }}>
{authFields.map((field, index) => {
{action.app.authentication.type === "oauth2-app" || action.app.authentication.type === "oauth2" || action.auth_type === "oauth2" ?
<div>
<AuthenticationOauth2
selectedApp={action.app}
selectedAction={{
"app_name": action.app.name,
"app_id": action.app.id,
"app_version": action.app.version,
"large_image": action.app.large_image,
}}
authenticationType={action.app.authentication}
isCloud={isCloud}
authButtonOnly={true}
isLoggedIn={true}
getAppAuthentication={undefined}
setFinalized={setFinalized}
/>
</div>
:
authFields.map((field, index) => {
console.log("THESE FIELDS?: ", field)
var parsedName = field.key
// Remove _basic at the end if it exists
if (parsedName.toLowerCase().endsWith("_basic")) {
@@ -807,25 +863,28 @@ const ConfigureWorkflow = (props) => {
</div>
)
})}
<Button
variant="contained"
color="primary"
style={{
marginTop: 15,
width: 150,
marginLeft: 135,
}}
disabled={!filled || submitted}
onClick={() => {
setSubmitted(true);
// const submitLocalAuth = (app, fields) => {
submitLocalAuth({"id": action.app.id, "name": action.app.name, "version": action.app.version, "large_image": action.large_image, }, authFields);
}}
>
{submitted ? <CircularProgress style={{color: theme.palette.primary.main, }} /> : "Submit"}
</Button>
{action.app.authentication.type !== "oauth2-app" && action.app.authentication.type !== "oauth2" ?
<Button
variant="contained"
color="primary"
style={{
marginTop: 15,
width: 150,
marginLeft: 135,
}}
disabled={!filled || submitted}
onClick={() => {
setSubmitted(true);
// const submitLocalAuth = (app, fields) => {
submitLocalAuth({"id": action.app.id, "name": action.app.name, "version": action.app.version, "large_image": action.large_image, }, authFields);
}}
>
{submitted ? <CircularProgress style={{color: theme.palette.primary.main, }} /> : "Submit"}
</Button>
: null}
</div>
: null}
</div>
@@ -1237,19 +1296,22 @@ const ConfigureWorkflow = (props) => {
return (
<div>
<div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px", maxHeight: 475, }}>
<div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px 0px", maxHeight: 475, }}>
{setConfigureWorkflowModalOpen !== undefined ?
<Typography variant="h6">{workflow.name}</Typography>
<Typography variant="h6">
{workflow.name}
</Typography>
: null
}
<Typography variant="body2" style={{}}>
Please configure the following apps for automatic startup of automation:
</Typography>
{requiredActions.length > 0 ? (
<span>
<Typography variant="body2" style={{}}>
Please configure the following steps to help us complete your workflow. This can also be done later.
</Typography>
{setConfigureWorkflowModalOpen !== undefined ?
<Typography variant="body1" style={{ marginTop: 10, }}>
Required Actions
@@ -1258,6 +1320,11 @@ const ConfigureWorkflow = (props) => {
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 250, }}>
{requiredActions.map((data, index) => {
// AppWrapper = Default in a workflow, only shows with steps
// AppSection =
// AppSectionSelfcontained = default for template generator
return (
<div key={index} style={{marginBottom: 10, }}>
{data.steps !== undefined && data.steps !== null && data.show_steps === true && setConfigureWorkflowModalOpen !== undefined ?
+14 -11
View File
@@ -235,7 +235,7 @@ const Header = (props) => {
setLoginHoverColor(hoverOutColor);
};
const notificationWidth = 300
const notificationWidth = 335
const imagesize = 22;
const boxColor = "#86c142";
@@ -398,7 +398,8 @@ const Header = (props) => {
<Button
color="primary"
variant="contained"
style={{ marginLeft: 30 }}
disabled={notifications.filter((data) => !data.read).length === 0}
style={{ marginLeft: 30, }}
onClick={() => {
clearNotifications();
}}
@@ -454,12 +455,9 @@ const Header = (props) => {
return response.json();
})
.then(function (responseJson) {
console.log("In here?")
if (responseJson.success === true) {
if (
responseJson.region_url !== undefined &&
responseJson.region_url !== null &&
responseJson.region_url.length > 0
) {
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
@@ -468,9 +466,14 @@ const Header = (props) => {
setTimeout(() => {
window.location.reload();
}, 2000);
toast("Successfully changed active organization - refreshing!");
} else {
toast("Failed changing org: ", responseJson.reason);
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
toast(responseJson.reason);
} else {
toast("Failed changing org. Try again or contact support@shuffler.io if this persists.");
}
}
})
.catch((error) => {
@@ -613,7 +616,7 @@ const Header = (props) => {
<Divider style={{marginBottom: 10, }}/>
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 5, marginBottom: 5,}}>
Version: 1.3.1
Version: 1.3.3
</Typography>
</Menu>
</span>
@@ -627,7 +630,7 @@ const Header = (props) => {
};
// Handle top bar or something
const defaultTop = isCloud ? 0 : 7;
const defaultTop = -2
const loginTextBrowser = !isLoggedIn ? (
<div
style={{
@@ -660,7 +663,7 @@ const Header = (props) => {
<img
src={"/images/logos/topleft_logo.svg"}
alt="shuffle logo"
style={{ height: 25, marginTop: 5, }}
style={{ height: 25, }}
/>
</Grid>
</Link>
+17 -8
View File
@@ -97,6 +97,8 @@ const AuthenticationOauth2 = (props) => {
autoAuth,
authButtonOnly,
isLoggedIn,
setFinalized,
} = props;
let navigate = useNavigate();
@@ -489,20 +491,27 @@ const AuthenticationOauth2 = (props) => {
var open = true;
const timer = setInterval(() => {
if (newwin.closed) {
console.log("Closing?")
console.log("Closing?")
setButtonClicked(false);
clearInterval(timer);
//alert('"Secure Payment" window closed!');
//
if (getAppAuthentication !== undefined) {
getAppAuthentication(true, true, true);
}
if (getAppAuthentication !== undefined) {
getAppAuthentication(true, true, true);
}
// This is more a guess than anything
// Should be handled in getAppAuthentication()
// in the parent component to make it accurate,
// seeing as we don't know what the parent component
// wants to happen
if (setFinalized !== undefined) {
setFinalized(true)
}
} else {
console.log("Not closed")
}
console.log("Not closed")
}
}, 1000);
//do {
// setTimeout(() => {
+148 -112
View File
@@ -11,6 +11,7 @@ import { NestedMenuItem } from "mui-nested-menu";
//import { useAlert
import {
Chip,
ButtonGroup,
Popper,
TextField,
@@ -174,8 +175,6 @@ const ParsedAction = (props) => {
} = props;
const classes = useStyles();
//const alert = useAlert()
const [hideBody, setHideBody] = React.useState(true);
const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false);
@@ -184,31 +183,34 @@ const ParsedAction = (props) => {
const [autoCompleting, setAutocompleting] = React.useState(false);
useEffect(() => {
if (setLastSaved !== undefined) {
setLastSaved(false)
}
}, [expansionModalOpen])
useEffect(() => {
if (setLastSaved !== undefined) {
setLastSaved(false)
}
}, [expansionModalOpen])
if (selectedAction.parameters === null || selectedAction.parameters === undefined) {
return
}
useEffect(() => {
if (selectedAction.parameters !== null && selectedAction.parameters !== undefined) {
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
//console.log("LOADED! Change hideBody based on input? Action: ", selectedAction, paramcheck)
if (paramcheck !== undefined && paramcheck !== null) {
if (paramcheck.id === "TOGGLED"){
setHideBody(false)
setActivateHidingBodyButton(false)
} else {
setHideBody(true)
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
if (paramcheck === undefined || paramcheck === null) {
return
}
if (paramcheck.id === "UNTOGGLED") {
setActivateHidingBodyButton(false)
}
}
}
// This was just opposite..
if (paramcheck.id === "TOGGLED"){
setHideBody(true)
} else {
setHideBody(false)
if (paramcheck.id === "UNTOGGLED") {
setActivateHidingBodyButton(false)
}
}, [])
}
}, [])
const keywords = [
"len(",
@@ -1189,14 +1191,14 @@ const ParsedAction = (props) => {
}
return (
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
}}
{...params}
label="Find App to Translate"
variant="outlined"
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
}}
{...params}
label="Find App to Translate"
variant="outlined"
/>
);
}}
@@ -1359,20 +1361,6 @@ const ParsedAction = (props) => {
var disabled = false;
var rows = "3";
var openApiHelperText = "This is an OpenAPI specific field";
/*
if (
selectedApp.generated &&
data.name === "url" &&
data.required &&
data.configuration
) {
//&&
//hideExtraTypes
//console.log("GENERATED WITH DATA: ", data);
return null;
}
*/
if (selectedApp.generated && data.name === "headers") {
//console.log("HEADER: ", data)
@@ -1385,8 +1373,9 @@ const ParsedAction = (props) => {
const hideBodyButtonValue = (
<div
key={data.name}
id="hide_body_button"
style={{
marginTop: 25,
marginTop: 50,
border: "1px solid rgba(255,255,255,0.7)",
borderTop: "1px solid rgba(255,255,255,0.7)",
borderRadius: theme.palette.borderRadius,
@@ -1396,7 +1385,7 @@ const ParsedAction = (props) => {
>
<Tooltip
color="secondary"
title={"Show all body fields"}
title={hideBody ? "Hide all body fields and only show the body itself" : "Show all body fields instead of the body itself"}
placement="top"
>
<FormControlLabel
@@ -1408,13 +1397,12 @@ const ParsedAction = (props) => {
color: theme.palette.primary.secondary,
}}
onChange={(event) => {
var tag = "TOGGLED"
if (hideBody) {
tag = "UNTOGGLED"
}
var tag = "TOGGLED"
if (hideBody) {
tag = "UNTOGGLED"
}
setHideBody(!hideBody);
setHideBody(!hideBody)
for (let paramkey in Object.entries(selectedActionParameters)) {
var currentItem = selectedActionParameters[paramkey];
if (currentItem.name === "ssl_verify") {
@@ -1422,21 +1410,33 @@ const ParsedAction = (props) => {
}
if (currentItem.name === "body") {
// FIXME: Workaround for toggling, as actions don't have IDs.
// May screw up something in the future.
currentItem.id = tag
}
if (currentItem.description === openApiFieldDesc) {
currentItem.field_active = !hideBody;
//console.log("Changing", currentItem);
currentItem.field_active = !hideBody
}
}
// Scroll to hide_body_button
setTimeout(() => {
var element = document.getElementById("hide_body_button")
if (element !== undefined && element !== null) {
// Keep the button a little below the top
element.scrollIntoView({
behavior: "smooth",
block: "center",
})
}
}, 100)
}}
name="requires_unique"
/>
}
label={"Automatically fix body"}
label={hideBody ? "Show Body" : "Hide Body"}
/>
</Tooltip>
</div>
@@ -1446,6 +1446,8 @@ const ParsedAction = (props) => {
const regex = /\${(\w+)}/g;
const found = placeholder.match(regex);
// setActivateHidingBodyButton(false)
//
hideBodyButton = hideBodyButtonValue;
if (found === null || !hideBody) {
if (found === null) {
@@ -1454,13 +1456,13 @@ const ParsedAction = (props) => {
//console.log("In found: ", found, hideBody)
}
} else {
//console.log("SHOW BUTTON");
rows = "1";
disabled = true;
openApiHelperText = "OpenAPI spec: fill the following fields.";
//console.log("SHOULD ADD TO selectedActionParameters!: ", found, selectedActionParameters)
var changed = false;
var tempArray = []
for (let specKey in found) {
const tmpitem = found[specKey];
var skip = false;
@@ -1489,7 +1491,7 @@ const ParsedAction = (props) => {
}
}
selectedActionParameters.push({
tempArray.push({
action_field: "",
configuration: false,
description: openApiFieldDesc,
@@ -1505,11 +1507,39 @@ const ParsedAction = (props) => {
value: "",
variant: "STATIC_VALUE",
field_active: true,
autocompleted: true,
});
}
console.log("TEMP ARRAY: ", tempArray)
var required = selectedActionParameters.filter(item => item.required === true)
var notRequired = selectedActionParameters.filter(item => item.required === false)
if (tempArray.length > 0) {
// Sort tempArray based on tempArray.required
tempArray.sort((a, b) => (a.required < b.required) ? 1 : -1)
// Add all items to the selectedActionParameters array
for (let innerkey in tempArray) {
tempArray[innerkey].id = "ADDED"
if (tempArray[innerkey].required === true) {
required.push(tempArray[innerkey])
} else {
notRequired.push(tempArray[innerkey])
}
}
}
//selectedActionParameters
if (changed) {
setSelectedActionParameters(selectedActionParameters);
// Sort selectedActionParameters based on selectedActionParameters.required
//selectedActionParameters.sort((a, b) => (a.required < b.required) ? 1 : -1)
// Find the "headers" and "queries" field names and put them on the first indexes anyway
var newArray = required.concat(notRequired)
setSelectedActionParameters(newArray)
}
return hideBodyButton;
@@ -1522,9 +1552,6 @@ const ParsedAction = (props) => {
const clickedFieldId = "rightside_field_" + count;
//<TextareaAutosize
// <CodeMirror
//fullWidth
var baseHelperText = ""
if (data !== undefined && data !== null && data.value !== undefined && data.value !== null && data.value.length > 0) {
baseHelperText = calculateHelpertext(data.value)
@@ -1618,33 +1645,15 @@ const ParsedAction = (props) => {
helperText={returnHelperText(data.name, data.value)}
onClick={() => {
console.log("Clicked field: ", clickedFieldId, data.name)
/*
setExpansionModalOpen(false);
*/
//if (data.name === "file_id") {
// console.log("show file video?")
// if (setShowVideo !== undefined) {
// setShowVideo("https://www.youtube.com/embed/DPYowyTbsSk")
// }
//}
//(data.name.toLowerCase().includes("api") ||
/*
setExpansionModalOpen(false);
if (
setScrollConfig !== undefined &&
scrollConfig !== null &&
scrollConfig !== undefined &&
scrollConfig.selected !== clickedFieldId
) {
scrollConfig.selected = clickedFieldId;
setScrollConfig(scrollConfig);
//console.log("Change field id!")
}
*/
//console.log("Clicked field: ", clickedFieldId)
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
console.log("IN SCROLL CONFIG!")
scrollConfig.selected = clickedFieldId
setScrollConfig(scrollConfig)
//console.log("Change field id!")
}
}}
id={clickedFieldId}
@@ -2475,19 +2484,19 @@ const ParsedAction = (props) => {
</Tooltip>
) : null}
{hasAutocomplete === true ?
<Tooltip
color="primary"
title={"Field was autocompleted by Shuffle based on previous actions (same fields or parent nodes)"}
placement="top"
>
<AutoFixHighIcon style={{
color: "rgba(255,255,255,0.7)" ,
marginRight: 10,
}}/>
</Tooltip>
:
null}
{hasAutocomplete === true ?
<Tooltip
color="primary"
title={"Field was autocompleted by Shuffle based on previous actions (same fields or parent nodes)"}
placement="top"
>
<AutoFixHighIcon style={{
color: "rgba(255,255,255,0.7)" ,
marginRight: 10,
}}/>
</Tooltip>
:
null}
<div
style={{
@@ -3028,8 +3037,6 @@ const ParsedAction = (props) => {
const parsedBaseLabel = "$"+baselabel.toLowerCase().replaceAll(" ", "_")
const newname = "$"+name.toLowerCase().replaceAll(" ", "_")
console.log("NAME: ", name)
// Check if it's the same as the current name in use
//if (name === selectedAction.label) {
// console.log("Returning from name thing")
@@ -3298,14 +3305,12 @@ const ParsedAction = (props) => {
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Authentication</Typography>
<div style={{ display: "flex" }}>
<Select
MenuProps={{
disableScrollLock: true,
}}
MenuProps={{
disableScrollLock: true,
}}
labelId="select-app-auth"
value={
Object.getOwnPropertyNames(
selectedAction.selectedAuthentication
).length === 0
Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0
? "No selection"
: selectedAction.selectedAuthentication
}
@@ -3353,7 +3358,10 @@ const ParsedAction = (props) => {
<em>No selection</em>
</MenuItem>
{selectedAction.authentication.map((data) => {
//console.log("AUTH DATA: ", data)
if (data.last_modified === true) {
//console.log("LAST MODIFIED: ", data.label)
}
return (
<MenuItem
key={data.id}
@@ -3363,7 +3371,23 @@ const ParsedAction = (props) => {
}}
value={data}
>
{data.label} - ({data.app.app_version})
{data.last_modified === true ?
<Chip
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
label={"Latest"}
variant="outlined"
color="secondary"
/>
: null}
{data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ?
<Chip
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
label={data.app.app_version}
variant="outlined"
color="secondary"
/>
: null}
{data.label}
</MenuItem>
);
})}
@@ -3449,6 +3473,14 @@ const ParsedAction = (props) => {
}}
value={data.Name}
>
{data.default === true ?
<Chip
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
label={"Default"}
variant="outlined"
color="secondary"
/>
: null}
{data.Name}
</MenuItem>
);
@@ -3562,7 +3594,7 @@ const ParsedAction = (props) => {
options={selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))}
ListboxProps={{
style: {
backgroundColor: theme.palette.inputColor,
backgroundColor: theme.palette.surfaceColor,
color: "white",
},
}}
@@ -3709,9 +3741,12 @@ const ParsedAction = (props) => {
return (
<TextField
{...params}
data-lpignore="true"
autocomplete="off"
dataLPIgnore="true"
autoComplete="off"
color="primary"
id="checkbox-search"
@@ -3720,9 +3755,10 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
}}
{...params}
label="Find Actions"
variant="outlined"
name={`disable_autocomplete_${Math.random()}`}
/>
);
}}
+77 -10
View File
@@ -54,6 +54,7 @@ const RuntimeDebugger = (props) => {
const [status, setStatus] = useState("")
const [endTime, setEndTime] = useState("")
const [startTime, setStartTime] = useState("")
const [totalCount, setTotalCount] = useState(0)
const [workflow, setWorkflow] = useState({})
const [ignoreOrg, setIgnoreOrg] = useState(false)
@@ -65,16 +66,80 @@ const RuntimeDebugger = (props) => {
const [workflows, setWorkflows] = useState([
{"id": "", "name": "All Workflows",}
])
/*
[
{id: "1", execution_id: "1", status: "FINISHED", startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
{id: "2", execution_id: "2", status: "WAITING", startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
{id: "3", execution_id: "3", status: "EXECUTING",startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
{id: "4", execution_id: "4", status: "ABORTED", startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
]);
*/
// Shitty workflow search on purpose :)
const handleWorkflowUsageCount = (workflows) => {
if (workflows === undefined || workflows === null || workflows.length === 0) {
return
}
setTotalCount(0)
var count = 0
var starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString()
var endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString()
var maxworkflows = 5
console.log("Looking for MAX this amount of workflows: ", maxworkflows)
for (let key in workflows) {
if (key > maxworkflows) {
break
}
const workflowId = workflows[key].id
// Fetch the data for the workflow
var url = `${globalUrl}/api/v1/workflows/${workflowId}/executions/count`
if (starttime !== "") {
url += `?start_time=${starttime}`
}
if (endtime !== "") {
if (starttime !== "") {
url += `&end_time=${endtime}`
} else {
url += `?end_time=${endtime}`
}
}
fetch(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;
}
return response.json()
})
.then((data) => {
if (data.success) {
if (data.count !== undefined && data.count !== null) {
count += data.count
}
} else {
console.log("Failed to get workflow usage count: ", data)
}
})
.catch((error) => {
console.error("Error:", error);
})
}
setTimeout(() => {
console.log("Setting total count: ", count)
setTotalCount(count)
}, maxworkflows*300)
}
const submitSearch = (workflowId, status, startTime, endTime, cursor, limit) => {
handleWorkflowUsageCount(workflows)
//setResultRows([])
setSearchLoading(true)
@@ -135,6 +200,7 @@ const RuntimeDebugger = (props) => {
})
}
const getAvailableWorkflows = () => {
fetch(globalUrl + "/api/v1/workflows", {
method: "GET",
@@ -624,7 +690,7 @@ const RuntimeDebugger = (props) => {
<div style={{minWidth: 1150, maxWidth: 1150, margin: "auto", }}>
<div style={{display: "flex", }}>
<h1 style={{flex: 3, }}>Workflow Run Debugger</h1>
<h1 style={{flex: 3, }}>Workflow Run Debugger {totalCount !== 0 ? ` (~${totalCount})` : ""}</h1>
{selectedWorkflowExecutions.length > 0 ?
<ButtonGroup>
<Tooltip title="Reruns ALL selected workflows. This will make a new execution for them, and not continue the existing.">
@@ -830,6 +896,7 @@ const RuntimeDebugger = (props) => {
);
}}
/>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DateTimePicker
sx={{
@@ -859,8 +926,8 @@ const RuntimeDebugger = (props) => {
onChange={handleEndTimeChange}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<Button
variant="outlined"
color="primary"
+81 -70
View File
@@ -42,14 +42,20 @@ const chipStyle = {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const SearchData = props => {
const { serverside, userdata, setModalOpen, modalOpen } = props
let navigate = useNavigate();
const borderRadius = 3
const node = useRef()
const [searchOpen, setSearchOpen] = useState(true)
const [searchOpen, setSearchOpen] = useState(false)
const [oldPath, setOldPath] = useState("")
const [value, setValue] = useState("");
const [userTyped, setUserTyped] = useState(false)
const handleLinkClick = () => {
if (modalOpen) {
setModalOpen(false); // Assuming setModalOpen is defined correctly
} else {
console.log("Condition not met, staying on the same page");
}
};
if (serverside === true) {
return null
@@ -72,37 +78,36 @@ const SearchData = props => {
// }, searchOpen)
const SearchBox = ({ currentRefinement, refine, isSearchStalled, }) => {
const [inputValue, setInputValue] = useState(currentRefinement);
const textFieldRef = useRef(null);
const keyPressHandler = (e) => {
// e.preventDefault();
if (e.which === 13) {
// alert("You pressed enter!");
navigate("/search?q=" + currentRefinement, { state: value, replace: true });
setSearchOpen(false)
setModalOpen(false)
return
// navigate(`/search?q=${currentRefinement}`, { state: value, replace: true });
// setModalOpen(false);
const trimmedValue = inputValue.trim();
if (trimmedValue !== '') {
e.preventDefault();
navigate(`/search?q=${trimmedValue}`, { state: trimmedValue, replace: true });
setModalOpen(false);
}
}
};
/*
endAdornment: (
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
event.preventDefault()
}}>
<CloseIcon style={{marginRight: 5,}} onClick={() => {
setSearchOpen(false)
}} />
</InputAdornment>
),
*/
useEffect(() => {
if (searchOpen && textFieldRef.current) {
textFieldRef.current.focus();
}
}, [searchOpen]);
return (
<form id="search_form" noValidate type="searchbox" action="" role="search" onClick={() => {
}}
>
<TextField
fullWidth
style={{ zIndex: 1100, marginTop:-20,marginBottom: 200, position:"fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 685, }}
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 685, }}
InputProps={{
style: {
color: "white",
@@ -130,28 +135,30 @@ const SearchData = props => {
type="search"
color="primary"
placeholder="Find Public Apps, Workflows, Documentation..."
value={currentRefinement}
onKeyDown={keyPressHandler}
value={inputValue}
id="shuffle_search_field"
onClick={(event) => {
if (!searchOpen) {
setSearchOpen(true)
setTimeout(() => {
var tarfield = document.getElementById("shuffle_search_field")
//console.log("TARFIELD: ", tarfield)
tarfield.focus()
}, 250)
if (inputValue.trim() !== '') {
setSearchOpen(true);
}
}}
onBlur={(event) => {
//setTimeout(() => {
// setSearchOpen(false)
//}, 500)
setSearchOpen(inputValue.trim() !== '')
}}
onChange={(event) => {
refine(event.currentTarget.value)
const newValue = event.target.value;
setInputValue(newValue);
refine(newValue);
if (newValue.trim() !== '') {
setSearchOpen(true);
} else {
setSearchOpen(false);
}
}}
onKeyDown={keyPressHandler}
inputRef={textFieldRef}
limit={5}
autoFocus
/>
{/*isSearchStalled ? 'My search is stalled' : ''*/}
</form>
@@ -183,8 +190,8 @@ const SearchData = props => {
const baseImage = <CodeIcon />
return (
<Card elevation={0} style={{ marginRight: 10,marginTop:50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 75, boxShadows: "none", }}>
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color:"#FF8444", borderBottom: "1px solid", width: 105 }}>
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 75, boxShadows: "none", }}>
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 105 }}>
Workflows
</Typography>
@@ -234,7 +241,7 @@ const SearchData = props => {
<Link key={hit.objectID} to={parsedUrl} rel="noopener noreferrer" style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
//console.log("CLICK")
setSearchOpen(true)
setModalOpen(false)
aa('init', {
appId: searchClient.appId,
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
@@ -258,7 +265,6 @@ const SearchData = props => {
event.preventDefault()
window.open(parsedUrl, '_blank');
}
setModalOpen(false)
}}>
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
setMouseHoverIndex(index)
@@ -350,7 +356,7 @@ const SearchData = props => {
}}>
<CloseIcon />
</IconButton> */}
<Typography variant="h6" style={{ margin: "40px 10px 0px 20px", color:"#FF8444", borderBottom: "1px solid", width: 50 }}>
<Typography variant="h6" style={{ margin: "40px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 50 }}>
Apps
</Typography>
@@ -424,10 +430,9 @@ const SearchData = props => {
return (
<Link key={hit.objectID} to={parsedUrl} style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
console.log("CLICK")
setSearchOpen(true)
setModalOpen(false)
setModalOpen(false)
aa('init', {
appId: searchClient.appId,
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
@@ -513,13 +518,13 @@ const SearchData = props => {
//console.log(type, hits.length, hits)
return (
<Card elevation={0} style={{ marginRight: 10,marginTop:50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
setSearchOpen(false)
}}>
<CloseIcon />
</IconButton> */}
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color:"#FF8444", borderBottom: "1px solid", width: 152}}>
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 152 }}>
Documentation
</Typography>
{/*
@@ -608,7 +613,7 @@ const SearchData = props => {
console.log("CLICK")
setSearchOpen(true)
setModalOpen(false)
setModalOpen(false)
}}>
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
setMouseHoverIndex(index)
@@ -636,14 +641,14 @@ const SearchData = props => {
</Card>
)
}
const gettingStartData = !searchOpen ? (
const gettingStartData = !searchOpen ? (
<Grid
container
direction="row"
alignItems="center"
// justify="space-evenly"
>
<Grid item xs={6} style={{ alignItems: "center", flexDirection: "row", marginTop: 70, }}>
<Grid item xs={6} style={{ alignItems: "center", flexDirection: "row", marginTop: 70, }}>
<List style={{ width: "100%", marginLeft: 10, color: "var(--Paragraph-text, #C8C8C8)" }}>
<ListItem>
<ArticleIcon style={{ marginRight: 10, display: "flex", width: 22 }} />
@@ -651,28 +656,34 @@ const SearchData = props => {
</ListItem>
<div style={{ marginLeft: 25, }}>
<ListItem>
<Link onClick={() => { window.location = "/docs"; }} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/docs" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Documentation</Typography>
<KeyboardArrowRightIcon />
</Link>
</ListItem>
<ListItem>
<Link to="https://github.com/Shuffle/Shuffle/blob/main/.github/install-guide.md" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Onprem Installation</Typography>
<a
href="https://github.com/Shuffle/Shuffle/blob/main/.github/install-guide.md"
style={{
textDecoration: "none",
color: "var(--Paragraph-text, #C8C8C8)",
display: "flex"
}}
>
<Typography variant="body1" style={{ fontSize: 16 }}>
Onprem Installation
</Typography>
<KeyboardArrowRightIcon />
</Link>
</a>
</ListItem>
<ListItem>
<Link to="/usecases" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/usecases" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Explore Usecases</Typography>
<KeyboardArrowRightIcon />
</Link>
</ListItem>
<ListItem>
<Link to="/search?tab=workflows" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/search?tab=workflows" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Find public workflows</Typography>
<KeyboardArrowRightIcon />
</Link>
@@ -688,25 +699,25 @@ const SearchData = props => {
</ListItem>
<div style={{ marginLeft: 35 }}>
<ListItem>
<Link to="/docs/app_creation" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/docs/app_creation" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Create Apps</Typography>
<KeyboardArrowRightIcon />
</Link>
</ListItem>
<ListItem>
<Link to="/apps" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/apps" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Find Apps</Typography>
<KeyboardArrowRightIcon />
</Link>
</ListItem>
<ListItem>
<Link to="/workflows" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/workflows" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Workflows</Typography>
<KeyboardArrowRightIcon />
</Link>
</ListItem>
<ListItem>
<Link to="/creators" style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Link to="/creators" onClick={handleLinkClick} style={{ textDecoration: "none", color: "var(--Paragraph-text, #C8C8C8)", display: "flex" }}>
<Typography variant="body1" style={{ fontSize: 16, }}>Creator</Typography>
<KeyboardArrowRightIcon />
</Link>
@@ -721,7 +732,7 @@ const SearchData = props => {
</Button>
</Grid>
</Grid>
): null
) : null
const CustomSearchBox = connectSearchBox(SearchBox)
const CustomAppHits = connectHits(AppHits)
@@ -731,17 +742,17 @@ const SearchData = props => {
const modalView = (
<div>
<Grid container style={{ display: "contents", }}>
<Grid item xs="auto" style={{ }}>
<Grid item xs="auto" style={{}}>
<Index indexName="appsearch">
<CustomAppHits />
</Index>
</Grid>
<Grid item xs="auto" style={{ }}>
<Grid item xs="auto" style={{}}>
<Index indexName="workflows">
<CustomWorkflowHits />
</Index>
</Grid>
<Grid item xs="auto" style={{ }}>
<Grid item xs="auto" style={{}}>
<Index indexName="documentation">
<CustomDocHits />
</Index>
@@ -753,14 +764,14 @@ const SearchData = props => {
return (
<div ref={node} style={{ width: "100%", maxWidth: "100%", margin: "auto", }}>
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
console.log("CLICKED")
}}>
<Configure clickAnalytics />
<CustomSearchBox onClick={() => {
console.log("Click 2")
}}/>
{modalView}
<CustomSearchBox />
{modalView}
</InstantSearch>
{gettingStartData}
{gettingStartData}
</div>
)
}
+3 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import SearchBox from "./SearchData.jsx";
import SearchBox from "../components/SearchData.jsx";
import {
Chip,
@@ -91,8 +91,8 @@ const SearchField = props => {
}}
>
{isHeader ? <div style={{ display: "flex"}}>
<DialogTitle style={{ marginTop: 15, marginLeft: 5, color: "var(--Paragraph-text, #C8C8C8)" }} >Search for docs, apps, workflows and more</DialogTitle>
<Button fullWidth style={{ marginLeft: 470 }} onClick={() => {
<DialogTitle style={{ marginTop: 15, marginLeft: 5, color: "var(--Paragraph-text, #C8C8C8)" }} >Search for Docs, Apps, Workflows and more</DialogTitle>
<Button fullWidth style={{ marginLeft:180, }} onClick={() => {
setModalOpen(false);
}}><CloseIcon /></Button>
</div>
+190 -197
View File
@@ -47,6 +47,7 @@ import ReactJson from "react-json-view";
import PaperComponent from "../components/PaperComponent.jsx";
import CodeMirror from '@uiw/react-codemirror';
import { python } from '@codemirror/lang-python';
//import 'codemirror/keymap/sublime';
//import 'codemirror/addon/selection/mark-selection.js'
//import 'codemirror/theme/gruvbox-dark.css';
@@ -212,6 +213,80 @@ const CodeEditor = (props) => {
expectedOutput(localcodedata)
}, [])
useEffect(() => {
expectedOutput(localcodedata)
}, [availableVariables])
var to_be_copied = "";
const HandleJsonCopy = (base, copy, base_node_name) => {
if (typeof copy.name === "string") {
copy.name = copy.name.replaceAll(" ", "_");
}
//lol
if (typeof base === 'object' || typeof base === 'dict') {
base = JSON.stringify(base)
}
if (base_node_name === "execution_argument" || base_node_name === "Execution Argument") {
base_node_name = "exec"
}
console.log("COPY: ", base_node_name, copy);
//var newitem = JSON.parse(base);
var newitem = validateJson(base).result
to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_");
for (let copykey in copy.namespace) {
if (copy.namespace[copykey].includes("Results for")) {
continue;
}
if (newitem !== undefined && newitem !== null) {
newitem = newitem[copy.namespace[copykey]];
if (!isNaN(copy.namespace[copykey])) {
to_be_copied += ".#";
} else {
to_be_copied += "." + copy.namespace[copykey];
}
}
}
if (newitem !== undefined && newitem !== null) {
newitem = newitem[copy.name];
if (!isNaN(copy.name)) {
to_be_copied += ".#";
} else {
to_be_copied += "." + copy.name;
}
}
to_be_copied.replaceAll(" ", "_");
const elementName = "copy_element_shuffle";
var copyText = document.getElementById(elementName);
if (copyText !== null && copyText !== undefined) {
console.log("NAVIGATOR: ", navigator);
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
toast("Can only copy over HTTPS (port 3443)");
return;
}
navigator.clipboard.writeText(to_be_copied);
copyText.select();
copyText.setSelectionRange(0, 99999); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
console.log("COPYING!");
toast("Copied JSON path to clipboard.")
} else {
console.log("Couldn't find element ", elementName);
}
}
const aiSubmit = (value, inputAction) => {
if (value === undefined || value === "") {
console.log("No value input!")
@@ -938,10 +1013,10 @@ const CodeEditor = (props) => {
style: {
zIndex: 12501,
color: "white",
minWidth: isMobile ? "100%" : isFileEditor ? 650 : 1200,
maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1200,
minHeight: isMobile ? "100%" : 720,
maxHeight: isMobile ? "100%" : 720,
minWidth: isMobile ? "100%" : isFileEditor ? 650 : 1165,
maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100,
minHeight: isMobile ? "100%" : 700,
maxHeight: isMobile ? "100%" : 700,
border: theme.palette.defaultBorder,
padding: isMobile ? "25px 10px 25px 10px" : 25,
},
@@ -951,8 +1026,8 @@ const CodeEditor = (props) => {
style={{
zIndex: 5000,
position: "absolute",
top: 14,
right: 18,
top: 6,
right: 6,
color: "grey",
}}
onClick={() => {
@@ -1233,7 +1308,7 @@ const CodeEditor = (props) => {
handleItemClick([innerdata]);
}}
>
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-270, padding: "10px 0px 10px 10px", backgroundColor: theme.palette.inputColor, overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
<Paper style={{minHeight: 550, maxHeight: 550, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-270, padding: "10px 0px 10px 10px", backgroundColor: theme.palette.inputColor, overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
<MenuItem
key={innerdata.name}
style={{
@@ -1425,22 +1500,26 @@ const CodeEditor = (props) => {
borderRadius: theme.palette.borderRadius,
position: "relative",
paddingTop: 0,
minHeight: 548,
overflow: "hidden",
// minHeight: 548,
// overflow: "hidden",
}}>
<CodeMirror
theme={vscodeDark}
value={localcodedata}
height={isFileEditor ? 450 : 500}
width={isFileEditor ? 650 : 600}
extensions={[python({ py: true })]}
height={isFileEditor ? 450 : 450}
width={isFileEditor ? 650 : 550}
style={{
maxWidth: isFileEditor ? 450 : 600,
maxHeight: 548,
minHeight: 548,
maxHeight: 450,
minHeight: 450,
wordBreak: "break-word",
marginTop: 0,
paddingBottom: 10,
overflow: "hidden",
// overflow: "hidden",
overflowY: "auto",
whiteSpace: "pre-wrap",
wordWrap: "break-word",
}}
onCursorActivity = {(value) => {
console.log("CURSOR: ", value.getCursor())
@@ -1463,122 +1542,17 @@ const CodeEditor = (props) => {
}}
options={{
mode: validation === true ? "json" : "python",
lineWrapping: linewrap,
lineWrapping: true,
theme: vscodeDark,
lineNumbers: true,
}}
/>
</div>
{/*editorPopupOpen ?
<Paper
style={{
margin: 10,
padding: 10,
width: isMobile ? "100%" : 250,
height: 95,
overflowY: 'auto',
// textOverflow: 'ellipsis'
}}
>
{mainVariables.map((data, index) => {
// console.log(data)
return (
<div
style={{
// textOverflow: 'ellipsis'
}}
>
<button
onClick={() => {
replaceVariables(data.substring(1,))
// console.log(currentCharacter, currentLine)
}}
style={{
backgroundColor: 'transparent',
color: 'white',
border: 'none',
padding: 7.5,
cursor: 'pointer',
width: '100%',
textAlign: 'left'
}}
>
{data.substring(0, 25)}
</button>
</div>
)
})}
</Paper>
: null*/}
<div
style={{
}}
>
{/*
<Typography
variant = 'body2'
color = 'textSecondary'
style={{
color: "white",
paddingLeft: 340,
width: 50,
display: 'inline',
}}
>
Line Wrap
<Checkbox
onClick={() => {
if (linewrap) {
setlinewrap(false)
}
if (!linewrap){
setlinewrap(true)
}
}}
defaultChecked
size="small"
sx={{
color: orange[600],
'&.Mui-checked': {
color: orange[800],
},
}}
/>
</Typography>
<Typography
variant = 'body2'
color = 'textSecondary'
style={{
color: "white",
paddingLeft: 10,
width: 100,
display: 'inline',
}}
>
Dark Theme
<Checkbox
onClick={() => {
if (codeTheme === "gruvbox-dark") {
setcodeTheme("duotone-light")
}
if (codeTheme === "duotone-light"){
setcodeTheme("gruvbox-dark")
}
}}
defaultChecked
size="small"
sx={{
color: orange[600],
'&.Mui-checked': {
color: orange[800],
},
}}
/>
</Typography>
*/}
</div>
</div>
@@ -1593,83 +1567,93 @@ const CodeEditor = (props) => {
display: "flex",
}}
>
<span style={{color: "white"}}>
Expected Output
</span>
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
<Button
variant="outlined"
disabled={executing}
color="primary"
style={{
border: `1px solid ${theme.palette.primary.main}`,
marginLeft: 200,
maxHeight: 35,
minWidth: 70,
}}
variant="contained"
onClick={() => {
executeSingleAction(expOutput)
}}
>
{executing ?
<CircularProgress style={{height: 18, width: 18, }} />
:
<span>Try it <PlayArrowIcon style={{height: 18, width: 18, marginBottom: -4, marginLeft: 5, }} /> </span>
}
</Button>
</Tooltip>
<div>
<span style={{color: "white"}}>
Expected Output
</span>
</div>
</DialogTitle>
}
{isMobile ? null :
validation === true ?
<ReactJson
src={expOutput}
theme={theme.palette.jsonTheme}
<div style={{position: "relative", }}>
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
<Button
variant="outlined"
disabled={executing}
color="primary"
style={{
borderRadius: 5,
border: `2px solid ${theme.palette.inputColor}`,
padding: 10,
maxHeight: 500,
minheight: 500,
overflow: "auto",
}}
collapsed={false}
enableClipboard={(copy) => {
//handleReactJsonClipboard(copy);
}}
displayDataTypes={false}
onSelect={(select) => {
//HandleJsonCopy(validate.result, select, "exec");
}}
name={"JSON autocompletion"}
/>
:
<p
id='expOutput'
style={{
whiteSpace: "pre-wrap",
color: "#ebdbb2",
fontFamily: "monospace",
backgroundColor: "#282828",
padding: 10,
marginTop: -2,
border: `2px solid ${theme.palette.inputColor}`,
borderRadius: theme.palette.borderRadius,
maxHeight: 500,
minHeight: 500,
minWidth: 580,
maxWidth: 580,
overflow: "auto",
whiteSpace: "pre-wrap",
border: `1px solid ${theme.palette.primary.main}`,
position: "absolute",
top: 10,
right: 10,
maxHeight: 35,
minWidth: 70,
}}
variant="contained"
onClick={() => {
executeSingleAction(expOutput)
}}
>
{expOutput}
</p>
}
{executing ?
<CircularProgress style={{height: 18, width: 18, }} />
:
<span>Try it <PlayArrowIcon style={{height: 18, width: 18, marginBottom: -4, marginLeft: 5, }} /> </span>
}
</Button>
</Tooltip>
{isMobile ? null :
validation === true ?
<ReactJson
src={expOutput}
theme={theme.palette.jsonTheme}
style={{
borderRadius: 5,
border: `2px solid ${theme.palette.inputColor}`,
padding: 10,
maxHeight: 450,
minheight: 450,
overflow: "auto",
}}
collapsed={false}
enableClipboard={(copy) => {
//handleReactJsonClipboard(copy);
}}
displayDataTypes={false}
onSelect={(select) => {
var basename = "exec"
if (selectedAction !== undefined && selectedAction !== null && Object.keys(selectedAction).length !== 0) {
basename = selectedAction.label.toLowerCase().replaceAll(" ", "_")
}
HandleJsonCopy(expOutput, select, basename)
}}
name={"JSON autocompletion"}
/>
:
<p
id='expOutput'
style={{
whiteSpace: "pre-wrap",
color: "#ebdbb2",
fontFamily: "monospace",
backgroundColor: "#282828",
padding: 10,
marginTop: -2,
border: `2px solid ${theme.palette.inputColor}`,
borderRadius: theme.palette.borderRadius,
maxHeight: 450,
minHeight: 450,
minWidth: 480,
maxWidth: "100%",
overflow: "auto",
whiteSpace: "pre-wrap",
}}
>
{expOutput}
</p>
}
</div>
{executionResult.valid === true ?
<ReactJson
@@ -1689,7 +1673,7 @@ const CodeEditor = (props) => {
}}
displayDataTypes={false}
onSelect={(select) => {
//HandleJsonCopy(validate.result, select, "exec");
//HandleJsonCopy(executionResult.result, select, "exec");
}}
name={"Test result"}
/>
@@ -1705,9 +1689,18 @@ const CodeEditor = (props) => {
</Typography>
</span>
:
<Typography variant="body2" style={{maxHeight: 150, overflow: "auto", marginTop: 20,}}>
No test output yet.
</Typography>
<div>
<Typography
variant = 'body2'
color = 'textSecondary'
>
Output is based on the last VALID run of the node(s) you are referencing. Only updates when you refresh the Workflow Window.
</Typography>
<Typography variant="body2" style={{maxHeight: 150, overflow: "auto", marginTop: 20,}}>
No test output yet.
</Typography>
</div>
}
{executionResult.errors !== undefined && executionResult.errors !== null && executionResult.errors.length > 0 ?
<Typography variant="body2" style={{maxHeight: 100, overflow: "auto", color: "#f85a3e",}}>
@@ -1725,7 +1718,7 @@ const CodeEditor = (props) => {
</div>
<div style={{display: 'flex',}}>
<div style={{display: 'flex'}}>
<Button
style={{
height: 35,
+131 -18
View File
@@ -43,6 +43,8 @@ const WorkflowTemplatePopup = (props) => {
const [missingSource, setMissingSource] = React.useState(undefined)
const [missingDestination, setMissingDestination] = React.useState(undefined);
const [configurationFinished, setConfigurationFinished] = React.useState(false);
useEffect(() => {
}, [missingSource, missingDestination])
@@ -175,6 +177,92 @@ const WorkflowTemplatePopup = (props) => {
});
}
// 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
@@ -221,8 +309,10 @@ const WorkflowTemplatePopup = (props) => {
},
}
//fetch(globalUrl + "/api/v1/workflows/merge", {
fetch("https://shuffler.io/api/v1/workflows/merge", {
const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge`
//const url = `https://shuffler.io/api/v1/workflows/merge`
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -243,13 +333,21 @@ const WorkflowTemplatePopup = (props) => {
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);
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.")
const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance."
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
setErrorMessage(defaultMessage + "\n\n" + responseJson.reason)
} else {
setErrorMessage(defaultMessage)
}
setIsActive(true)
//setTimeout(() => {
@@ -365,7 +463,6 @@ const WorkflowTemplatePopup = (props) => {
fontSize: 18,
color: "rgba(255, 132, 68, 1)",
marginTop: 32,
justifyContent: isMobile ? "center" : null,
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
fontWeight: 550,
}}
@@ -420,8 +517,22 @@ const WorkflowTemplatePopup = (props) => {
appAuthentication={appAuthentication}
setAppAuthentication={setAppAuthentication}
apps={apps}
setConfigurationFinished={setConfigurationFinished}
/>
{errorMessage === "" ?
{errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
<Tooltip title="Workflow generated!" placement="top">
<span style={{display: "flex", }}>
{/*<CheckIcon color="primary" sx={{ borderRadius: 4 }} /> */}
<Typography variant="h6" style={{ marginLeft: 20, }}>
Workflow generated!
</Typography>
</span>
</Tooltip>
: null}
{/*errorMessage === "" ?
<Button
style={{marginTop: 50, }}
variant={isFinished() ? "contained" : "outlined"}
@@ -431,7 +542,7 @@ const WorkflowTemplatePopup = (props) => {
>
Done
</Button>
: null}
: null*/}
</DialogContent>
</Drawer>
)
@@ -449,7 +560,7 @@ const WorkflowTemplatePopup = (props) => {
return (
<div style={{ display: "flex", maxWidth: isCloud ? 470 : isMobile? null: 450, minWidth: isCloud ? 470 : isMobile? null: 450, height: 78, borderRadius: 8, }}>
<div style={{ display: "flex", maxWidth: isCloud ? isMobile ? null : 470 : isMobile? 345: 450, minWidth: isCloud ? isMobile ? null : 470 : isMobile? null: 450, height: 78, borderRadius: 8, justifyContent: isMobile ? null : "center" }}>
<ModalView />
<div
// variant={isActive === 1 ? "contained" : "outlined"}
@@ -457,7 +568,7 @@ const WorkflowTemplatePopup = (props) => {
disabled={visualOnly === true}
style={{
margin: isHomePage ? isMobile ? null : 4 : 4 ,
width: "100%",
width: isHomePage? isMobile ? null : "100%" : "100%",
borderRadius: 8,
textTransform: "none",
backgroundColor: isHomePage ? null : theme.palette.inputColor,
@@ -503,31 +614,31 @@ const WorkflowTemplatePopup = (props) => {
<div style={{display: "flex", flex: 1, marginTop: 3, }}>
{img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ?
<Tooltip title={srcapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
<span style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
<div style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
<img src={img1} style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleDefault : imagestyle} />
</span>
</div>
</Tooltip>
:
<span style={{width: 50, }} />
<div style={{width: 50, }} />
}
{img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ?
<Tooltip title={dstapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
<span style={{display: "flex", }}>
<div style={{display: "flex", }}>
<TrendingFlatIcon style={{ marginTop: 7, }} />
<span style={dstapp !== undefined && dstapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
<div style={dstapp !== undefined && dstapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
<img src={img2} style={dstapp !== undefined && dstapp.includes(":default") ? imagestyleDefault : imagestyle} />
</span>
</span>
</div>
</div>
</Tooltip>
:
<span style={{width: 50, }} />
<div style={{width: 50, }} />
}
</div>
<div style={{ flex: 3, marginLeft: 20, }}>
<div style={{ flex: 3, marginLeft: 20, maxHeight: 50, overflow: "hidden", }}>
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, fontSize: isMobile ? 13 : 16,fontWeight: isHomePage? 600 : null,textTransform: 'capitalize', color: isHomePage ? "var(--White-text, #F1F1F1)" :"rgba(241, 241, 241, 1)"}} >
{parsedTitle}
</Typography>
<Typography variant="body2" color="textSecondary" style={{ fontSize: isMobile ? 10: 16, fontWeight: isHomePage ? 400 : null, textTransform: 'capitalize', marginTop: 0, overflow: "hidden", maxHeight: 21, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
<Typography variant="body2" color="textSecondary" style={{ fontSize: isMobile ? 10: 16, fontWeight: isHomePage ? 400 : null, textTransform: 'capitalize', marginTop: 0, overflow: "hidden", maxHeight: 31,}} color="rgba(158, 158, 158, 1)">
{parsedDescription}
</Typography>
</div>
@@ -538,6 +649,8 @@ const WorkflowTemplatePopup = (props) => {
: ""}
</div>
</div>
</div>
)
}
+1
View File
@@ -31,6 +31,7 @@ const theme = createTheme(adaptV4Theme({
borderRadius: 5,
border: "1px solid rgba(255,255,255,0.7)",
padding: 5,
width: "100%",
},
textFieldStyle: {
backgroundColor: "#383B40",
+354 -99
View File
@@ -73,6 +73,7 @@ import {
Visibility as VisibilityIcon,
VisibilityOff as VisibilityOffIcon,
Flag as FlagIcon,
FmdGood as FmdGoodIcon,
} from "@mui/icons-material";
@@ -87,7 +88,6 @@ import Priorities from "../components/Priorities.jsx";
import Branding from "../components/Branding.jsx";
import Files from "../components/Files.jsx";
import { display, style } from "@mui/system";
//import EnvironmentStats from "../components/EnvironmentStats.jsx";
const useStyles = makeStyles({
notchedOutline: {
@@ -180,10 +180,12 @@ const Admin = (props) => {
const [secret2FA, setSecret2FA] = React.useState("");
const [show2faSetup, setShow2faSetup] = useState(false);
const [adminTab, setAdminTab] = React.useState(2);
const [showApiKey, setShowApiKey] = useState(false);
const [adminTab, setAdminTab] = React.useState(3);
const [showApiKey, setShowApiKey] = useState(false);
const [billingInfo, setBillingInfo] = React.useState({});
const [selectedStatus, setSelectedStatus] = React.useState([]);
const [selectedStatus, setSelectedStatus] = React.useState([]);
const [, forceUpdate] = React.useState();
useEffect(() => {
getUsers()
@@ -748,11 +750,20 @@ If you're interested, please let me know a time that works for you, or set up a
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed changing authentication");
// Check if .reason exists
if (responseJson.reason !== undefined) {
toast("Failed changing authentication: " + responseJson.reason);
} else {
toast("Failed changing authentication");
}
} else {
//toast("Successfully password!")
setSelectedUserModalOpen(false);
getAppAuthentication();
setSelectedAuthentication({});
setSelectedAuthenticationModalOpen(false);
}
})
)
@@ -813,7 +824,8 @@ If you're interested, please let me know a time that works for you, or set up a
const data = {
id: id,
action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere",
};
}
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config";
fetch(url, {
@@ -999,62 +1011,62 @@ If you're interested, please let me know a time that works for you, or set up a
responseJson.sync_features = {};
}
if (responseJson.lead_info !== undefined && responseJson.lead_info !== null) {
var leads = []
if (responseJson.lead_info.contacted) {
leads.push("contacted")
}
if (responseJson.lead_info !== undefined && responseJson.lead_info !== null) {
var leads = []
if (responseJson.lead_info.contacted) {
leads.push("contacted")
}
if (responseJson.lead_info.customer) {
leads.push("customer")
}
if (responseJson.lead_info.customer) {
leads.push("customer")
}
if (responseJson.lead_info.old_customer) {
leads.push("old customer")
}
if (responseJson.lead_info.old_customer) {
leads.push("old customer")
}
if (responseJson.lead_info.old_lead) {
leads.push("old lead")
}
if (responseJson.lead_info.old_lead) {
leads.push("old lead")
}
if (responseJson.lead_info.tech_partner) {
leads.push("tech partner")
}
if (responseJson.lead_info.tech_partner) {
leads.push("tech partner")
}
if (responseJson.lead_info.creator) {
leads.push("creator")
}
if (responseJson.lead_info.creator) {
leads.push("creator")
}
if (responseJson.lead_info.opensource) {
leads.push("open source")
}
if (responseJson.lead_info.opensource) {
leads.push("open source")
}
if (responseJson.lead_info.demo_done) {
leads.push("demo done")
}
if (responseJson.lead_info.demo_done) {
leads.push("demo done")
}
if (responseJson.lead_info.pov) {
leads.push("pov")
}
if (responseJson.lead_info.pov) {
leads.push("pov")
}
if (responseJson.lead_info.lead) {
leads.push("lead")
}
if (responseJson.lead_info.lead) {
leads.push("lead")
}
if (responseJson.lead_info.student) {
leads.push("student")
}
if (responseJson.lead_info.student) {
leads.push("student")
}
if (responseJson.lead_info.internal) {
leads.push("internal")
}
if (responseJson.lead_info.internal) {
leads.push("internal")
}
if (responseJson.lead_info.sub_org) {
leads.push("sub_org")
}
if (responseJson.lead_info.sub_org) {
leads.push("sub_org")
}
setSelectedStatus(leads)
}
setSelectedStatus(leads)
}
setSelectedOrganization(responseJson)
var lists = {
@@ -1491,6 +1503,19 @@ If you're interested, please let me know a time that works for you, or set up a
})
.then((responseJson) => {
setEnvironments(responseJson);
// Helper info for users in case they have a large queue and don't know about queue flushing
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) {
for (var i = 0; i < responseJson.length; i++) {
const env = responseJson[i];
// Check if queuesize is too large
if (env.queue !== undefined && env.queue !== null && env.queue > 100) {
toast("Queue size for " + env.name + " is very large. We recommend you to reduce it by flushing the queue before continuing.");
break
}
}
}
})
.catch((error) => {
toast(error.toString());
@@ -1798,17 +1823,60 @@ If you're interested, please let me know a time that works for you, or set up a
>
<DialogTitle>
<span style={{ color: "white" }}>
Edit authentication for {selectedAuthentication.app.name} (
Edit authentication for {selectedAuthentication.app.name.replaceAll("_", " ")} (
{selectedAuthentication.label})
</span>
<Typography variant="body1" color="textSecondary" style={{marginTop: 10}}>
You can <b>not</b> see the previous values for an authentication while editing. This is to keep your data secure. You can overwrite one- or multiple fields at a time.
</Typography>
</DialogTitle>
<DialogContent>
{selectedAuthentication.fields.map((data, index) => {
<Typography style={{ marginBottom: 0, marginTop: 10 }}>
Authentication Label
</Typography>
<TextField
style={{
backgroundColor: theme.palette.inputColor,
marginTop: 0,
}}
InputProps={{
style: {
height: 50,
color: "white",
},
}}
color="primary"
required
fullWidth={true}
placeholder={selectedAuthentication.label}
defaultValue={selectedAuthentication.label}
type="text"
margin="normal"
variant="outlined"
onChange={(e) => {
selectedAuthentication.label = e.target.value
}}
/>
<Divider />
{selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
<div>
<Typography variant="body1" color="textSecondary" style={{ marginBottom: 0, marginTop: 10 }}>
Only the name of the auth can be modified for Oauth2. Please remake the authentication to change the fields like Client ID, Secret, Scopes etc.
</Typography>
</div>
:
selectedAuthentication.fields.map((data, index) => {
var fieldname = data.key.replaceAll("_", " ")
if (fieldname.endsWith(" basic")) {
fieldname = fieldname.substring(0, fieldname.length - 6)
}
//console.log("DATA: ", data, selectedAuthentication)
return (
<div key={index}>
<Typography style={{ marginBottom: 0, marginTop: 10 }}>
{data.key}
{fieldname}
</Typography>
<TextField
style={{
@@ -1824,7 +1892,7 @@ If you're interested, please let me know a time that works for you, or set up a
color="primary"
required
fullWidth={true}
placeholder={data.key}
placeholder={fieldname}
type="text"
id={`authentication-${index}`}
margin="normal"
@@ -1851,9 +1919,11 @@ If you're interested, please let me know a time that works for you, or set up a
style={{ borderRadius: "0px" }}
onClick={() => {
var error = false;
var fails = 0
for (var key in authenticationFields) {
const item = authenticationFields[key];
if (item.value.length === 0) {
fails += 1
console.log("ITEM: ", item);
//var currentnode = cy.getElementById(data.id)
var textfield = document.getElementById(
@@ -1866,14 +1936,17 @@ If you're interested, please let me know a time that works for you, or set up a
}
}
if (error) {
toast("All fields must have a new value");
if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") {
selectedAuthentication.fields = []
}
if (error && fails === authenticationFields.length) {
toast("Updating auth with new name only")
saveAuthentication(selectedAuthentication);
} else {
toast("Saving new version of this authentication");
selectedAuthentication.fields = authenticationFields;
saveAuthentication(selectedAuthentication);
setSelectedAuthentication({});
setSelectedAuthenticationModalOpen(false);
}
}}
color="primary"
@@ -2195,35 +2268,124 @@ If you're interested, please let me know a time that works for you, or set up a
const GridItem = (props) => {
const [expanded, setExpanded] = React.useState(false);
const [showEdit, setShowEdit] = React.useState(false);
const [newValue, setNewValue] = React.useState(-100);
const primary = props.data.primary;
const secondary = props.data.secondary;
const primaryIcon = props.data.icon;
const secondaryIcon = props.data.active ? (
const secondaryIcon = props.data.active ?
<CheckCircleIcon style={{ color: "green" }} />
) : (
:
<CloseIcon style={{ color: "red" }} />
)
const submitFeatureEdit = (sync_features) => {
if (!userdata.support) {
console.log("User does not have support access and can't edit features");
return
}
sync_features.editing = true
const data = {
org_id: selectedOrganization.id,
sync_features: sync_features,
};
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
fetch(url, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed updating org: ", responseJson.reason);
} else {
toast("Successfully edited org!");
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
}
const enableFeature = () => {
console.log("Enabling "+primary)
console.log(selectedOrganization.sync_features)
// Check if primary is in sync_features
var tmpprimary = primary.replaceAll(" ", "_")
if (!(tmpprimary in selectedOrganization.sync_features)) {
console.log("Primary not in sync_features: "+tmpprimary)
return
}
if (props.data.active) {
selectedOrganization.sync_features[tmpprimary].active = false
} else {
selectedOrganization.sync_features[tmpprimary].active = true
}
setSelectedOrganization(selectedOrganization)
forceUpdate(Math.random())
submitFeatureEdit(selectedOrganization.sync_features)
}
const submitEdit = (e) => {
e.preventDefault();
e.stopPropagation();
// Check if primary is in sync_features
var tmpprimary = primary.replaceAll(" ", "_")
if (!(tmpprimary in selectedOrganization.sync_features)) {
console.log("Primary not in sync_features: "+tmpprimary)
return
}
// Make it into a number
var tmp = parseInt(newValue)
if (isNaN(tmp)) {
console.log("Not a number: "+newValue)
return
}
selectedOrganization.sync_features[tmpprimary].limit = tmp
setSelectedOrganization(selectedOrganization)
forceUpdate(Math.random())
submitFeatureEdit(selectedOrganization.sync_features)
}
return (
<Grid
item
xs={4}
style={{ cursor: "pointer" }}
onClick={() => {
setExpanded(!expanded);
}}
>
<Card
style={{
margin: 4,
backgroundColor: theme.palette.surfaceColor,
backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius,
border: "1px solid rgba(255,255,255,0.3)",
color: "white",
minHeight: expanded ? 250 : "inherit",
maxHeight: expanded ? 250 : "inherit",
maxHeight: expanded ? 300 : "inherit",
}}
>
<ListItem>
<ListItem
style={{cursor: "pointer", }}
onClick={() => {
setExpanded(!expanded);
}}
>
<ListItemAvatar>
<Avatar>{primaryIcon}</Avatar>
</ListItemAvatar>
@@ -2231,9 +2393,46 @@ If you're interested, please let me know a time that works for you, or set up a
style={{ textTransform: "capitalize" }}
primary={primary}
/>
{secondaryIcon}
{isCloud && userdata.support === true ?
<Tooltip title="Edit features (support users only)">
<EditIcon
color="secondary"
style={{marginRight: 10, cursor: "pointer", }}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (showEdit) {
setShowEdit(false)
return
}
console.log("Edit")
setShowEdit(true)
}}
/>
</Tooltip>
: null}
<Tooltip title={props.data.active ? "Disable feature" : "Enable feature"}>
<span
style={{cursor: "pointer", marginTop: 5, }}
onClick={(e) => {
if (!isCloud || userdata.support !== true) {
return
}
e.preventDefault();
e.stopPropagation();
enableFeature()
}}
>
{secondaryIcon}
</span>
</Tooltip>
</ListItem>
{expanded ? (
{expanded ?
<div style={{ padding: 15 }}>
<Typography>
<b>Usage:&nbsp;</b>
@@ -2250,7 +2449,41 @@ If you're interested, please let me know a time that works for you, or set up a
</Typography>*/}
<Typography style={{maxHeight: 150, overflowX: "hidden", overflowY: "auto"}}><b>Description:</b> {secondary}</Typography>
</div>
) : null}
: null}
{showEdit ?
<FormControl fullWidth onSubmit={(e) => {
console.log("Submit")
submitEdit(e)
}}>
<span style={{display: "flex", }}>
<TextField
style={{flex: 3, }}
color="primary"
label={"Edit value"}
defaultValue={props.data.limit}
style={{
}}
onChange={(event) => {
setNewValue(event.target.value)
}}
/>
<Button
style={{flex: 1, }}
variant="contained"
disabled={newValue < -1}
onClick={(e) => {
console.log("Submit 2")
submitEdit(e)
}}
>
Submit
</Button>
</span>
</FormControl>
: null}
</Card>
</Grid>
);
@@ -2380,16 +2613,23 @@ If you're interested, please let me know a time that works for you, or set up a
</Dialog>
);
var regiontag = "eu";
if (userdata.region_url !== undefined && userdata.region_url !== null && userdata.region_url.length > 0) {
const regionsplit = userdata.region_url.split(".");
if (regionsplit.length > 2 && !regionsplit[0].includes("shuffler")) {
const namesplit = regionsplit[0].split("/");
regiontag = namesplit[namesplit.length - 1];
}
}
const organizationView =
curTab === 0 && selectedOrganization.id !== undefined ? (
<div style={{ position: "relative" }}>
<div style={{ marginTop: 20, marginBottom: 20 }}>
<h2 style={{ display: "inline" }}>Organization overview</h2>
<span style={{ marginLeft: 25 }}>
On this page you can configure individual parts of your
organization.{" "}
<Typography variant="body1" color="textSecondary" style={{ marginLeft: 0}}>
On this page organization admins can configure organisations, and sub-orgs (MSSP).{" "}
<a
target="_blank"
rel="noopener noreferrer"
@@ -2398,7 +2638,7 @@ If you're interested, please let me know a time that works for you, or set up a
>
Learn more
</a>
</span>
</Typography>
</div>
{selectedOrganization.id === undefined ? (
<div
@@ -2471,6 +2711,21 @@ If you're interested, please let me know a time that works for you, or set up a
</FormControl>
</span>
: null}
{isCloud ?
<Tooltip
title={`Organization is in ${regiontag}. Click to change!`}
style={{}}
>
<Avatar
style={{ top: -10, right: 50, position: "absolute", }}
onClick={() => {
toast("Region change is not implemented yet for users. Please contact support.")
}}
>
{regiontag}
</Avatar>
</Tooltip>
: null}
<Tooltip
title={"Copy Organization ID"}
@@ -2550,13 +2805,13 @@ If you're interested, please let me know a time that works for you, or set up a
value={adminTab}
indicatorColor="primary"
textColor="secondary"
style={{marginTop: 20, }}
style={{marginTop: 50, }}
onChange={(event, inputValue) => {
const newValue = parseInt(inputValue);
const newValue = parseInt(inputValue);
setAdminTab(newValue);
//const setConfig = (event, inputValue) => {
navigate(`/admin?admin_tab=${admin_views[newValue]}`);
//const setConfig = (event, inputValue) => {
navigate(`/admin?admin_tab=${admin_views[newValue]}`);
}}
aria-label="disabled tabs example"
>
@@ -2577,7 +2832,7 @@ If you're interested, please let me know a time that works for you, or set up a
/>
<Tab
label=<span>
Licensing
Billing & Stats
</span>
/>
<Tab
@@ -2777,12 +3032,12 @@ If you're interested, please let me know a time that works for you, or set up a
</div>
)}
<Typography variant="h6" style={{ marginLeft: 5, marginTop: 40, marginBottom: 5 }}>
Cloud sync features
Features
</Typography>
<Typography variant="body2" color="textSecondary" style={{marginBottom: 10, marginLeft: 5, }}>
If not otherwise specified, Usage will reset monthly
Features and Limitations that are currently available to you in your Cloud or Hybrid Organization. App Executions (App Runs) reset monthly. If the organization is a customer or in a trial, these features limitations are not always enforced.
</Typography>
<Grid container style={{ width: "100%", marginBottom: 15 }}>
<Grid container style={{ width: "100%", marginBottom: 15, paddingBottom: 150, }}>
{selectedOrganization.sync_features === undefined ||
selectedOrganization.sync_features === null
@@ -2791,15 +3046,15 @@ If you're interested, please let me know a time that works for you, or set up a
key,
index
) {
// unnecessary parts
if (key === "schedule" || key === "apps" || key === "updates") {
if (key === "schedule" || key === "apps" || key === "updates" || key === "editing") {
return null;
}
const item = selectedOrganization.sync_features[key];
if (item === null) {
return null
}
if (item === null) {
return null
}
const newkey = key.replaceAll("_", " ");
const griditem = {
@@ -3732,7 +3987,7 @@ If you're interested, please let me know a time that works for you, or set up a
}}
/>
<ListItemText
primary={data.app.name}
primary={data.app.name.replaceAll("_", " ")}
style={{ minWidth: 175, maxWidth: 175, marginLeft: 10 }}
/>
{/*
@@ -3799,7 +4054,7 @@ If you're interested, please let me know a time that works for you, or set up a
{data.defined ? (
<Tooltip
color="primary"
title="Set in EVERY workflow in the organization"
title="Set for EVERY instance of this App being used in this organization"
placement="top"
>
<IconButton
@@ -3983,7 +4238,7 @@ If you're interested, please let me know a time that works for you, or set up a
/>
<ListItemText
primary="Type"
style={{ minWidth: 125, maxWidth: 125 }}
style={{ minWidth: 100, maxWidth: 100, }}
/>
<ListItemText
primary={"In Queue"}
@@ -3991,7 +4246,7 @@ If you're interested, please let me know a time that works for you, or set up a
/>
<ListItemText
primary="Default"
style={{ minWidth: 150, maxWidth: 150 }}
style={{ minWidth: 100, maxWidth: 100 }}
/>
<ListItemText
primary="Actions"
@@ -4031,7 +4286,7 @@ If you're interested, please let me know a time that works for you, or set up a
//console.log("Show CPU alert: ", showCPUAlert)
const queueSize = environment.queue !== undefined && environment.queue !== null ? environment.queue < 0 ? 0 : environment.queue > 99 ? ">99" : environment.queue : 0
const queueSize = environment.queue !== undefined && environment.queue !== null ? environment.queue < 0 ? 0 : environment.queue > 1000 ? ">1000" : environment.queue : 0
return (
<span key={index}>
@@ -4121,22 +4376,22 @@ If you're interested, please let me know a time that works for you, or set up a
<ListItemText
primary={environment.Type}
style={{ minWidth: 125, maxWidth: 125 }}
style={{ minWidth: 100, maxWidth: 100, }}
/>
<ListItemText
style={{
minWidth: 100,
maxWidth: 100,
overflow: "hidden",
marginLeft: 10,
marginLeft: 0,
}}
primary={queueSize}
/>
<ListItemText
style={{
minWidth: 140,
maxWidth: 140,
minWidth: 80,
maxWidth: 80,
overflow: "hidden",
}}
primary={environment.default ? "true" : null}
@@ -4144,7 +4399,7 @@ If you're interested, please let me know a time that works for you, or set up a
{environment.default ? null : (
<Button
variant="outlined"
style={{ marginRight: 5 }}
style={{ marginLeft: 0, marginRight: 0, }}
onClick={() => setDefaultEnvironment(environment)}
color="primary"
>
@@ -4242,7 +4497,7 @@ If you're interested, please let me know a time that works for you, or set up a
<div style={{ marginTop: 20, marginBottom: 20 }}>
<h2 style={{ display: "inline" }}>Organizations</h2>
<span style={{ marginLeft: 25 }}>
Global admin: control organizations
Control sub organizations (tenants)! {isCloud ? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out." : ""}. <a href="/docs/organizations" target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: theme.palette.primary.main }}>Learn more</a>
</span>
</div>
<Button
+417 -103
View File
@@ -128,8 +128,8 @@ import Draggable from "react-draggable";
import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
import { validateJson, GetIconInfo } from "./Workflows.jsx";
import { GetParsedPaths } from "./Apps.jsx";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import ParsedAction from "../components/ParsedAction.jsx";
@@ -523,6 +523,7 @@ const AngularWorkflow = (defaultprops) => {
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
const [workflowExecutionCount, setWorkflowExecutionCount] = React.useState(0);
const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0);
const [workflowRecommendations, setWorkflowRecommendations] = React.useState(undefined);
@@ -547,6 +548,8 @@ const AngularWorkflow = (defaultprops) => {
"field_id": "",
})
const [executionArgumentModalOpen, setExecutionArgumentModalOpen] = React.useState(false);
// This should all be set once, not on every iteration
// Use states and don't update lol
const cloudSyncEnabled =
@@ -709,6 +712,33 @@ const AngularWorkflow = (defaultprops) => {
});
};
const getWorkflowExecutionCount = (workflowId) => {
fetch(`${globalUrl}/api/v1/workflows/${workflowId}/executions/count`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflow execution count: O!");
return;
} else {
return response.json();
}
})
.then((responseJson) => {
if (responseJson !== undefined) {
setWorkflowExecutionCount(responseJson.count || 0);
}
})
.catch((error) => {
toast(error.toString());
});
};
const getAvailableWorkflows = (trigger_index) => {
fetch(globalUrl + "/api/v1/workflows", {
method: "GET",
@@ -992,6 +1022,7 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
console.log("GOT A RESPONSE??")
// getWorkflowExecutionCount(id);
if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null) {
// - means it's opposite
@@ -1362,7 +1393,7 @@ const AngularWorkflow = (defaultprops) => {
}
if (streamDisabled) {
console.log("Stream disabled")
console.log("Stream disabled - send")
return
}
@@ -1377,13 +1408,20 @@ const AngularWorkflow = (defaultprops) => {
const streamUrl = "https://stream.shuffler.io"
const url = `${streamUrl}/api/v1/workflows/${props.match.params.key}/stream`
var parsedbody = body
try {
parsedbody = JSON.stringify(body)
} catch (e) {
console.log("Error parsing body for stream: ", e)
}
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(body),
body: parsedbody,
credentials: "include",
})
.then((response) => {
@@ -1733,6 +1771,7 @@ const AngularWorkflow = (defaultprops) => {
};
const executeWorkflow = (executionArgument, startNode, hasSaved) => {
ReactDOM.unstable_batchedUpdates(() => {
if (hasSaved === false) {
setExecutionRequestStarted(true);
@@ -1755,6 +1794,41 @@ const AngularWorkflow = (defaultprops) => {
setExecutionRequest({})
stop()
// FIXME: Check if any node contains $exec in a param
// If they do, show a popup asking if they want to execute it without an execution argument, or to use a previous one
if (executionArgument === undefined || executionArgument === null || executionArgument.length === 0 && workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
var foundmissing = false
for (let actionkey in workflow.actions) {
if (workflow.actions[actionkey].parameters === undefined || workflow.actions[actionkey].parameters === null || workflow.actions[actionkey].parameters.length === 0) {
continue
}
for (let paramkey in workflow.actions[actionkey].parameters) {
const param = workflow.actions[actionkey].parameters[paramkey]
if (param.value === undefined || param.value === null || param.value.length === 0) {
continue
}
if (param.value.indexOf("$exec") !== -1) {
foundmissing = true
break
}
}
if (foundmissing) {
break
}
}
console.log("FOUNDMISSING: ", foundmissing)
if (foundmissing) {
//toast("This workflow contains a node that requires an execution argument. Please provide one.")
setExecutionRequestStarted(false)
setExecutionArgumentModalOpen(true)
return
}
}
var curelements = cy.elements();
for (let i = 0; i < curelements.length; i++) {
curelements[i].addClass("not-executing-highlight");
@@ -1782,18 +1856,22 @@ const AngularWorkflow = (defaultprops) => {
})
.then((responseJson) => {
if (!responseJson.success) {
//toast("Failed to start: " + responseJson.reason);
toast(responseJson.reason);
//toast.error(responseJson.reason);
setExecutionRunning(false);
setExecutionRequestStarted(false);
stop();
//toast("Failed to start: " + responseJson.reason);
toast(responseJson.reason);
//toast.error(responseJson.reason);
setExecutionRunning(false);
setExecutionRequestStarted(false);
stop();
for (let i = 0; i < curelements.length; i++) {
curelements[i].removeClass("not-executing-highlight");
}
return;
} else {
if (responseJson.execution_id !== undefined && responseJson.execution_id !== null && responseJson.execution_id.length > 0) {
navigate(`?execution_id=${responseJson.execution_id}`)
}
setExecutionRunning(true);
setExecutionRequestStarted(false);
}
@@ -1836,7 +1914,6 @@ const AngularWorkflow = (defaultprops) => {
// This can be used to only show prioritzed ones later
// Right now, it can prioritize authenticated ones
//"Testing",
const internalIds = ["Shuffle Tools", "http", "email"];
const getAppAuthentication = (reset, updateAction, closeMenu) => {
fetch(globalUrl + "/api/v1/apps/authentication", {
@@ -2038,8 +2115,8 @@ const AngularWorkflow = (defaultprops) => {
setApps(responseJson);
if (isCloud) {
setFilteredApps(responseJson.filter((app) => !internalIds.includes(app.name)));
setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name)));
setFilteredApps(responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase())));
setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name.toLowerCase())));
} else {
//setFilteredApps(
@@ -2050,12 +2127,12 @@ const AngularWorkflow = (defaultprops) => {
// )
//);
var tmpFiltered = responseJson.filter((app) => !internalIds.includes(app.name))
var tmpFiltered = responseJson.filter((app) => !internalIds.includes(app.name.toLowerCase()))
//tmpFiltered = sortByKey(tmpFiltered, "activated")
setFilteredApps(tmpFiltered)
//!(!app.activated && app.generated)
setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name)));
setPrioritizedApps(responseJson.filter((app) => internalIds.includes(app.name.toLowerCase())));
}
setAppsLoaded(true)
@@ -2564,7 +2641,6 @@ const AngularWorkflow = (defaultprops) => {
const appendChunks = (result) => {
var chunk = decoder.decode(result.value || new Uint8Array, {stream: !result.done});
console.log("Got chunk: ", chunk)
if (chunk === undefined || chunk === null) {
console.log("Chunk is undefined or null")
@@ -2663,7 +2739,7 @@ const AngularWorkflow = (defaultprops) => {
}
if (streamDisabled) {
console.log("Stream disabled")
console.log("Stream listener disabled")
return
}
@@ -2675,6 +2751,7 @@ const AngularWorkflow = (defaultprops) => {
const url = `${streamUrl}/api/v1/workflows/${workflowId}/stream`
while (true) {
if (streamDisabled === true || streamDisabled2 === true) {
console.log("Stream disabled, breaking")
break
}
@@ -2711,8 +2788,11 @@ const AngularWorkflow = (defaultprops) => {
if (response.status >= 500) {
toast("Something went wrong while loading the workflow. Please reload.")
} else {
toast("You don't access to this workflow or loading failed.")
window.location.pathname = "/workflows";
toast("You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..")
setTimeout(() => {
window.location.pathname = "/workflows";
}, 2000);
}
}
@@ -3652,6 +3732,10 @@ const AngularWorkflow = (defaultprops) => {
return
} else if (data.buttonType === "set_startnode" && data.type !== "TRIGGER") {
//console.log("STARTNODE")
//event.preventDefault()
//event.stopPropagation()
const parentNode = cy.getElementById(data.attachedTo);
if (parentNode !== null && parentNode !== undefined) {
var oldstartnode = cy.getElementById(workflow.start);
@@ -3784,19 +3868,19 @@ const AngularWorkflow = (defaultprops) => {
return;
} else if (data.isDescriptor) {
console.log("Can't select descriptor");
if (data.isTrigger) {
console.log("But maybe we can select trigger descriptor? Maybe open execution tab?")
setExecutionModalOpen(true)
}
if (data.isTrigger) {
console.log("But maybe we can select trigger descriptor? Maybe open execution tab?")
setExecutionModalOpen(true)
}
event.target.unselect();
return;
}
if (data.type === undefined) {
console.log("No type, automatically setting to action");
data.type = "ACTION"
}
if (data.type === undefined) {
console.log("No type, automatically setting to action");
data.type = "ACTION"
}
if (data.type === "ACTION") {
setSelectedComment({})
@@ -3888,8 +3972,6 @@ const AngularWorkflow = (defaultprops) => {
//}
curaction.app_id = curapp.id
console.log("CURAPP: ", curapp.authentication)
setAuthenticationType(
curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null ? {
type: "oauth2",
@@ -3921,41 +4003,58 @@ const AngularWorkflow = (defaultprops) => {
}
const tmpAuth = JSON.parse(JSON.stringify(newAppAuth));
//console.log("FOUND AUTH OPTIONS: ", tmpAuth)
const curappName = curapp.name.toLowerCase()
for (let tmpAuthKey in tmpAuth) {
var item = tmpAuth[tmpAuthKey];
const curappName = curapp.name.toLowerCase()
for (let tmpAuthKey in tmpAuth) {
var item = tmpAuth[tmpAuthKey];
const newfields = {};
if (item.app.name.toLowerCase() !== curappName) {
continue
}
const newfields = {};
if (item.app.name.toLowerCase() !== curappName) {
continue
}
// Makes list into key:value object
for (let fieldkey in item.fields) {
if (item.fields[fieldkey] === undefined) {
console.log("Problem with filterkey in Node select", fieldkey)
continue
}
// Makes list into key:value object
for (let fieldkey in item.fields) {
if (item.fields[fieldkey] === undefined) {
console.log("Problem with filterkey in Node select", fieldkey)
continue
}
const filterkey = item.fields[fieldkey]["key"]
if (filterkey === null || filterkey === undefined) {
console.log("Problem with filterkey 2. Null or undefined 3")
continue
}
const filterkey = item.fields[fieldkey]["key"]
if (filterkey === null || filterkey === undefined) {
console.log("Problem with filterkey 2. Null or undefined 3")
continue
}
newfields[filterkey] = item.fields[fieldkey]["value"];
}
newfields[filterkey] = item.fields[fieldkey]["value"];
}
item.fields = newfields;
if (item.app.name.toLowerCase() === curappName) {
authenticationOptions.push(item);
if (item.id === findAuthId) {
curaction.selectedAuthentication = item;
}
}
}
item.fields = newfields;
if (item.app.name.toLowerCase() === curappName) {
authenticationOptions.push(item);
if (item.id === findAuthId) {
curaction.selectedAuthentication = item;
}
}
}
// Find with authenticationOption (authenticationOptions) has the highest .edited time. In this index, set the "last_modified" to true
var latesttime = 0
var latestindex = -1
for (var i = 0; i < authenticationOptions.length; i++) {
const authopt = authenticationOptions[i]
if (authopt.edited > latesttime) {
latesttime = authopt.edited
latestindex = i
}
}
if (latestindex !== -1) {
authenticationOptions[latestindex].last_modified = true
}
curaction.authentication = authenticationOptions;
if (
@@ -5833,7 +5932,7 @@ const AngularWorkflow = (defaultprops) => {
if (workflow.actions.length < 4) {
addSuggestionButtons(nodedata, event);
} else {
console.log("Too many actions to suggest (for now)")
//console.log("Too many actions to suggest (for now)")
}
}
}
@@ -7467,26 +7566,26 @@ const AngularWorkflow = (defaultprops) => {
const tmpAuth = JSON.parse(JSON.stringify(appAuthentication));
for (let authkey in tmpAuth) {
if (authkey === undefined) {
continue
}
if (authkey === undefined) {
continue
}
var item = tmpAuth[authkey];
const newfields = {};
for (let fieldkey in item.fields) {
if (item.fields[fieldkey] === undefined) {
console.log("Problem with filterkey in Node select", fieldkey)
continue
}
for (let fieldkey in item.fields) {
if (item.fields[fieldkey] === undefined) {
console.log("Problem with filterkey in Node select", fieldkey)
continue
}
const filterkey = item.fields[fieldkey]["key"]
if (filterkey === null || filterkey === undefined) {
console.log("Problem with filterkey 2. Null or undefined 3")
continue
}
const filterkey = item.fields[fieldkey]["key"]
if (filterkey === null || filterkey === undefined) {
console.log("Problem with filterkey 2. Null or undefined 3")
continue
}
newfields[filterkey] = item.fields[fieldkey]["value"];
}
newfields[filterkey] = item.fields[fieldkey]["value"];
}
item.fields = newfields;
if (item.app.id === app.id || item.app.name === app.name) {
@@ -7733,9 +7832,9 @@ const AngularWorkflow = (defaultprops) => {
const [visibleApps, setVisibleApps] = React.useState(
Array.prototype.concat.apply(
prioritizedApps,
filteredApps.filter((innerapp) => !internalIds.includes(innerapp.id)),
)
);
filteredApps.filter((innerapp) => !internalIds.includes(innerapp.id.toLowerCase()))
)
)
var delay = -75
var runDelay = false
@@ -7982,7 +8081,7 @@ const AngularWorkflow = (defaultprops) => {
setVisibleApps(
prioritizedApps.concat(
filteredApps.filter(
(innerapp) => !internalIds.includes(innerapp.id)
(innerapp) => !internalIds.includes(innerapp.id.toLowerCase())
)
)
);
@@ -9158,6 +9257,139 @@ const AngularWorkflow = (defaultprops) => {
backgroundColor: theme.palette.inputColor,
};
// Makes a list of items the user can choose from based on previous runs
var availableArguments = []
if (executionArgumentModalOpen && workflowExecutions.length > 0) {
for (let executionKey in workflowExecutions) {
if (availableArguments.length > 5) {
break
}
const execution = workflowExecutions[executionKey]
if (execution.execution_argument === undefined || execution.execution_argument === null || execution.execution_argument.length < 2) {
continue
}
if (execution.execution_argument.includes("too large ")) {
continue
}
if (availableArguments.includes(execution.execution_argument)) {
continue
}
availableArguments.push(execution.execution_argument)
}
}
const executionArgumentModal =
<Dialog
PaperComponent={PaperComponent}
disableEnforceFocus={true}
hideBackdrop={true}
disableBackdropClick={true}
style={{ pointerEvents: "none" }}
PaperComponent={PaperComponent}
aria-labelledby="draggable-dialog-title"
open={executionArgumentModalOpen}
PaperProps={{
style: {
padding: 30,
pointerEvents: "auto",
color: "white",
minWidth: isMobile ? "90%" : 800,
border: theme.palette.defaultBorder,
},
}}
onClose={() => {
}}
>
<Tooltip
title="Close window"
placement="top"
style={{ zIndex: 10011 }}
>
<IconButton
style={{ zIndex: 5000, position: "absolute", top: 34, right: 34 }}
onClick={(e) => {
e.preventDefault();
setExecutionArgumentModalOpen(false);
}}
>
<CloseIcon style={{ color: "white" }} />
</IconButton>
</Tooltip>
<DialogTitle id="draggable-dialog-title" style={{ cursor: "move", }}>
<span style={{ color: "white" }}>Please provide an execution argument</span>
</DialogTitle>
<DialogContent>
<Typography variant="body1" color="textSecondary">
At least one node in this workflow requires an execution argument. Please select one below, or provide a custom one in the text field next to the run button.
</Typography>
{/*
<div style={{marginTop: 10, }}>
<Tooltip
color="primary"
title="An argument to be used for execution. This is a variable available to every node in your workflow."
placement="top"
>
<TextField
id="execution_argument_input_field"
style={theme.palette.textFieldStyle}
disabled={workflow.public}
color="secondary"
placeholder={"Execution Argument"}
defaultValue={executionText}
onBlur={(e) => {
setExecutionText(e.target.value);
}}
/>
</Tooltip>
</div>
*/}
<Divider style={{marginTop: 10, marginBottom: 20, }}/>
{availableArguments.length > 0 ?
<div>
<Typography variant="body1" style={{}}>
Previously used arguments:
</Typography>
{availableArguments.map((data) => {
return (
<Paper style={{ padding: 10, marginTop: 10, backgroundColor: theme.palette.platformColor, maxHeight: 70, overflow: "auto", cursor: "pointer", position: "relative", }}
onClick={() => {
setExecutionText(data)
executeWorkflow(data, workflow.start, lastSaved);
setExecutionArgumentModalOpen(false)
}}
>
<div style={{height: "100%", width: 2, backgroundColor: "rgba(255, 255, 255, 0.5)", position: "absolute", left: 0, top: 0}} />
<Typography variant="body1" color="textSecondary">
{data}
</Typography>
</Paper>
)
})}
</div>
: null}
<Button
variant="outlined"
color="primary"
onClick={() => {
executeWorkflow(" ", workflow.start, lastSaved);
setExecutionArgumentModalOpen(false);
}}
style={{ marginTop: 20, marginBottom: 20 }}
>
Run anyway
</Button>
</DialogContent>
</Dialog>
const aiQueryModal =
<Dialog
PaperComponent={PaperComponent}
@@ -10420,7 +10652,6 @@ const AngularWorkflow = (defaultprops) => {
};
const handleWorkflowSelectionUpdate = (e, isUserinput) => {
setUpdate(Math.random());
if (e.target.value === undefined || e.target.value === null || e.target.value.id === undefined) {
console.log("Returning as there's no id. Value: ", e.target.value);
@@ -10428,6 +10659,16 @@ const AngularWorkflow = (defaultprops) => {
}
const paramIndex = isUserinput === true ? 5 : 0
console.log("USERINPUT: ", paramIndex, workflow.triggers[selectedTriggerIndex])
if (workflow.triggers[selectedTriggerIndex].parameters[paramIndex] === undefined || workflow.triggers[selectedTriggerIndex].parameters[paramIndex] === null) {
workflow.triggers[selectedTriggerIndex].parameters[paramIndex] = {
"name": "subflow",
"value": "",
}
}
setUpdate(Math.random());
workflow.triggers[selectedTriggerIndex].parameters[paramIndex].value = e.target.value.id;
setSubworkflow(e.target.value);
@@ -13489,9 +13730,54 @@ const AngularWorkflow = (defaultprops) => {
variant="body2"
>
{workflow.errors.slice(0,3).map((error) => {
// Loop through each word, and if it matches "Action <name> " then replace it with a link to the action
var colornext = false
const newerror = error === undefined || error == null ? "" : error.split(" ").map((word) => {
if (colornext) {
colornext = false
return (
<span
style={{color: "#f85a3e", cursor: "pointer"}}
onClick={() => {
console.log("Clicked action: ", word)
// Find it in cytoscape
if (cy === undefined || cy === null) {
return
}
const foundnode = cy.nodes().filter((node) => {
return node.data().label === word
})
if (foundnode === undefined || foundnode === null || foundnode.length === 0) {
return
}
console.log("Found node: ", foundnode)
cy.elements().unselect()
foundnode[0].select()
}}
>
{word}&nbsp;
</span>
)
}
if (word.toLowerCase() === "action") {
colornext = true
}
return word + " "
})
if (newerror === undefined || newerror === null || newerror === "") {
return null
}
return (
<div>
- {error}
- {newerror}
</div>
)
})}
@@ -13499,6 +13785,40 @@ const AngularWorkflow = (defaultprops) => {
</div>
: null
const RightsideBar = () => {
const [hovered, setHovered] = useState(false)
return (
<div
style={{
position: "fixed", right: -5, top: "40%", width: 70, height: 235, border: "1px solid #f85a3e", cursor: "pointer", borderRadius: theme.palette.borderRadius,
padding: 10,
backgroundColor: hovered ? theme.palette.surfaceColor : theme.palette.platformColor,
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={() => {
setExecutionModalOpen(true);
getWorkflowExecution(props.match.params.key, "");
}}
>
<ArrowLeftIcon style={{marginTop: 10, marginLeft: 10, marginBottom: 10, }}/>
<Typography
variant="h6"
style={{
writingMode: "vertical-rl",
textOrientation: "mixed",
marginLeft: 10,
fontWeight: "bold",
}}
>
Explore runs
</Typography>
{/*<ArrowLeftIcon style={{marginTop: 10, marginLeft: 10, marginBottom: 10, }}/> */}
</div>
)
}
const BottomCytoscapeBar = () => {
if (workflow.id === undefined || workflow.id === null || (!workflow.public && apps.length === 0)) {
return null;
@@ -13634,7 +13954,7 @@ const AngularWorkflow = (defaultprops) => {
</Button>
</span>
</Tooltip>
{workflow.public ? (
{workflow.public || userdata.support == true ? (
<Tooltip
color="secondary"
title="Download workflow"
@@ -14345,6 +14665,7 @@ const AngularWorkflow = (defaultprops) => {
}
}
return (
<div style={{ display: "flex" }}>
<IconButton
@@ -14810,7 +15131,7 @@ const AngularWorkflow = (defaultprops) => {
>
<h2 style={{ color: "rgba(255,255,255,0.5)" }}>
<DirectionsRunIcon style={{ marginRight: 10 }} />
All Workflow Runs
All Workflow Runs
</h2>
</Breadcrumbs>
<Tooltip
@@ -14818,7 +15139,7 @@ const AngularWorkflow = (defaultprops) => {
placement="left-start"
style={{ zIndex: 10010 }}
>
<a href={`/workflows/debug?workflow_id=${workflow.id}`} style={{textDecoration: "none", }}>
<a target="_blank" href={`/workflows/debug?workflow_id=${workflow.id}`} style={{textDecoration: "none", }}>
<Button
color="secondary"
style={{marginLeft: 50, maxHeight: 30, marginTop: 20, }}
@@ -14838,7 +15159,7 @@ const AngularWorkflow = (defaultprops) => {
color="primary"
>
<CachedIcon style={{ marginRight: 10 }} />
Refresh Runs
Refresh Runs
</Button>
<Divider
style={{
@@ -15538,13 +15859,17 @@ const AngularWorkflow = (defaultprops) => {
}
}
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
const chosenNodeId = new URLSearchParams(cursearch).get("node");
const highlightNode = chosenNodeId !== null && chosenNodeId !== undefined && chosenNodeId !== "" && chosenNodeId === data.action.id
return (
<div
key={index}
style={{
marginBottom: 20,
border:
border: highlightNode ? "2px solid red"
:
data.action.sub_action === true
? "1px solid rgba(255,255,255,0.3)"
: "1px solid rgba(255,255,255, 0.3)",
@@ -16255,6 +16580,7 @@ const AngularWorkflow = (defaultprops) => {
{showErrors}
<BottomCytoscapeBar />
<TopCytoscapeBar />
<RightsideBar />
</span>
}
</div>
@@ -17909,6 +18235,7 @@ const AngularWorkflow = (defaultprops) => {
{newView}
<VariablesModal variableInfo={variableInfo} setVariableInfo={setVariableInfo} />
<ExecutionVariableModal variableInfo={variableInfo} setVariableInfo={setVariableInfo} />
{executionArgumentModal}
{aiQueryModal}
{conditionsModal}
{authenticationModal}
@@ -18026,28 +18353,15 @@ const AngularWorkflow = (defaultprops) => {
);
// Awful way of handling scroll
if (
scrollConfig !== undefined &&
setScrollConfig !== undefined &&
Object.getOwnPropertyNames(selectedAction).length !== 0
) {
if (scrollConfig !== undefined && setScrollConfig !== undefined && Object.getOwnPropertyNames(selectedAction).length !== 0) {
const rightSideActionView = document.getElementById("rightside_actions");
if (rightSideActionView !== undefined && rightSideActionView !== null) {
if (
scrollConfig.top !== 0 &&
scrollConfig.top !== undefined &&
scrollConfig.top !== 0
) {
if (scrollConfig.top !== null && scrollConfig.top !== undefined && scrollConfig.top !== 0) {
setTimeout(() => {
if (
scrollConfig.selected !== undefined &&
scrollConfig.selected !== null
) {
const selectedField = document.getElementById(
scrollConfig.selected
);
if (scrollConfig.selected !== undefined && scrollConfig.selected !== null) {
const selectedField = document.getElementById(scrollConfig.selected)
if (selectedField !== undefined && selectedField !== null) {
selectedField.focus();
selectedField.focus()
}
}
}, 5);
+202 -103
View File
@@ -20,6 +20,7 @@ import {
TextField,
Tooltip,
Breadcrumbs,
Drawer,
CircularProgress,
Chip,
IconButton,
@@ -43,6 +44,7 @@ import {
Loop as LoopIcon,
AddPhotoAlternate as AddPhotoAlternateIcon,
CallMerge as CallMergeIcon,
CloudDownload as CloudDownloadIcon,
} from "@mui/icons-material";
import { v4 as uuidv4 } from "uuid";
@@ -448,6 +450,8 @@ const AppCreator = (defaultprops) => {
const [openApiData, setOpenApiData] = React.useState("");
const [openApiModal, setOpenApiModal] = React.useState(false);
const [appDownloadData, setAppDownloadData] = React.useState("");
useEffect(() => {
console.log("In useEffect for openApiData: ", openApiData)
}, [openApiData]);
@@ -900,8 +904,9 @@ const AppCreator = (defaultprops) => {
}
if (newaction.url !== undefined && newaction.url !== null && newaction.url.includes("_shuffle_replace_")) {
const regex = /_shuffle_replace_\d/i;
//console.log("NEW: ",
//const regex = /_shuffle_replace_\d/i;
const regex = /_shuffle_replace_\d+/i
newaction.url = newaction.url.replaceAll(new RegExp(regex, 'g'), "")
}
@@ -911,11 +916,12 @@ const AppCreator = (defaultprops) => {
var categoryindex = -1;
// Stupid way of finding a category/grouping
for (let splitkey in pathsplit) {
if (pathsplit[splitkey].includes("_shuffle_replace_")) {
const regex = /_shuffle_replace_\d/i;
//console.log("NEW: ",
pathsplit[splitkey] = pathsplit[splitkey].replaceAll(new RegExp(regex, 'g'), "")
}
if (pathsplit[splitkey].includes("_shuffle_replace_")) {
//const regex = /_shuffle_replace_\d/i;
const regex = /_shuffle_replace_\d+/i
//console.log("NEW: ",
pathsplit[splitkey] = pathsplit[splitkey].replaceAll(new RegExp(regex, 'g'), "")
}
if (
pathsplit[splitkey].length > 0 &&
@@ -1205,9 +1211,9 @@ const AppCreator = (defaultprops) => {
);
}
}
} catch (e) {
console.log("Param Error: ", e, path)
}
} catch (e) {
console.log("Param Error: ", e, path)
}
}
}
}
@@ -1527,6 +1533,10 @@ const AppCreator = (defaultprops) => {
in: "query",
};
if (parameter.example !== undefined && parameter.example !== null) {
tmpaction.example = parameter.example
}
if (parameter.required === undefined) {
tmpaction.required = false;
}
@@ -2007,9 +2017,10 @@ const AppCreator = (defaultprops) => {
var pathjoin = item.url+"_"+item.method.toLowerCase()
if (handledPaths.includes(pathjoin)) {
// Max 100 of same lol
for (let i = 0; i < 100; i++) {
item.url = item.url+"_shuffle_replace_"+i
// Max 1000 of same. Will it be ok for graphql longterm?
const baseurl = item.url
for (let i = 0; i < 1000; i++) {
item.url = baseurl+"_shuffle_replace_"+i
pathjoin = item.url+"_"+item.method.toLowerCase()
if (handledPaths.includes(pathjoin)) {
@@ -2289,7 +2300,7 @@ const AppCreator = (defaultprops) => {
}
const methodname = item.method.toLowerCase()
if (methodname === "post" || methodname === "put" || methodname === "patch") {
if (methodname === "post" || methodname === "put" || methodname === "patch" || methodname === "delete") {
if (
item.body !== undefined &&
item.body !== null &&
@@ -2565,6 +2576,8 @@ const AppCreator = (defaultprops) => {
}
}
setAppDownloadData(JSON.stringify(data, null, 4))
fetch(globalUrl + "/api/v1/verify_openapi", {
method: "POST",
headers: {
@@ -2585,9 +2598,16 @@ const AppCreator = (defaultprops) => {
})
.then((responseJson) => {
if (!responseJson.success) {
if (responseJson.extra !== undefined && responseJson.extra !== null) {
toast("Failed building: " + responseJson.extra);
}
if (responseJson.reason !== undefined) {
setErrorCode(responseJson.reason);
toast("Failed to verify: " + responseJson.reason);
if (responseJson.extra === undefined && responseJson.extra === null) {
toast("Failed to verify: " + responseJson.reason);
}
}
} else {
toast("Successfully uploaded openapi");
@@ -3390,13 +3410,13 @@ const AppCreator = (defaultprops) => {
}}
label={parsedChip}
onClick={() => {
if (chipRequired) {
currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1)
} else {
currentAction["required_bodyfields"].push(chipData)
}
if (chipRequired) {
currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1)
} else {
currentAction["required_bodyfields"].push(chipData)
}
setCurrentAction(currentAction);
setCurrentAction(currentAction);
setChipRequired(!chipRequired);
}}
/>
@@ -3432,11 +3452,11 @@ const AppCreator = (defaultprops) => {
};
const deletePathQuery = (index) => {
console.log("Should delete index: ", index)
var tmpqueries = JSON.parse(JSON.stringify(urlPathQueries))
tmpqueries.splice(index, 1)
console.log("Should delete index: ", index)
var tmpqueries = JSON.parse(JSON.stringify(urlPathQueries))
tmpqueries.splice(index, 1)
console.log("Queries: ", tmpqueries)
console.log("Queries: ", tmpqueries)
setUrlPathQueries(tmpqueries);
if (updater === "deleteupdater") {
@@ -3683,48 +3703,55 @@ const AppCreator = (defaultprops) => {
return errormessage;
};
const getBackgroundColor = (data) => {
var bgColor = "#61afee";
if (data === "POST") {
bgColor = "#49cc90";
} else if (data === "PUT") {
bgColor = "#fca130";
} else if (data === "PATCH") {
bgColor = "#50e3c2";
} else if (data === "DELETE") {
bgColor = "#f93e3e";
} else if (data === "HEAD") {
bgColor = "#9012fe";
}
return bgColor;
}
const newActionModal = (
<Dialog
<Drawer
anchor={"right"}
open={actionsModalOpen}
fullWidth
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
minWidth: 550,
maxWidth: 550,
maxHeight: 750,
minWidth: 700,
maxWidth: 700,
},
}}
onClose={() => {
setUrlPath("");
setCurrentAction({
name: "",
description: "",
url: "",
file_field: "",
headers: "",
paths: [],
queries: [],
body: "",
errors: [],
method: actionNonBodyRequest[0],
action_label: "No Label",
required_bodyfields: [],
});
setCurrentActionMethod(apikeySelection[0]);
setUrlPathQueries([]);
setActionsModalOpen(false);
setFileUploadEnabled(false);
console.log("Closing modal");
console.log(currentAction);
const errors = getActionErrors();
addActionToView(errors);
setActionsModalOpen(false);
setUrlPathQueries([]);
setUrlPath("");
setFileUploadEnabled(false);
}}
>
<FormControl style={{ backgroundColor: surfaceColor, color: "white" }}>
<DialogTitle>
<DialogTitle style={{marginTop: 30, }}>
<div style={{ color: "white" }}>New action</div>
</DialogTitle>
<DialogContent>
<DialogContent style={{paddingBottom: 100, }}>
<a
target="_blank"
href="https://shuffler.io/docs/app_creation#actions"
@@ -3750,7 +3777,8 @@ const AppCreator = (defaultprops) => {
variant="outlined"
defaultValue={currentAction["name"]}
onChange={(e) => {
setActionField("name", e.target.value);
var trimmed = e.target.value.trim();
setActionField("name", trimmed)
}}
onBlur={(e) => {
// Fix basic issues in frontend. Python functions run a-zA-Z0-9_
@@ -3759,6 +3787,18 @@ const AppCreator = (defaultprops) => {
if (found !== null) {
setActionField("name", found.join(""));
}
// Look through all actions and see if there is one with the same name
if (currentAction.url === "" && actions !== undefined && actions !== null && actions.length > 0) {
for (var i = 0; i < actions.length; i++) {
if (actions[i].name.toLowerCase() === e.target.value.toLowerCase()) {
toast("Action with name " + e.target.value + " already exists. If you keep this, it will be overwritten.")
break
}
}
}
}}
key={currentAction}
InputProps={{
@@ -3822,26 +3862,33 @@ const AppCreator = (defaultprops) => {
id: "method-option",
}}
>
{actionNonBodyRequest.map((data, index) => {
// Add actionBodyRequest to actionNonBodyRequest
{actionNonBodyRequest.concat(actionBodyRequest).map((data, index) => {
const backgroundColor = getBackgroundColor(data);
return (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
style={{}}
value={data}
>
{data}
<Chip
style={{
color: "white",
borderRadius: 5,
minWidth: 80,
marginRight: 10,
marginTop: 2,
cursor: "pointer",
fontSize: 14,
fontWeight: "bold",
backgroundColor: backgroundColor,
}}
label={data}
/>
</MenuItem>
);
})}
{actionBodyRequest.map((data, index) => (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
value={data}
>
{data}
</MenuItem>
))}
</Select>
<div style={{ marginTop: "15px" }} />
URL path / Curl statement
@@ -4218,19 +4265,11 @@ const AppCreator = (defaultprops) => {
/>
{exampleResponse}
</DialogContent>
<DialogActions>
<Button
style={{ borderRadius: "0px" }}
onClick={() => {
setActionsModalOpen(false);
}}
>
Cancel
</Button>
<div style={{position: "fixed", backgroundColor: theme.palette.surfaceColor, bottom: 0, width: "100%", padding: 25, borderTop: "1px solid rgba(255,255,255,0.3)", }}>
<Button
color="primary"
variant={urlPath.length > 0 ? "contained" : "outlined"}
style={{ borderRadius: "0px" }}
style={{ }}
onClick={() => {
//console.log(urlPathQueries)
//console.log(urlPath)
@@ -4245,9 +4284,19 @@ const AppCreator = (defaultprops) => {
>
Submit
</Button>
</DialogActions>
{/*
<Button
style={{ marginLeft: 10, }}
onClick={() => {
setActionsModalOpen(false);
}}
>
Cancel
</Button>
*/}
</div>
</FormControl>
</Dialog>
</Drawer>
);
@@ -4316,6 +4365,8 @@ const AppCreator = (defaultprops) => {
setCurrentAction(data);
setCurrentActionMethod(data.method);
console.log("QUERIES: ", data.queries)
setUrlPathQueries(data.queries);
setUrlPath(data.url);
setActionsModalOpen(true);
@@ -4453,7 +4504,7 @@ const AppCreator = (defaultprops) => {
style={{ borderRadius: 0, position: "absolute", top: 70, }}
variant={actions.length === 0 ? "contained" : "outlined"}
onClick={(e) => {
e.preventDefault();
e.preventDefault();
setCurrentActionMethod(actionNonBodyRequest[0]);
setCurrentAction({
@@ -4467,8 +4518,8 @@ const AppCreator = (defaultprops) => {
body: "",
errors: [],
method: actionNonBodyRequest[0],
action_label: "No Label",
required_bodyfields: [],
action_label: "No Label",
required_bodyfields: [],
});
setActionsModalOpen(true);
}}
@@ -4485,20 +4536,20 @@ const AppCreator = (defaultprops) => {
//console.log("Actions: ", filteredActions)
if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) {
return null
}
return null
}
return (
return (
<div>
{filteredActions.slice(0, actionAmount).map((data, index) => {
//console.log("Found action: ", data)
return (
<ActionPaper key={index} index={index} data={data} />
)
})}
</div>
)
}
return (
<ActionPaper key={index} index={index} data={data} />
)
})}
</div>
)
}
@@ -6097,20 +6148,68 @@ const AppCreator = (defaultprops) => {
{testView}
*/}
<Button
disabled={appBuilding}
color="primary"
variant="contained"
style={{ borderRadius: "0px", marginTop: "30px", height: "50px" }}
onClick={() => {
submitApp();
}}
>
{appBuilding ? <CircularProgress /> : "Save"}
</Button>
<Typography style={{ marginTop: 5 }}>
{errorCode.length > 0 ? `Error: ${errorCode}` : null}
</Typography>
<div style={{display: "flex", marginTop: 35, }}>
{appDownloadData.length > 0 ?
<Tooltip title="Download the OpenAPI specification for the App" placement="bottom">
<IconButton
style={{marginRight: 25, }}
onClick={() => {
toast(`Downloading OpenAPI JSON data for for ${name}`)
// Download as file
var blob = new Blob([appDownloadData], {
type: "application/octet-stream",
});
var url = URL.createObjectURL(blob);
var link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", `${name}.json`);
var event = document.createEvent("MouseEvents");
event.initMouseEvent(
"click",
true,
true,
window,
1,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null
);
link.dispatchEvent(event);
}}
>
<CloudDownloadIcon />
</IconButton>
</Tooltip>
: null}
<Button
disabled={appBuilding}
color="primary"
variant="contained"
fullWidth
style={{ height: "50px", flex: 1, }}
onClick={() => {
submitApp();
}}
>
{appBuilding ? <CircularProgress /> : "Save"}
</Button>
{appDownloadData.length > 0 ?
<div style={{width: 50, }}/>
: null}
</div>
<Typography style={{ marginTop: 25, textAlign: "center", }}>
{errorCode.length > 0 ? `Upload Error: ${errorCode}` : null}
</Typography>
</Paper>
</div>
);
+210 -59
View File
@@ -88,6 +88,7 @@ export const FixName = (name) => {
return newAppname;
};
// Takes input of e.g. $node.data.#.asd and a matching value from a json blob
// Returns
export const FindJsonPath = (path, inputdata) => {
@@ -157,6 +158,8 @@ export const FindJsonPath = (path, inputdata) => {
return inputdata
}
export const internalIds = ["shuffle tools", "http", "email"];
// Parses JSON data into keys that can be used everywhere :)
// Reverse of this is FindJsonPath
export const GetParsedPaths = (inputdata, basekey) => {
@@ -270,7 +273,7 @@ export const GetParsedPaths = (inputdata, basekey) => {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const Apps = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props;
//const [workflows, setWorkflows] = React.useState([]);
const baseRepository = "https://github.com/frikky/shuffle-apps";
@@ -304,6 +307,8 @@ const Apps = (props) => {
const [cursearch, setCursearch] = React.useState("");
const [sharingConfiguration, setSharingConfiguration] = React.useState("you");
const [downloadBranch, setDownloadBranch] = React.useState("master");
const [creatorProfile, setCreatorProfile] = React.useState({});
const [contact, setContact] = React.useState("");
const [isDropzone, setIsDropzone] = React.useState(false);
const upload = React.useRef(null);
@@ -324,6 +329,40 @@ const Apps = (props) => {
},
});
const getUserProfile = (username) => {
if (serverside === true || !isCloud) {
setCreatorProfile({})
return;
}
fetch(`${globalUrl}/api/v1/users/creators/${username}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success !== false) {
setCreatorProfile(responseJson);
} else {
setCreatorProfile({})
}
})
.catch((error) => {
console.log(error);
setCreatorProfile({})
});
};
useEffect(() => {
if (apps.length <= 0 && firstrequest) {
document.title = "Shuffle - Apps";
@@ -441,6 +480,12 @@ const Apps = (props) => {
setFilteredApps(privateapps);
if (privateapps.length > 0) {
if (selectedApp.id === undefined || selectedApp.id === null) {
if (privateapps[0].owner !== undefined && privateapps[0].owner !== null) {
getUserProfile(privateapps[0].owner);
}
setContact(privateapps[0].contact_info)
setSelectedApp(privateapps[0]);
setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you")
}
@@ -646,6 +691,13 @@ const Apps = (props) => {
style={paperAppStyle}
onClick={() => {
if (selectedApp.id !== data.id) {
if (data.owner !== undefined && data.owner !== null) {
getUserProfile(data.owner);
}
setContact(data.contact_info)
data.name = newAppname;
setSelectedApp(data);
setSharingConfiguration(data.sharing === true ? "public" : "you")
@@ -869,6 +921,7 @@ const Apps = (props) => {
</Link>
: null
console.log("Sharing config: ", sharingConfiguration);
const activateButton =
selectedApp.generated && !selectedApp.activated ? (
<div>
@@ -882,7 +935,7 @@ const Apps = (props) => {
Activate App
</Button>
</Link>
<Tooltip title={"Delete app"}>
<Tooltip title={"Delete app (confirm box will show)"}>
<Button
variant="outlined"
component="label"
@@ -891,6 +944,7 @@ const Apps = (props) => {
onClick={() => {
setDeleteModalOpen(true);
}}
disabled={sharingConfiguration === undefined || sharingConfiguration === null || sharingConfiguration == "public"}
>
<DeleteIcon />
</Button>
@@ -905,7 +959,7 @@ const Apps = (props) => {
(selectedApp.downloaded !== undefined && selectedApp.downloaded == true) ||
!selectedApp.generated) &&
activateButton === null ? (
<Tooltip title={"Delete app"}>
<Tooltip title={"Delete app (confirm box will show)"}>
<Button
variant="outlined"
component="label"
@@ -914,6 +968,7 @@ const Apps = (props) => {
onClick={() => {
setDeleteModalOpen(true);
}}
disabled={sharingConfiguration === undefined || sharingConfiguration === null || sharingConfiguration == "public"}
>
<DeleteIcon />
</Button>
@@ -1114,7 +1169,7 @@ const Apps = (props) => {
{activateButton}
{ /* editNewButton === null && */ }
{ /* editNewButton === null && */ }
{canEditApp ? (
<div>
@@ -1127,35 +1182,12 @@ const Apps = (props) => {
{editNewButton}
</div>
}
{selectedApp.tags !== undefined && selectedApp.tags !== null ? (
<div
style={{
display: "inline-block",
marginLeft: 15,
float: "right",
}}
>
{selectedApp.tags.map((tag, index) => {
if (index >= 3) {
return null;
}
return (
<Chip
key={index}
style={chipStyle}
variant="outlined"
label={tag}
color="primary"
/>
);
})}
</div>
) : null}
{canEditApp
? (
<div style={{ marginTop: 15 }}>
{canEditApp
? (
<div style={{ marginTop: 15, display: "flex", }}>
{/*<p><b>ID:</b> {selectedApp.id}</p>*/}
<b style={{ marginRight: 15 }}>Sharing </b>
<Select
value={sharingConfiguration}
@@ -1209,8 +1241,119 @@ const Apps = (props) => {
);
})}
</Select>
{isCloud && (selectedApp.sharing === true || selectedApp.public === true || creatorProfile.github_avatar !== undefined) && !internalIds.includes(selectedApp.name.toLowerCase()) ?
<Tooltip title="Deactivates this app for the current organisation. This means the app will not be usable again until you re-activate it." placement="top">
<Button
variant="contained"
component="label"
color="primary"
onClick={() => {
const tmpurl = new URL(window.location.href);
const searchParams = tmpurl.searchParams;
const queryID = searchParams.get("queryID");
if (queryID !== undefined && queryID !== null) {
aa("init", {
appId: "JNSS5CFDZZ",
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
});
const timestamp = new Date().getTime();
aa("sendEvents", [
{
eventType: "conversion",
eventName: "Public App Activated",
index: "appsearch",
objectIDs: [selectedApp.id],
timestamp: timestamp,
queryID: queryID,
userToken:
userdata === undefined ||
userdata === null ||
userdata.id === undefined
? "unauthenticated"
: userdata.id,
},
]);
} else {
console.log("No query to handle when activating");
}
activateApp(selectedApp.id, true)
}}
style={{ height: 35, marginTop: 0, marginLeft: 10, }}
>
Deactivate
</Button>
</Tooltip>
: null}
</div>
) : null}
<div style={{display: "flex", }}>
{isCloud && Object.getOwnPropertyNames(creatorProfile).length !== 0 && creatorProfile.github_avatar !== undefined && creatorProfile.github_avatar !== null ?
<div style={{ display: "flex", marginTop: 15, }}>
<IconButton
color="primary"
style={{ padding: 0, marginRight: 10 }}
aria-controls="simple-menu"
aria-haspopup="true"
onClick={(event) => {
//setAnchorElAvatar(event.currentTarget);
}}
>
<Link
to={`/creators/${creatorProfile.github_username}`}
style={{ textDecoration: "none", color: "#f86a3e" }}
>
<Avatar
style={{ height: 30, width: 30 }}
alt={contact.name}
src={creatorProfile.github_avatar}
/>
</Link>
</IconButton>
<Typography
variant="body1"
color="textSecondary"
style={{ color: "" }}
>
Shared by{" "}
<Link
to={`/creators/${creatorProfile.github_username}`}
style={{ textDecoration: "none", color: "#f86a3e" }}
>
{creatorProfile.github_username}
</Link>
</Typography>
</div>
: null}
{selectedApp.tags !== undefined && selectedApp.tags !== null ? (
<div
style={{
marginLeft: 20,
marginTop: 15,
}}
>
{selectedApp.tags.map((tag, index) => {
if (index >= 3) {
return null;
}
return (
<Chip
key={index}
style={chipStyle}
variant="outlined"
label={tag}
color="primary"
/>
);
})}
</div>
) : null}
</div>
{/*<p><b>Owner:</b> {selectedApp.owner}</p>*/}
{selectedApp.privateId !== undefined &&
selectedApp.privateId.length > 0 ? (
@@ -1666,37 +1809,45 @@ const Apps = (props) => {
)
}
const activateApp = (appid, refresh) => {
fetch(globalUrl + "/api/v1/apps/" + appid + "/activate", {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
credentials: "include",
const activateApp = (appid, refresh) => {
const appExists = userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appid)
const url = appExists ? `${globalUrl}/api/v1/apps/${appid}/deactivate` : `${globalUrl}/api/v1/apps/${appid}/activate`
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Failed to deactivate")
}
return response.json()
})
.then((response) => {
if (response.status !== 200) {
console.log("Failed to activate")
}
.then((responseJson) => {
if (responseJson.success === false) {
toast("Failed to activate the app")
} else {
//toast("App activated for your organization! Refresh the page to use the app.")
if (appExists) {
toast("App deactivated for your organization! Existing workflows with the app will continue to work.")
} else {
toast("App activated for your organization!")
}
return response.json()
})
.then((responseJson) => {
if (responseJson.success === false) {
toast("Failed to activate the app")
} else {
toast("App activated for your organization! Refresh the page to use the app.")
if (refresh === true) {
getApps()
}
if (refresh === true) {
getApps()
}
})
.catch(error => {
//toast(error.toString())
console.log("Activate app error: ", error.toString())
});
}
})
.catch(error => {
//toast(error.toString())
console.log("Deactivate app error: ", error.toString())
});
}
+6
View File
@@ -523,6 +523,12 @@ const Docs = (defaultprops) => {
);
}
if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) {
if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) {
extraInfo = ""
}
}
return (
<Typography
onMouseOver={() => {
+13 -13
View File
@@ -181,20 +181,20 @@ const LoginDialog = (props) => {
);
}
if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
console.log("RUN Welcome!!")
window.location.pathname = "/welcome"
return
}
if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
console.log("RUN Welcome!!")
window.location.pathname = "/welcome"
return
}
const tmpView = new URLSearchParams(window.location.search).get("view")
if (tmpView !== undefined && tmpView !== null) {
//const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
const newUrl = `/${tmpView}`
window.location.pathname = newUrl
} else {
window.location.pathname = "/workflows"
}
const tmpView = new URLSearchParams(window.location.search).get("view")
if (tmpView !== undefined && tmpView !== null) {
//const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
const newUrl = `/${tmpView}`
window.location.pathname = newUrl
} else {
window.location.pathname = "/workflows"
}
setIsLoggedIn(true);
}
-53
View File
@@ -290,59 +290,6 @@ const Settings = (props) => {
}
};
// const registerProviders = (userdata) => {
// // Register hooks here
// detectEthereumProvider().then((provider) => {
// if (provider) {
// if (!provider.isMetaMask) {
// toast("Only MetaMask is supported as of now.");
// return;
// }
// // Find the ethereum network
// // Get the users' account(s)
// //toast("Connecting to MetaMask")
// //console.log("Connected: ", provider.isConnected())
// if (!provider.isConnected()) {
// toast("Metamask is not connected.");
// return;
// }
// provider.on("message", (event) => {
// toast("Ethereum message: ", event);
// });
// provider.on("chainChanged", (chainId) => {
// console.log("Changed chain to: ", chainId);
// const method = "eth_getBalance";
// const params = [userdata.eth_info.account, "latest"];
// provider
// .request({
// method: method,
// params,
// })
// .then((result) => {
// console.log("Got result: ", result);
// if (result !== undefined && result !== null) {
// userdata.eth_info.balance = result;
// userdata.eth_info.parsed_balance = result / 1000000000000000000;
// console.log("INFO: ", userdata);
// setUserData(userdata);
// } else {
// toast("Couldn't find balance: ", result);
// }
// })
// .catch((error) => {
// // If the request fails, the Promise will reject with an error.
// toast("Failed getting info from ethereum API: " + error);
// });
// });
// }
// });
// };
// This should "always" have data
useEffect(() => {
if (firstrequest) {
+75 -65
View File
@@ -1336,16 +1336,21 @@ const Workflows = (props) => {
};
const exportAllWorkflows = (allWorkflows) => {
for (var i = 0; i < allWorkflows.length; i++) {
console.log(allWorkflows[i])
setTimeout(() => {
console.log(allWorkflows[i])
exportWorkflow(allWorkflows[i], false)
}, i * 200);
}
for (var i = 0; i < allWorkflows.length; i++) {
const wf = allWorkflows[i]
toast(`Exporting and keeping original for all ${allWorkflows.length} workflows`);
};
if (wf === undefined || wf.id === undefined) {
continue
}
console.log("Exporting workflow: ", wf)
setTimeout(() => {
exportWorkflow(JSON.parse(JSON.stringify(wf)), false)
}, i * 100);
}
toast(`Exporting and keeping original for all ${allWorkflows.length} workflows`);
}
const deduplicateIds = (data, skip_sanitize) => {
if (data.triggers !== null && data.triggers !== undefined) {
@@ -1498,7 +1503,12 @@ const Workflows = (props) => {
};
const exportWorkflow = (data, sanitize) => {
data = JSON.parse(JSON.stringify(data));
try {
data = JSON.parse(JSON.stringify(data));
} catch (e) {
console.log("Failed to parse JSON: ", e);
}
let exportFileDefaultName = data.name + ".json";
if (sanitize === true) {
@@ -2199,15 +2209,15 @@ const Workflows = (props) => {
// Can create and set workflows
const setNewWorkflow = (
name,
description,
tags,
defaultReturnValue,
editingWorkflow,
redirect,
currentUsecases,
inputblogpost,
inputstatus,
name,
description,
tags,
defaultReturnValue,
editingWorkflow,
redirect,
currentUsecases,
inputblogpost,
inputstatus,
) => {
var method = "POST";
var extraData = "";
@@ -2292,7 +2302,7 @@ const Workflows = (props) => {
})
.catch((error) => {
toast(error.toString());
setSubmitLoading(false)
setSubmitLoading(false)
setModalOpen(false);
setSubmitLoading(false);
});
@@ -2859,55 +2869,55 @@ const Workflows = (props) => {
<FormControl style={{flex: 1, marginLeft: 5, }}>
<InputLabel htmlFor="grouped-select-usecase">Usecases</InputLabel>
<Select
defaultValue=""
id="grouped-select"
label="Matching Usecase"
multiple
value={selectedUsecases}
renderValue={(selected) => selected.join(', ')}
onChange={(event) => {
console.log("Changed: ", event)
}}
>
defaultValue=""
id="grouped-select"
label="Matching Usecase"
multiple
value={selectedUsecases}
renderValue={(selected) => selected.join(', ')}
onChange={(event) => {
console.log("Changed: ", event)
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{usecases.map((usecase, index) => {
//console.log(usecase)
return (
<span key={index}>
<ListSubheader
style={{
color: usecase.color
}}
>
{usecase.name}
</ListSubheader>
{usecase.list.map((subcase, subindex) => {
//console.log(subcase)
total_count += 1
return (
<MenuItem key={subindex} value={total_count} onClick={(event) => {
if (selectedUsecases.includes(subcase.name)) {
const itemIndex = selectedUsecases.indexOf(subcase.name)
if (itemIndex > -1) {
selectedUsecases.splice(itemIndex, 1)
}
} else {
selectedUsecases.push(subcase.name)
}
{usecases.map((usecase, index) => {
//console.log(usecase)
return (
<span key={index}>
<ListSubheader
style={{
color: usecase.color
}}
>
{usecase.name}
</ListSubheader>
{usecase.list.map((subcase, subindex) => {
//console.log(subcase)
total_count += 1
return (
<MenuItem key={subindex} value={total_count} onClick={(event) => {
if (selectedUsecases.includes(subcase.name)) {
const itemIndex = selectedUsecases.indexOf(subcase.name)
if (itemIndex > -1) {
selectedUsecases.splice(itemIndex, 1)
}
} else {
selectedUsecases.push(subcase.name)
}
setUpdate(Math.random());
setSelectedUsecases(selectedUsecases)
}}>
<Checkbox style={{color: selectedUsecases.includes(subcase.name) ? usecase.color : theme.palette.inputColor}} checked={selectedUsecases.includes(subcase.name)} />
<ListItemText primary={subcase.name} />
</MenuItem>
)
})}
</span>
)
})}
setUpdate(Math.random());
setSelectedUsecases(selectedUsecases)
}}>
<Checkbox style={{color: selectedUsecases.includes(subcase.name) ? usecase.color : theme.palette.inputColor}} checked={selectedUsecases.includes(subcase.name)} />
<ListItemText primary={subcase.name} />
</MenuItem>
)
})}
</span>
)
})}
</Select>
</FormControl>
: null}
+1 -1
View File
@@ -9,7 +9,7 @@ require (
github.com/mackerelio/go-osstat v0.2.3
github.com/satori/go.uuid v1.2.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/shuffle/shuffle-shared v0.5.31
github.com/shuffle/shuffle-shared v0.5.68
k8s.io/api v0.28.1
k8s.io/apimachinery v0.28.1
k8s.io/client-go v0.28.1
-2
View File
@@ -280,8 +280,6 @@ github.com/shuffle/shuffle-shared v0.4.96 h1:iaIB/HP9eKpw9DMMJZhSLDbKdHJt075kFYL
github.com/shuffle/shuffle-shared v0.4.96/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.29 h1:n4vThl7v3mFVXbrIW71XREFdmZZo7mOBAWxnsdiNjDk=
github.com/shuffle/shuffle-shared v0.5.29/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.31 h1:OV4IIfKWWFW66WjGvyXOmmsSz3p8pW9L1ge1mDo8ftM=
github.com/shuffle/shuffle-shared v0.5.31/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+54 -41
View File
@@ -396,16 +396,16 @@ func deployServiceWorkers(image string) {
}
innerContainerName := fmt.Sprintf("shuffle-workers")
cnt, _ := findActiveSwarmNodes()
cnt, err := findActiveSwarmNodes()
if err != nil {
log.Printf("[ERROR] Failed to find active swarm nodes: %s. Defaulting to 1", err)
}
nodeCount := uint64(1)
if cnt > 0 {
nodeCount = uint64(cnt)
}
if cnt == 0 {
cnt = 1
}
appReplicas := os.Getenv("SHUFFLE_APP_REPLICAS")
appReplicaCnt := 1
if len(appReplicas) > 0 {
@@ -477,6 +477,8 @@ func deployServiceWorkers(image string) {
fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")),
fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")),
fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")),
fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%d", os.Getenv("SHUFFLE_MAX_SWARM_NODES")),
fmt.Sprintf("SHUFFLE_BASE_IMAGE_NAME=%s", baseImageName),
},
//Hosts: []string{
// innerContainerName,
@@ -533,6 +535,10 @@ func deployServiceWorkers(image string) {
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_CLOUDRUN_URL=%s", os.Getenv("SHUFFLE_CLOUDRUN_URL")))
}
if len(os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD")) > 0 {
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_AUTO_IMAGE_DOWNLOAD=%s", os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD")))
}
if len(os.Getenv("DOCKER_HOST")) > 0 {
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("DOCKER_HOST=%s", os.Getenv("DOCKER_HOST")))
} else {
@@ -580,6 +586,8 @@ func deployServiceWorkers(image string) {
serviceOptions,
)
//dockercli.ServiceUpdate(
if err == nil {
log.Printf("[DEBUG] Successfully deployed workers with %d replica(s) on %d node(s)", replicas, cnt)
//time.Sleep(time.Duration(10) * time.Second)
@@ -608,14 +616,14 @@ func deployServiceWorkers(image string) {
}
}
// Deploys the internal worker whenever something happens
// Deploys the worker with the current available environments
// https://docs.docker.com/engine/api/sdk/examples/
func buildEnvVars(envMap map[string]string) []corev1.EnvVar {
var envVars []corev1.EnvVar
for key, value := range envMap {
envVars = append(envVars, corev1.EnvVar{Name: key, Value: value})
}
return envVars
}
@@ -667,13 +675,18 @@ func deployWorker(image string, identifier string, env []string, executionReques
},
}
// Add environment variables
// pod.Spec.Containers[0].Env = buildEnvVars(envMap)
createdPod, err := clientset.CoreV1().Pods("shuffle").Create(context.Background(), pod, metav1.CreateOptions{})
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err)
log.Printf("[ERROR] Failed creating pod: %v", err)
return err
}
log.Printf("[INFO] Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace)
} else {
return nil
}
// Binds is the actual "-v" volume.
// Max 20% CPU every second
@@ -717,26 +730,6 @@ func deployWorker(image string, identifier string, env []string, executionReques
// In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns
// a 401 from the worker, which returns an error here
go sendWorkerRequest(executionRequest)
//sendWorkerRequest(executionRequest)
//err := sendWorkerRequest(executionRequest)
//if err != nil {
// log.Printf("[ERROR] Failed worker request for %s: %s", executionRequest.ExecutionId, err)
// if strings.Contains(fmt.Sprintf("%s", err), "connection refused") || strings.Contains(fmt.Sprintf("%s", err), "EOF") {
// workerImage := fmt.Sprintf("%s/%s/shuffle-worker:%s", baseimageregistry, baseimagename, workerVersion)
// deployServiceWorkers(workerImage)
// time.Sleep(time.Duration(10) * time.Second)
// err = sendWorkerRequest(executionRequest)
// }
//}
//if err == nil {
// // FIXME: Readd this? Removed for rerun reasons
// // executionIds = append(executionIds, executionRequest.ExecutionId)
//}
//}()
return nil
}
@@ -833,9 +826,6 @@ func deployWorker(image string, identifier string, env []string, executionReques
log.Printf("[INFO][%s] New Worker created. Environment %s: docker logs %s", executionRequest.ExecutionId, environment, cont.ID)
}
return nil
}
return nil
}
@@ -922,7 +912,7 @@ func findActiveSwarmNodes() (int64, error) {
ctx := context.Background()
nodes, err := dockercli.NodeList(ctx, types.NodeListOptions{})
if err != nil {
return 0, err
return 1, err
}
nodeCount := int64(0)
@@ -933,13 +923,21 @@ func findActiveSwarmNodes() (int64, error) {
}
}
return nodeCount, nil
// Check for SHUFFLE_MAX_NODES
// Make it into a number and check if it's lower than nodeCount
maxNodesString := os.Getenv("SHUFFLE_MAX_SWARM_NODES")
if len(maxNodesString) > 0 {
maxNodes, err := strconv.ParseInt(maxNodesString, 10, 64)
if err != nil {
return nodeCount, err
}
/*
containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{
All: true,
})
*/
if nodeCount > maxNodes {
nodeCount = maxNodes
}
}
return nodeCount, nil
}
// Get IP
@@ -1033,6 +1031,16 @@ func getOrborusStats(ctx context.Context) shuffle.OrborusStats {
Timestamp: time.Now().Unix(),
}
// FIXME: Returning for now due to this causing network congestion
// and database fillup. The backend api also has it disabled.
return newStats
// Disable orborus stats
if os.Getenv("SHUFFLE_STATS_DISABLED") == "true" {
return newStats
}
if swarmConfig == "run" || swarmConfig == "swarm" {
newStats.Swarm = true
}
@@ -1562,6 +1570,7 @@ func main() {
fmt.Sprintf("SHUFFLE_PASS_APP_PROXY=%s", os.Getenv("SHUFFLE_PASS_APP_PROXY")),
fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", os.Getenv("SHUFFLE_SWARM_CONFIG")),
fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")),
fmt.Sprintf("SHUFFLE_BASE_IMAGE_NAME=%s", baseimagename),
}
//log.Printf("Running worker with proxy? %s", os.Getenv("SHUFFLE_PASS_WORKER_PROXY"))
@@ -1611,12 +1620,16 @@ func main() {
overrideHttpsProxy := os.Getenv("SHUFFLE_INTERNAL_HTTPS_PROXY")
if len(overrideHttpProxy) > 0 {
log.Printf("[DEBUG] Added internal proxy: %s", overrideHttpProxy)
env = append(env, fmt.Sprintf("HTTP_PROXY=%s", overrideHttpProxy))
env = append(env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTP_PROXY=%s", overrideHttpProxy))
}
if len(overrideHttpsProxy) > 0 {
log.Printf("[DEBUG] Added internal proxy: %s", overrideHttpsProxy)
env = append(env, fmt.Sprintf("HTTPS_PROXY=%s", overrideHttpsProxy))
env = append(env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTPS_PROXY=%s", overrideHttpsProxy))
}
if len(os.Getenv("SHUFFLE_MAX_SWARM_NODES")) > 0 {
env = append(env, fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%s", os.Getenv("SHUFFLE_MAX_SWARM_NODES")))
}
err = deployWorker(workerImage, containerName, env, execution)
+28 -14
View File
@@ -5,13 +5,10 @@ go 1.19
//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
require (
cloud.google.com/go/datastore v1.10.0
cloud.google.com/go/storage v1.29.0
github.com/docker/docker v23.0.3+incompatible
github.com/gorilla/mux v1.8.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.5.31
github.com/shuffle/shuffle-shared v0.5.68
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
@@ -21,21 +18,30 @@ require (
cloud.google.com/go v0.107.0 // indirect
cloud.google.com/go/compute v1.14.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/datastore v1.10.0 // indirect
cloud.google.com/go/iam v0.8.0 // indirect
cloud.google.com/go/storage v1.29.0 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/adrg/strutil v0.2.3 // indirect
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 // indirect
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/frikky/go-elasticsearch/v8 v8.13.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/frikky/kin-openapi v0.41.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
@@ -44,7 +50,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-github/v28 v28.1.1 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@@ -52,8 +58,10 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -64,20 +72,25 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opensearch-project/opensearch-go v1.1.0 // indirect
github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opencensus.io v0.24.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.8.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.106.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
@@ -85,6 +98,7 @@ require (
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect
+229 -59
View File
@@ -48,18 +48,29 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ=
github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ=
github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
@@ -78,17 +89,20 @@ github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y=
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -100,42 +114,61 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM=
github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s=
github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
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 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
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=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -161,7 +194,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@@ -181,8 +213,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -195,7 +228,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@@ -206,8 +238,10 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
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-20210720184732-4bb14d4b1be1/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -221,10 +255,14 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
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.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -233,21 +271,27 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/libgit2/git2go/v34 v34.0.0/go.mod h1:blVco2jDAw6YTXkErMMqzHLcAjKkwF0aWIRHBqiJkZ0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -259,6 +303,46 @@ 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 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
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 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -269,45 +353,53 @@ github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsr
github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shuffle/shuffle-shared v0.4.17 h1:56ll366bdmIJu/7GFqNC2XTjjl0SGBf430PSq+EB6Ro=
github.com/shuffle/shuffle-shared v0.4.17/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ=
github.com/shuffle/shuffle-shared v0.4.50 h1:fJLfhWIJ5mYap4JwHnD/B5aaLyIULwylFSl3FoWlajM=
github.com/shuffle/shuffle-shared v0.4.50/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.4.57 h1:o+mMPRY4ourkE3R0qdi80jg6RlCtvAJ/VVrPk4y75Hk=
github.com/shuffle/shuffle-shared v0.4.57/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.29 h1:n4vThl7v3mFVXbrIW71XREFdmZZo7mOBAWxnsdiNjDk=
github.com/shuffle/shuffle-shared v0.5.29/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/shuffle/shuffle-shared v0.5.31 h1:OV4IIfKWWFW66WjGvyXOmmsSz3p8pW9L1ge1mDo8ftM=
github.com/shuffle/shuffle-shared v0.5.31/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/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=
@@ -319,17 +411,25 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8=
go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
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 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -363,13 +463,18 @@ 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.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
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.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/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=
@@ -390,6 +495,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
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=
@@ -400,15 +506,26 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
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-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -418,8 +535,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -431,9 +546,16 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
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 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -441,7 +563,11 @@ 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=
@@ -462,23 +588,49 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
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 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -488,15 +640,17 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
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 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -512,6 +666,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -547,15 +702,21 @@ golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4X
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.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
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.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -662,23 +823,31 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-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/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
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=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
@@ -703,6 +872,7 @@ k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4Va
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/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=
+97 -28
View File
@@ -52,7 +52,8 @@ var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME")
var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION"))
var baseimagename = "frikky/shuffle"
//var baseimagename = "frikky/shuffle"
var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME")
// var baseimagename = "registry.hub.docker.com/frikky/shuffle"
var registryName = "registry.hub.docker.com"
@@ -120,7 +121,7 @@ func setWorkflowExecution(ctx context.Context, workflowExecution shuffle.Workflo
}
//log.Printf("[DEBUG][%s] Setting with %d results (pre)", workflowExecution.ExecutionId, len(workflowExecution.Results))
workflowExecution = shuffle.Fixexecution(ctx, workflowExecution)
workflowExecution, _ = shuffle.Fixexecution(ctx, workflowExecution)
cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId)
execData, err := json.Marshal(workflowExecution)
@@ -354,7 +355,8 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason
req.Header.Add("Content-Type", "application/json")
//log.Printf("[DEBUG][%s] All App Logs: %#v", workflowExecution.ExecutionId, allLogs)
newresp, err := topClient.Do(req)
client := shuffle.GetExternalClient(abortUrl)
newresp, err := client.Do(req)
if err != nil {
log.Printf("[WARNING][%s] Failed abort request: %s", workflowExecution.ExecutionId, err)
} else {
@@ -464,6 +466,9 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
// Check action if subflow
// Check if url is default (shuffle-backend)
// If it doesn't exist, add it
// FIXME: This does NOT replace it in all cases as the data
// is not saved in the database as the correct param.
if action.AppName == "shuffle-subflow" {
// Automatic replacement of URL
for paramIndex, param := range action.Parameters {
@@ -471,17 +476,19 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
continue
}
if strings.Contains(param.Value, "shuffle-backend") {
// Automatic replacement as this is default
if len(os.Getenv("BASE_URL")) > 0 {
action.Parameters[paramIndex].Value = os.Getenv("BASE_URL")
log.Printf("[DEBUG][%s] Replaced backend_url with base_url %s", workflowExecution.ExecutionId, os.Getenv("BASE_URL"))
}
if !strings.Contains(param.Value, "shuffle-backend") {
continue
}
if len(os.Getenv("SHUFFLE_CLOUDRUN_URL")) > 0 {
action.Parameters[paramIndex].Value = os.Getenv("SHUFFLE_CLOUDRUN_URL")
log.Printf("[DEBUG][%s] Replaced backend_url with cloudrun %s", workflowExecution.ExecutionId, os.Getenv("SHUFFLE_CLOUDRUN_URL"))
}
// Automatic replacement as this is default
if len(os.Getenv("BASE_URL")) > 0 {
action.Parameters[paramIndex].Value = os.Getenv("BASE_URL")
log.Printf("[DEBUG][%s] Replaced backend_url with base_url %s", workflowExecution.ExecutionId, os.Getenv("BASE_URL"))
}
if len(os.Getenv("SHUFFLE_CLOUDRUN_URL")) > 0 {
action.Parameters[paramIndex].Value = os.Getenv("SHUFFLE_CLOUDRUN_URL")
log.Printf("[DEBUG][%s] Replaced backend_url with cloudrun %s", workflowExecution.ExecutionId, os.Getenv("SHUFFLE_CLOUDRUN_URL"))
}
}
}
@@ -745,6 +752,8 @@ func removeContainer(containername string) error {
return err
}
defer cli.Close()
// FIXME - ucnomment
// containers, err := cli.ContainerList(ctx, types.ContainerListOptions{
// All: true,
@@ -800,6 +809,8 @@ func getWorkerURLs() ([]string, error) {
return workerUrls, err
}
defer cli.Close()
// Specify the name of the service for which you want to list tasks
serviceName := "shuffle-workers"
@@ -827,12 +838,19 @@ func askOtherWorkersToDownloadImage(image string) {
return
}
// Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD
if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" {
log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. NOT distributing images %s", image)
return
}
urls, err := getWorkerURLs()
if err != nil {
log.Printf("[ERROR] Error in listing worker urls: %s", err)
return
}
httpClient := &http.Client{}
for _, url := range urls {
log.Printf("[DEBUG] Trying to speak to: %s", url)
imagesRequest := ImageRequest{
@@ -855,7 +873,6 @@ func askOtherWorkersToDownloadImage(image string) {
continue
}
httpClient := &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
log.Printf("[ERROR] Error in making request to %s : %s", url, err)
@@ -892,6 +909,8 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
return
}
defer dockercli.Close()
for _, action := range relevantActions {
appname := action.AppName
appversion := action.AppVersion
@@ -1116,6 +1135,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
shutdown(workflowExecution, action.ID, fmt.Sprintf("%s", err.Error()), true)
return
} else {
defer reader.Close()
baseTag := strings.Split(image, ":")
if len(baseTag) > 1 {
tag := baseTag[1]
@@ -1226,6 +1246,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
shutdown(workflowExecution, action.ID, fmt.Sprintf("%s", err.Error()), true)
return
} else {
defer reader.Close()
baseTag := strings.Split(image, ":")
if len(baseTag) > 1 {
tag := baseTag[1]
@@ -1481,6 +1502,8 @@ func executionInit(workflowExecution shuffle.WorkflowExecution) error {
}
func handleSubflowPoller(ctx context.Context, workflowExecution shuffle.WorkflowExecution, streamResultUrl, subflowId string) error {
// FIXME: If MEMCACHE is enabled, check in this order:
extra := 0
for _, trigger := range workflowExecution.Workflow.Triggers {
if trigger.AppName == "User Input" || trigger.AppName == "Shuffle Workflow" {
@@ -1494,7 +1517,8 @@ func handleSubflowPoller(ctx context.Context, workflowExecution shuffle.Workflow
bytes.NewBuffer([]byte(data)),
)
newresp, err := topClient.Do(req)
client := shuffle.GetExternalClient(streamResultUrl)
newresp, err := client.Do(req)
if err != nil {
log.Printf("[ERROR] Failed making request (1): %s", err)
time.Sleep(time.Duration(sleepTime) * time.Second)
@@ -2043,7 +2067,8 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
}
if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
log.Printf("[DEBUG][%s] Running setexec with status %s and %d result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results))
log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions))
//result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results))
err = setWorkflowExecution(ctx, *workflowExecution, dbSave)
if err != nil {
resp.WriteHeader(401)
@@ -2101,7 +2126,8 @@ func sendSelfRequest(actionResult shuffle.ActionResult) {
return
}
newresp, err := topClient.Do(req)
client := shuffle.GetExternalClient(streamUrl)
newresp, err := client.Do(req)
if err != nil {
log.Printf("[ERROR][%s] Error running finishing request (2): %s", actionResult.ExecutionId, err)
return
@@ -2158,9 +2184,10 @@ func sendResult(workflowExecution shuffle.WorkflowExecution, data []byte) {
return
}
newresp, err := topClient.Do(req)
client := shuffle.GetExternalClient(streamUrl)
newresp, err := client.Do(req)
if err != nil {
log.Printf("[ERROR][%s] Error running finishing request: %s", workflowExecution.ExecutionId, err)
log.Printf("[ERROR][%s] Error running finishing request (1): %s", workflowExecution.ExecutionId, err)
log.Printf("[DEBUG][%s] Shutting down (23)", workflowExecution.ExecutionId)
shutdown(workflowExecution, "", "", false)
return
@@ -2189,7 +2216,7 @@ func validateFinished(workflowExecution shuffle.WorkflowExecution) bool {
}
//startAction, extra, children, parents, visited, executed, nextActions, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId)
workflowExecution = shuffle.Fixexecution(ctx, workflowExecution)
workflowExecution, _ = shuffle.Fixexecution(ctx, workflowExecution)
_, extra, _, _, _, _, _, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId)
log.Printf("[INFO][%s] VALIDATION. Status: %s, shuffle.Actions: %d, Extra: %d, Results: %d. Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Workflow.Actions), extra, len(workflowExecution.Results), workflowExecution.ExecutionParent)
@@ -2337,6 +2364,8 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener {
port := listener.Addr().(*net.TCPAddr).Port
// Set the port environment variable
os.Setenv("WORKER_PORT", fmt.Sprintf("%d", port))
log.Printf("[DEBUG] Starting webserver (2) on port %d with hostname: %s", port, hostname)
appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, port)
@@ -2348,6 +2377,12 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener {
func downloadDockerImageBackend(client *http.Client, imageName string) error {
log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages)
// Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD
if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" {
log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName)
return nil
}
if arrayContains(downloadedImages, imageName) {
log.Printf("[DEBUG] Image %s already downloaded", imageName)
return nil
@@ -2407,12 +2442,15 @@ func downloadDockerImageBackend(client *http.Client, imageName string) error {
return err
}
defer dockercli.Close()
imageLoadResponse, err := dockercli.ImageLoad(context.Background(), tar, true)
if err != nil {
log.Printf("[ERROR] Error loading images: %s", err)
return err
}
defer imageLoadResponse.Body.Close()
body, err := ioutil.ReadAll(imageLoadResponse.Body)
if err != nil {
log.Printf("[ERROR] Error reading: %s", err)
@@ -2448,7 +2486,7 @@ func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) {
ctx := context.Background()
nodes, err := dockercli.NodeList(ctx, types.NodeListOptions{})
if err != nil {
return 0, err
return 1, err
}
nodeCount := int64(0)
@@ -2459,6 +2497,20 @@ func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) {
}
}
// Check for SHUFFLE_MAX_NODES
maxNodesString := os.Getenv("SHUFFLE_MAX_SWARM_NODES")
// Make it into a number and check if it's lower than nodeCount
if len(maxNodesString) > 0 {
maxNodes, err := strconv.ParseInt(maxNodesString, 10, 64)
if err != nil {
return nodeCount, err
}
if nodeCount > maxNodes {
nodeCount = maxNodes
}
}
return nodeCount, nil
/*
@@ -2570,9 +2622,11 @@ func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int,
log.Printf("[DEBUG][%s] Adding %s to cache (%#v)", workflowExecution.ExecutionId, newExecId, action.Name)
}
// FIXME: Add 5 tries
client := shuffle.GetExternalClient(streamUrl)
newresp, err := topClient.Do(req)
// Set client timeout to 5 seconds
//client.Timeout = time.Duration(10) * time.Second
newresp, err := client.Do(req)
if err != nil {
// Another timeout issue here somewhere
// context deadline
@@ -2630,6 +2684,8 @@ func baseDeploy() {
return
}
defer cli.Close()
for key, value := range autoDeploy {
newNameSplit := strings.Split(key, ":")
@@ -2771,6 +2827,7 @@ func getStreamResultsWrapper(client *http.Client, req *http.Request, workflowExe
log.Printf("[DEBUG] Environments: %s. Source: %s. 1 env = webserver, 0 or >1 = default. Subflow exists: %#v", environments, workflowExecution.ExecutionSource, subflowFound)
if len(environments) == 1 && workflowExecution.ExecutionSource != "default" && !subflowFound {
log.Printf("[DEBUG] Running OPTIMIZED execution (not manual)")
os.Setenv("SHUFFLE_OPTIMIZED", "true")
listener := webserverSetup(workflowExecution)
err := executionInit(workflowExecution)
if err != nil {
@@ -2784,7 +2841,13 @@ func getStreamResultsWrapper(client *http.Client, req *http.Request, workflowExe
handleExecutionResult(workflowExecution)
}()
log.Printf("[DEBUG] Running with port %#v", os.Getenv("WORKER_PORT"))
runWebserver(listener)
// Set environment variable
//log.Printf("Before wait")
//wg := sync.WaitGroup{}
//wg.Add(1)
@@ -2846,6 +2909,12 @@ func main() {
timezone = "Europe/Amsterdam"
}
if baseimagename == "" {
log.Printf("[DEBUG] Setting baseimagename")
baseimagename = "frikky/shuffle" // Dockerhub
//baseimagename = "shuffle" // Github (ghcr.io)
}
topClient = client
swarmConfig := os.Getenv("SHUFFLE_SWARM_CONFIG")
log.Printf("[INFO] Running with timezone %s and swarm config %#v", timezone, swarmConfig)
@@ -2897,7 +2966,6 @@ func main() {
shutdown(workflowExecution, "", "", true)
}
topClient = client
firstRequest := true
environments := []string{}
for {
@@ -3007,15 +3075,14 @@ func handleRunExecution(resp http.ResponseWriter, request *http.Request) {
var workflowExecution shuffle.WorkflowExecution
data = fmt.Sprintf(`{"execution_id": "%s", "authorization": "%s"}`, execRequest.ExecutionId, execRequest.Authorization)
streamResultUrl := fmt.Sprintf("%s/api/v1/streams/results", baseUrl)
topClient = shuffle.GetExternalClient(streamResultUrl)
req, err := http.NewRequest(
"POST",
streamResultUrl,
bytes.NewBuffer([]byte(data)),
)
newresp, err := topClient.Do(req)
client := shuffle.GetExternalClient(streamResultUrl)
newresp, err := client.Do(req)
if err != nil {
log.Printf("[ERROR] Failed making request (2): %s", err)
resp.WriteHeader(401)
@@ -3145,6 +3212,8 @@ func handleDownloadImage(resp http.ResponseWriter, request *http.Request) {
return
}
defer client.Close()
// check if images are already downloaded
// Retrieve a list of Docker images
images, err := client.ImageList(context.Background(), types.ImageListOptions{})
@@ -3155,6 +3224,7 @@ func handleDownloadImage(resp http.ResponseWriter, request *http.Request) {
return
}
for _, img := range images {
for _, tag := range img.RepoTags {
splitTag := strings.Split(tag, ":")
@@ -3225,5 +3295,4 @@ func runWebserver(listener net.Listener) {
if err != nil {
log.Printf("[ERROR] Serve issue in worker: %#v", err)
}
log.Printf("[DEBUG] Do we see this?")
}