Merge branch '2.0.0' into main

This commit is contained in:
Frikky
2024-05-31 09:40:24 +02:00
committed by GitHub
15 changed files with 750 additions and 333 deletions
+10 -10
View File
@@ -3,7 +3,7 @@ name: dockerbuild
on: on:
push: push:
branches: branches:
- main - 2.0.0
paths: paths:
- "**" - "**"
- "!.github/**" - "!.github/**"
@@ -11,7 +11,7 @@ on:
- "!docker-compose.yml" - "!docker-compose.yml"
jobs: jobs:
main: main:
runs-on: ubuntu-latest runs-on: ubuntu-nightly
continue-on-error: ${{ matrix.experimental }} continue-on-error: ${{ matrix.experimental }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -19,23 +19,23 @@ jobs:
include: include:
- app: frontend - app: frontend
path: frontend path: frontend
version: 1.4.0 version: nightly
experimental: true experimental: true
- app: backend - app: backend
path: backend path: backend
version: 1.4.0 version: nightly
experimental: true experimental: true
- app: app_sdk - app: app_sdk
path: backend/app_sdk path: backend/app_sdk
version: 1.4.0 version: nightly
experimental: true experimental: true
- app: orborus - app: orborus
path: functions/onprem/orborus path: functions/onprem/orborus
version: 1.4.0 version: nightly
experimental: true experimental: true
- app: worker - app: worker
path: functions/onprem/worker path: functions/onprem/worker
version: 1.4.0 version: nightly
experimental: true experimental: true
steps: steps:
- name: Checkout - name: Checkout
@@ -77,11 +77,11 @@ jobs:
cache-to: type=local,dest=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache
tags: | tags: |
ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }} ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }}
ghcr.io/shuffle/shuffle-${{ matrix.app }}:latest ghcr.io/shuffle/shuffle-${{ matrix.app }}:nightly
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }} ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }}
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:latest ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:nightly
frikky/shuffle-${{ matrix.app }}:${{ matrix.version }} frikky/shuffle-${{ matrix.app }}:${{ matrix.version }}
frikky/shuffle-${{ matrix.app }}:latest frikky/shuffle-${{ matrix.app }}:nightly
frikky/shuffle:${{ matrix.app }} frikky/shuffle:${{ matrix.app }}
- name: Image digest - name: Image digest
+86 -58
View File
@@ -928,7 +928,7 @@ class AppBase:
#self.action = action #self.action = action
loopnames = [] loopnames = []
#self.logger.info(f"Baseparams to check!!: {baseparams}") self.logger.info(f"Baseparams to check: {baseparams}")
for key, value in baseparams.items(): for key, value in baseparams.items():
check_value = "" check_value = ""
for param in self.original_action["parameters"]: for param in self.original_action["parameters"]:
@@ -991,20 +991,33 @@ class AppBase:
except: except:
pass pass
#self.logger.info(f"MERGE: {should_merge}") #self.logger.info(f"VALUE LENGTH: {len(value)}")
if isinstance(value, list): if isinstance(value, list):
#subvalue = []
# Override for single vs multi items
#if len(value) > 0:
# if isinstance(value[0], list) and len(value[0]) == 1:
# subvalue = value[0]
# subvalue = value[0]
if len(value) <= 1: if len(value) <= 1:
# FIXME: This broke some shit for a single item fml
# Necessary as override again :(
if len(value) == 1: if len(value) == 1:
baseparams[key] = value[0] baseparams[key] = value[0]
#if "#" in check_value: #if "#" in check_value:
# should_merge = True # should_merge = True
else: else:
#if len(value) > 1:
if not should_merge: if not should_merge:
self.logger.info("[DEBUG] Adding WITHOUT looping list") self.logger.info("[DEBUG] Adding WITHOUT looping list")
else: else:
if len(value) not in listlengths: if len(value) not in listlengths:
listlengths.append(len(value)) listlengths.append(len(value))
#listlength
listitems.append( listitems.append(
{ {
@@ -1018,24 +1031,31 @@ class AppBase:
#self.logger.info(f"{value} is not a list") #self.logger.info(f"{value} is not a list")
pass pass
self.logger.info("[DEBUG] Listlengths: %s" % listlengths) self.logger.info("[DEBUG] Listlengths: %s - listitems: %d" % (listlengths, len(listitems)))
#if len(listitems) == 0:
if len(listlengths) == 0: if len(listlengths) == 0:
self.logger.info("[DEBUG] NO multiplier. Running a single iteration.") self.logger.info("[DEBUG] NO multiplier. Running a single iteration.")
paramlist.append(baseparams) paramlist.append(baseparams)
#elif len(listitems) == 1:
elif len(listlengths) == 1: elif len(listlengths) == 1:
self.logger.info("All subitems are the same length")
for item in listitems: for item in listitems:
# This loops should always be length 1 # This loops should always be length 1
for key, value in item.items(): for key, value in item.items():
if isinstance(value, int): if not isinstance(value, int):
if len(paramlist) == value: continue
for subloop in range(value):
baseitem = copy.deepcopy(baseparams) if len(paramlist) == value:
paramlist[subloop][key] = baseparams[key][subloop] for subloop in range(value):
else: baseitem = copy.deepcopy(baseparams)
for subloop in range(value): paramlist[subloop][key] = baseparams[key][subloop]
baseitem = copy.deepcopy(baseparams) else:
baseitem[key] = baseparams[key][subloop] for subloop in range(value):
paramlist.append(baseitem) baseitem = copy.deepcopy(baseparams)
baseitem[key] = baseparams[key][subloop]
paramlist.append(baseitem)
else: else:
newlength = 1 newlength = 1
@@ -1046,10 +1066,14 @@ class AppBase:
self.logger.info("[DEBUG] Newlength of array: %d. Lists: %s" % (newlength, all_lists)) self.logger.info("[DEBUG] Newlength of array: %d. Lists: %s" % (newlength, all_lists))
# Get the cartesian product of the arrays # Get the cartesian product of the arrays
#cartesian = await self.cartesian_product(all_lists) #cartesian = await self.cartesian_product(all_lists)
cartesian = self.cartesian_product(all_lists) try:
newlist = [] cartesian = self.cartesian_product(all_lists)
for item in cartesian: newlist = []
newlist.append(list(item)) for item in cartesian:
newlist.append(list(item))
except Exception as e:
self.logger.info(f"[ERROR] Error in cartesian product: {e}")
newlist = []
newobject = {} newobject = {}
for subitem in range(len(newlist)): for subitem in range(len(newlist)):
@@ -1059,7 +1083,7 @@ class AppBase:
paramlist.append(baseitem) paramlist.append(baseitem)
#self.logger.info("PARAMLIST: %s" % paramlist) self.logger.info("CARTESIAN PARAMLIST: %s" % paramlist)
#newlist[subitem[0]] #newlist[subitem[0]]
#if len(newlist) > 0: #if len(newlist) > 0:
@@ -1070,14 +1094,14 @@ class AppBase:
#self.logger.info("Listlengths: %s" % listlengths) #self.logger.info("Listlengths: %s" % listlengths)
#paramlist = [baseparams] #paramlist = [baseparams]
#self.logger.info("[INFO] Return paramlist: %s" % paramlist) #self.logger.info("[INFO] Return paramlist (1): %s" % paramlist)
return paramlist return paramlist
# Runs recursed versions with inner loops and such # Runs recursed versions with inner loops and such
#async def run_recursed_items(self, func, baseparams, loop_wrapper): #async def run_recursed_items(self, func, baseparams, loop_wrapper):
def run_recursed_items(self, func, baseparams, loop_wrapper): def run_recursed_items(self, func, baseparams, loop_wrapper):
#self.logger.info(f"RECURSED ITEMS: {baseparams}") self.logger.info(f"PRE RECURSED ITEMS: {baseparams}")
has_loop = False has_loop = False
newparams = {} newparams = {}
@@ -1085,29 +1109,30 @@ class AppBase:
if isinstance(value, list) and len(value) > 0: if isinstance(value, list) and len(value) > 0:
self.logger.info(f"[DEBUG] In list check for {key}") self.logger.info(f"[DEBUG] In list check for {key}")
try: for value_index in range(len(value)):
# Added skip for body (OpenAPI) which uses data= in requests try:
# Can be screwed up if they name theirs body too # Added skip for body (OpenAPI) which uses data= in requests
if key != "body": # Can be screwed up if they name theirs body too
value[0] = json.loads(value[0]) if key != "body":
except json.decoder.JSONDecodeError as e: value[value_index] = json.loads(value[value_index])
pass except json.decoder.JSONDecodeError as e:
except TypeError as e: pass
pass except TypeError as e:
pass
try: try:
if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list): #if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list):
try: # try:
loop_wrapper[key] += 1 # loop_wrapper[key] += 1
except Exception as e: # except Exception as e:
self.logger.info("[WARNING] Exception in loop wrapper: {e}") # self.logger.info(f"[WARNING] Exception in loop wrapper: {e}")
loop_wrapper[key] = 1 # loop_wrapper[key] = 1
newparams[key] = value[0] # newparams[key] = value[0]
has_loop = True # has_loop = True
else: #else:
#self.logger.info(f"Key {key} is NOT a list within a list. Value: {value}") #self.logger.info(f"Key {key} is NOT a list within a list. Value: {value}")
newparams[key] = value newparams[key] = value
except Exception as e: except Exception as e:
self.logger.info(f"[WARNING] Error in baseparams list: {e}") self.logger.info(f"[WARNING] Error in baseparams list: {e}")
newparams[key] = value newparams[key] = value
@@ -1119,19 +1144,18 @@ class AppBase:
#ret = await self.run_recursed_items(func, newparams, loop_wrapper) #ret = await self.run_recursed_items(func, newparams, loop_wrapper)
ret = self.run_recursed_items(func, newparams, loop_wrapper) ret = self.run_recursed_items(func, newparams, loop_wrapper)
else: else:
#self.logger.info(f"[DEBUG] Should run multiplier check with params (inner): {newparams}") self.logger.info(f"[DEBUG] Should run multiplier check with params (inner): {newparams}")
self.logger.info(f"[DEBUG] Should run multiplier check with params (inner)") #self.logger.info(f"[DEBUG] Should run multiplier check with params (inner)")
# 1. Find the loops that are required and create new multipliers # 1. Find the loops that are required and create new multipliers
# If here: check for multipliers within this scope. # If here: check for multipliers within this scope.
ret = [] ret = []
#param_multiplier = await self.get_param_multipliers(newparams)
param_multiplier = self.get_param_multipliers(newparams) param_multiplier = self.get_param_multipliers(newparams)
#self.logger.info("PARAM MULTIPLIER: %s" % param_multiplier) #self.logger.info("PARAM MULTIPLIER: %s" % param_multiplier)
# FIXME: This does a deduplication of the data # FIXME: This does a deduplication of the data
new_params = self.validate_unique_fields(param_multiplier) new_params = self.validate_unique_fields(param_multiplier)
#self.logger.info(f"NEW PARAMS: {new_params}")
if len(new_params) == 0: if len(new_params) == 0:
self.logger.info("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE") self.logger.info("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE")
self.action_result = { self.action_result = {
@@ -1151,9 +1175,10 @@ class AppBase:
return return
else: else:
#subparams = new_params #subparams = new_params
#self.logger.info(f"NEW PARAMS: {new_params}")
param_multiplier = new_params param_multiplier = new_params
#self.logger.info(f"NEW PARAM MULTIPLIER: {param_multiplier}")
#if isinstance(new_params, list) and len(new_params) == 1: #if isinstance(new_params, list) and len(new_params) == 1:
# params = new_params[0] # params = new_params[0]
#else: #else:
@@ -1205,7 +1230,8 @@ class AppBase:
tmp = json.dumps({ tmp = json.dumps({
"success": False, "success": False,
"reason": f"An error occured during execution: {e}", "reason": f"An error occured during the App Function Run (not Shuffle)",
"details": f"{e}",
}) })
@@ -1255,7 +1281,7 @@ class AppBase:
#else: #else:
ret.append(new_value) ret.append(new_value)
self.logger.info("[INFO] Ret length: %d" % len(ret)) self.logger.info("[INFO] Function return length: %d" % len(ret))
if len(ret) == 1: if len(ret) == 1:
#ret = ret[0] #ret = ret[0]
self.logger.info("[DEBUG] DONT make list of 1 into 0!!") self.logger.info("[DEBUG] DONT make list of 1 into 0!!")
@@ -1953,11 +1979,11 @@ class AppBase:
try: try:
#self.logger.info(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}") #self.logger.info(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}")
# data = data.replace("\'", "\"") # data = data.replace("\'", "\"")
data = data.replace("True", "true") data = data.replace("True", "true", -1)
data = data.replace("False", "false") data = data.replace("False", "false", -1)
data = data.replace("None", "null") data = data.replace("None", "null", -1)
data = data.replace("\"", "\\\"") data = data.replace("\"", "\\\"", -1)
data = data.replace("'", "\"") data = data.replace("'", "\"", -1)
tmp_len = json.loads(data, parse_float=str, parse_int=str, parse_constant=str) tmp_len = json.loads(data, parse_float=str, parse_int=str, parse_constant=str)
except (NameError, KeyError, TypeError, json.decoder.JSONDecodeError) as e: except (NameError, KeyError, TypeError, json.decoder.JSONDecodeError) as e:
@@ -2037,10 +2063,9 @@ class AppBase:
# if result is a string then parse else return # if result is a string then parse else return
if isinstance(inner_result, str): if isinstance(inner_result, str):
parse_string = parse_string.replace(f"{custom_casting[0]}({c_parentheses})", inner_result) parse_string = parse_string.replace(f"{custom_casting[0]}({c_parentheses})", inner_result, 1)
elif isinstance(inner_result, list): elif isinstance(inner_result, list):
parse_string = parse_string.replace(f"{custom_casting[0]}({c_parentheses})", parse_string = parse_string.replace(f"{custom_casting[0]}({c_parentheses})", json.dumps(inner_result), 1)
json.dumps(inner_result))
else: else:
parse_string = inner_result parse_string = inner_result
break break
@@ -3432,6 +3457,7 @@ class AppBase:
# Loops in general goes in here to be parsed out as one->multi # Loops in general goes in here to be parsed out as one->multi
if len(actualitem) > 0: if len(actualitem) > 0:
self.logger.info(f"[INFO] Found {len(actualitem)} items in {parameter['name']}. MULTI EXEC.")
multiexecution = True multiexecution = True
handled = False handled = False
@@ -3612,10 +3638,12 @@ class AppBase:
#self.logger.info() #self.logger.info()
if not multiexecution: if not multiexecution:
self.logger.info("NOT MULTI EXEC")
# Runs a single iteration here # Runs a single iteration here
new_params = self.validate_unique_fields(params) new_params = self.validate_unique_fields(params)
if isinstance(new_params, list) and len(new_params) == 1: if isinstance(new_params, list) and len(new_params) == 1:
params = new_params[0] params = new_params[0]
#params = new_params
else: else:
#self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") #self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE")
self.action_result["status"] = "SKIPPED" self.action_result["status"] = "SKIPPED"
@@ -3663,7 +3691,7 @@ class AppBase:
newres = { newres = {
"success": False, "success": False,
"reason": "Iteration count more than 10. This happens if the input to the action is wrong. Try remaking the action, and contact support@shuffler.io if this persists.", "reason": "Iteration count more than 10. This happens if the input to the action is wrong. Try remaking the action, and contact support@shuffler.io if this persists.",
"details": found_error, "details": f"{found_error}",
} }
break break
@@ -3897,7 +3925,7 @@ class AppBase:
self.action_result["result"] = json.dumps({ self.action_result["result"] = json.dumps({
"success": False, "success": False,
"reason": f"Typeerror. Most likely due to a list that should've been a string. See details for more info.", "reason": f"Typeerror. Most likely due to a list that should've been a string. See details for more info.",
"details": e, "details": f"{e}",
}) })
#self.action_result["result"] = "TypeError: %s" % str(e) #self.action_result["result"] = "TypeError: %s" % str(e)
else: else:
@@ -3925,7 +3953,7 @@ class AppBase:
self.action_result["result"] = json.dumps({ self.action_result["result"] = json.dumps({
"success": False, "success": False,
"reason": f"Request error - failing silently. Details in detail section", "reason": f"Request error - failing silently. Details in detail section",
"details": e, "details": f"{e}",
}) })
except json.decoder.JSONDecodeError as e: except json.decoder.JSONDecodeError as e:
self.action_result["result"] = f"Request error: {e}" self.action_result["result"] = f"Request error: {e}"
@@ -3942,7 +3970,7 @@ class AppBase:
self.action_result["result"] = json.dumps({ self.action_result["result"] = json.dumps({
"success": False, "success": False,
"reason": f"General exception in the app. See shuffle action logs for more details.", "reason": f"General exception in the app. See shuffle action logs for more details.",
"details": e, "details": f"{e}",
}) })
# Send the result :) # Send the result :)
+1 -1
View File
@@ -117,7 +117,7 @@ const CacheView = (props) => {
const deleteCache = (orgId, key) => { const deleteCache = (orgId, key) => {
toast("Attempting to delete Cache"); //toast("Attempting to delete Cache");
// method: "DELETE", // method: "DELETE",
const method = "POST" const method = "POST"
@@ -477,7 +477,7 @@ const ConfigureWorkflow = (props) => {
setConfigureWorkflowModalOpen(false) setConfigureWorkflowModalOpen(false)
} }
setRequiredTriggers(requiredTriggers) //setRequiredTriggers(requiredTriggers)
setRequiredVariables(requiredVariables) setRequiredVariables(requiredVariables)
setRequiredActions(newactions) setRequiredActions(newactions)
} }
+53 -24
View File
@@ -62,8 +62,7 @@ const Header = (props) => {
userdata, userdata,
isMobile, isMobile,
serverside, serverside,
curpath, billingInfo,
billingInfo
} = props; } = props;
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
@@ -114,6 +113,10 @@ const Header = (props) => {
window.location.host === "shuffler.io" || window.location.host === "shuffler.io" ||
window.location.host === "localhost:5002"; window.location.host === "localhost:5002";
const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string")
? window.location.pathname
: "";
const clearNotifications = () => { const clearNotifications = () => {
// Don't really care about the logout // Don't really care about the logout
@@ -784,7 +787,7 @@ const Header = (props) => {
</Link> </Link>
</ListItem> </ListItem>
</List> </List>
<List style={{ flex: 1.5, display: "flex", flexDirect: "row", marginTop: 10, itemAlign: "center", }} component="nav"> <List style={{ flex: 1.5, display: "flex", flexDirect: "row", marginTop: 10, itemAlign: "center", padding: 0 }} component="nav">
<ListItem style={{ textAlign: "center", marginLeft: "0px" }}> <ListItem style={{ textAlign: "center", marginLeft: "0px" }}>
<Link to="/usecases" style={hrefStyle}> <Link to="/usecases" style={hrefStyle}>
<Button <Button
@@ -849,9 +852,27 @@ const Header = (props) => {
</Button> </Button>
</Link> </Link>
</ListItem> </ListItem>
<ListItem
style={{
textAlign: "center",
marginLeft: 0,
paddingRight: 0,
}}
>
<Link rel="noopener noreferrer" to="/training" style={hrefStyle}>
<Button
variant="text"
color="secondary"
style={menuText}
onClick={() => { }}
>
Training
</Button>
</Link>
</ListItem>
</List> </List>
<List <List
style={{ flex: 2, display: "flex", alignItems: "flex-start", }} style={{ flex: 2, display: "flex", alignItems: "flex-start", padding: 0, }}
component="nav" component="nav"
> >
<div style={{ maxWidth: 70, minWidth: 70, }} /> <div style={{ maxWidth: 70, minWidth: 70, }} />
@@ -1514,14 +1535,15 @@ const Header = (props) => {
*/ */
const topbarHeight = showTopbar ? 40 : 0 const topbarHeight = showTopbar ? 40 : 0
const topbar = !showTopbar ? null : const topbar = !isCloud || !showTopbar ? null :
curpath === "/" || curpath.includes("/docs/") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" ? curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/training" ?
<span style={{ zIndex: 50001, }}> <span style={{ zIndex: 50001, }}>
<div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}> <div style={{ position: "relative", height: topbarHeight, backgroundImage: "linear-gradient(to right, #f86a3e, #f34079)", overflow: "hidden", }}>
<Typography variant="body1" style={{ paddingTop: 7, margin: "auto", textAlign: "center", color: "white", }}> <Typography variant="body1" style={{ paddingTop: 7, margin: "auto", textAlign: "center", color: "white", }}>
Shuffle 1.4 is out! Read more about&nbsp; {/* Shuffle 1.4.0 is out! Read more about&nbsp; */}
<u> Shuffle now offers&nbsp;
<a href="https://github.com/Shuffle/Shuffle" style={{ color: "inherit", }} onClick={() => { {/* <u>
<a href="https://github.com/Shuffle/Shuffle/releases/tag/v1.4.0" target="_blank" style={{ color: "inherit", }} onClick={() => {
ReactGA.event({ ReactGA.event({
category: "landingpage", category: "landingpage",
action: "click_header_features", action: "click_header_features",
@@ -1557,16 +1579,16 @@ const Header = (props) => {
Pricing Pricing
</span> </span>
</u> </u>
&nbsp;and&nbsp; &nbsp;and&nbsp; */}
<u> <u>
<span onClick={() => { <span onClick={() => {
ReactGA.event({ ReactGA.event({
category: "landingpage", category: "landingpage",
action: "click_header_creators", action: "click_header_training",
label: "", label: "",
}) })
navigate("/creators") navigate("/training")
//if (window.drift !== undefined) { //if (window.drift !== undefined) {
// window.drift.api.startInteraction({ interactionId: 341911 }) // window.drift.api.startInteraction({ interactionId: 341911 })
@@ -1574,7 +1596,7 @@ const Header = (props) => {
// console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) // console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
//} //}
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}> }} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
Earning as a Creator Public Training!
</span> </span>
</u> </u>
</Typography> </Typography>
@@ -1586,8 +1608,8 @@ const Header = (props) => {
: :
null null
return !isMobile ? return !isMobile ? (
<div style={{ marginTop: 0, }}> <div style={{ marginTop: 0 }}>
<AppBar <AppBar
color="transparent" color="transparent"
elevation={0} elevation={0}
@@ -1599,17 +1621,24 @@ const Header = (props) => {
backgroundColor: theme.palette.backgroundColor, backgroundColor: theme.palette.backgroundColor,
}} }}
> >
{topbar} {topbar}
<div
<div style={{ position: "sticky", top: 0, }}> style={{
{loginTextBrowser} position: "sticky",
</div> top: 0,
{modalView} minHeight: 68,
</AppBar> maxHeight: 68,
</div> backgroundColor: theme.palette.backgroundColor,
: }}
>
{loginTextBrowser}
</div>
{modalView}
</AppBar>
</div>
) : (
<MobileView>{loginTextMobile}</MobileView> <MobileView>{loginTextMobile}</MobileView>
);
}; };
export default Header; export default Header;
+36 -19
View File
@@ -18,6 +18,7 @@ import {
ButtonBase, ButtonBase,
Tooltip, Tooltip,
Select, Select,
Autocomplete,
MenuItem, MenuItem,
Divider, Divider,
Dialog, Dialog,
@@ -296,7 +297,17 @@ const AuthenticationOauth2 = (props) => {
} }
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => { const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt, skipScopeReplace) => {
console.log("SKIP SCOPE: ", skipScopeReplace)
if (skipScopeReplace === false || skipScopeReplace === undefined) {
console.log("Selected scopes: ", selectedScopes)
if (selectedScopes !== undefined && selectedScopes !== null && selectedScopes.length > 0) {
toast("Using your scopes instead of the default ones")
scopes = selectedScopes
}
}
if ((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) { if ((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) {
console.log("No redirect URI found, and token URI found. Assuming client credentials flow and saving directly in the database") console.log("No redirect URI found, and token URI found. Assuming client credentials flow and saving directly in the database")
@@ -336,6 +347,7 @@ const AuthenticationOauth2 = (props) => {
"value": tokenUri, "value": tokenUri,
}] }]
if (authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0) { if (authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0) {
if (authenticationType.grant_type === "client_credentials") { if (authenticationType.grant_type === "client_credentials") {
parsedFields.push({ parsedFields.push({
@@ -502,6 +514,8 @@ const AuthenticationOauth2 = (props) => {
getAppAuthentication(true, true, true); getAppAuthentication(true, true, true);
} }
toast("Authentication successful!")
// This is more a guess than anything // This is more a guess than anything
// Should be handled in getAppAuthentication() // Should be handled in getAppAuthentication()
// in the parent component to make it accurate, // in the parent component to make it accurate,
@@ -635,9 +649,7 @@ const AuthenticationOauth2 = (props) => {
}; };
const handleScopeChange = (event) => { const handleScopeChange = (event) => {
const { const {target: { value }} = event;
target: { value },
} = event;
console.log("VALUE: ", value); console.log("VALUE: ", value);
@@ -972,13 +984,13 @@ const AuthenticationOauth2 = (props) => {
} }
{allscopes === undefined || allscopes === null || allscopes.length === 0 ? null : "Scopes (access rights)"} {allscopes === undefined || allscopes === null || allscopes.length === 0 ? null : "Scopes (access rights)"}
{allscopes === undefined || allscopes === null || allscopes.length === 0 ? null : ( {allscopes === undefined || allscopes === null || allscopes.length === 0 ? null : (
<div style={{width: "100%", marginTop: 10, display: "flex"}}> <div style={{width: "100%", marginTop: 10, display: "flex"}}>
<span> <span>
<Select <Autocomplete
multiple multiple
underline={false} underline={false}
value={selectedScopes}
label="Scopes" label="Scopes"
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
@@ -987,23 +999,28 @@ const AuthenticationOauth2 = (props) => {
minWidth: 300, minWidth: 300,
maxWidth: 300, maxWidth: 300,
}} }}
onChange={(e) => { onChange={(e, value) => {
handleScopeChange(e) //handleScopeChange(e)
setSelectedScopes(typeof value === "string" ? value.split(",") : value);
}} }}
fullWidth fullWidth
input={<Input id="select-multiple-native" />} input={<Input id="select-multiple-native" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps} MenuProps={MenuProps}
> options={allscopes}
{allscopes.map((data, index) => { getOptionLabel={(option) => option}
renderInput={(params) => {
return ( return (
<MenuItem key={index} value={data}> <div>
<Checkbox checked={selectedScopes.indexOf(data) > -1} /> {/*<Checkbox checked={selectedScopes.indexOf(data) > -1} />*/}
<ListItemText primary={data} /> <TextField
</MenuItem> {...params}
); label="Search Scopes"
})} variant="outlined"
</Select> />
</div>
)
}}
/>
</span> </span>
{((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) ? null : {((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) ? null :
@@ -1039,7 +1056,7 @@ const AuthenticationOauth2 = (props) => {
"autoClose": 1500, "autoClose": 1500,
}) })
handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes); handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes, undefined, true);
}} }}
color="primary" color="primary"
> >
+1 -1
View File
@@ -621,7 +621,7 @@ const ParsedAction = (props) => {
var helperText = "" var helperText = ""
var looperText = "" var looperText = ""
//const found = input_data.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) //const found = input_data.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g)
var found = input_data.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) var found = input_data.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
if (found !== null && found !== undefined) { if (found !== null && found !== undefined) {
var new_occurences = [] var new_occurences = []
+1 -1
View File
@@ -821,7 +821,7 @@ const RuntimeDebugger = (props) => {
}} }}
onChange={(e)=>{handleQueryChange(e)}} onChange={(e)=>{handleQueryChange(e)}}
color="primary" color="primary"
placeholder="Search Workflow Name, Status, Execution Argument, Results.." placeholder="Filter by Workflow Name, Status, Execution Argument, Results.."
id="shuffle_search_field" id="shuffle_search_field"
/> />
</div> </div>
+27 -39
View File
@@ -580,12 +580,12 @@ const CodeEditor = (props) => {
// var session = localcodedata.getSession(); // var session = localcodedata.getSession();
//var code_lines = localcodedata.split('\n') //var code_lines = localcodedata.split('\n')
var code_lines = value.split('\n')
var newMarkers = [] var newMarkers = []
var code_lines = value.split('\n')
for (var i = 0; i < code_lines.length; i++) { for (var i = 0; i < code_lines.length; i++) {
var current_code_line = code_lines[i]; var current_code_line = code_lines[i]
var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g); var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g);
if (!variable_occurence) { if (!variable_occurence) {
continue; continue;
@@ -623,15 +623,26 @@ const CodeEditor = (props) => {
var startCh = dollar_occurence[occ] var startCh = dollar_occurence[occ]
var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] var endCh = dollar_occurence[occ] + dollar_occurence_len[occ]
try {
newMarkers.push({ newMarkers.push({
startRow: i, startRow: i,
startCol: startCh, startCol: startCh,
endRow: i, endRow: i+1,
endCol: endCh, endCol: endCh+1,
className: correctVariable ? "good-marker" : "bad-marker", className: correctVariable ? "good-marker" : "bad-marker",
type: "text", type: "text",
}) })
} catch (e) {
console.log("Error in color highlighting: ", e);
newMarkers.push({
startRow: i,
startCol: startCh,
endRow: i,
endCol: endCh,
className: correctVariable ? "good-marker" : "bad-marker",
type: "text",
})
}
setMarkers(newMarkers) setMarkers(newMarkers)
} }
@@ -643,7 +654,7 @@ const CodeEditor = (props) => {
} }
setMarkers(newMarkers) setMarkers(newMarkers)
}; }
const replaceVariables = (swapVariable) => { const replaceVariables = (swapVariable) => {
// var updatedCode = localcodedata.slice(0,index) + "$" + str + localcodedata.slice(index+currentVariable.length+1,) // var updatedCode = localcodedata.slice(0,index) + "$" + str + localcodedata.slice(index+currentVariable.length+1,)
@@ -686,7 +697,7 @@ const CodeEditor = (props) => {
const expectedOutput = (input) => { const expectedOutput = (input) => {
//const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) //const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g)
const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) const found = input.match(/[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
// Whelp this is inefficient af. Single loop pls // Whelp this is inefficient af. Single loop pls
// When the found array is empty. // When the found array is empty.
@@ -1448,34 +1459,11 @@ const CodeEditor = (props) => {
</div> </div>
} }
<IconButton
style={{
marginLeft: isMobile ? "80%" : 30,
height: 50,
width: 50,
}}
onClick={() => {
}}
>
<Tooltip
color="primary"
title={"Test Liquid in the playground"}
placement="top"
>
<a
href="https://pwwang.github.io/liquidpy/playground/"
rel="norefferer"
target="_blank"
>
<ExtensionIcon style={{color: "rgba(255,255,255,0.7)"}}/>
</a>
</Tooltip>
</IconButton>
<IconButton <IconButton
style={{ style={{
height: 50, height: 50,
width: 50, width: 50,
marginLeft: 100,
}} }}
disabled={isAiLoading} disabled={isAiLoading}
onClick={() => { onClick={() => {
@@ -1536,7 +1524,7 @@ const CodeEditor = (props) => {
setCurrentLine(cursorPosition.row) setCurrentLine(cursorPosition.row)
findIndex(cursorPosition.row, cursorPosition.column) findIndex(cursorPosition.row, cursorPosition.column)
highlight_variables(localcodedata) //highlight_variables(localcodedata)
//console.log("VALUE CURSOR: ", value) //console.log("VALUE CURSOR: ", value)
}} }}
onChange={(value, editor) => { onChange={(value, editor) => {
+402 -144
View File
@@ -378,7 +378,7 @@ const svgSize = 24;
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const AngularWorkflow = (defaultprops) => { const AngularWorkflow = (defaultprops) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, data_id } = defaultprops; const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id } = defaultprops;
const referenceUrl = globalUrl + "/api/v1/hooks/"; const referenceUrl = globalUrl + "/api/v1/hooks/";
//const alert = useAlert() //const alert = useAlert()
let navigate = useNavigate(); let navigate = useNavigate();
@@ -540,6 +540,7 @@ const AngularWorkflow = (defaultprops) => {
}) })
// New for generated stuff // New for generated stuff
const releaseToConnectLabel = "Release to Connect"
const integrationApps = [{ const integrationApps = [{
"id": "integration", "id": "integration",
"name": "Integration Framework", "name": "Integration Framework",
@@ -1357,20 +1358,20 @@ const AngularWorkflow = (defaultprops) => {
return; return;
} }
trigger.parameters = []; trigger.parameters = []
const topic = document.getElementById('topic')?.value; const topic = document.getElementById('topic')?.value
const bootstrapServers = document.getElementById('bootstrap_servers')?.value; const bootstrapServers = document.getElementById('bootstrap_servers')?.value
const groupId = document.getElementById('group_id')?.value; const groupId = document.getElementById('group_id')?.value
//const autoOffsetReset = document.getElementById('auto_offset_reset')?.value; //const autoOffsetReset = document.getElementById('auto_offset_reset')?.value;
if(topic) { if(topic) {
trigger.parameters.push({ trigger.parameters.push({
name: "topic", name: "topic",
value: topic value: topic
}); })
} else { } else {
toast("please enter the topic name"); toast("Please enter the topic name");
return; return;
} }
@@ -3102,12 +3103,19 @@ const AngularWorkflow = (defaultprops) => {
// don't redirect if it exists // don't redirect if it exists
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
var execFound = new URLSearchParams(cursearch).get("execution_id"); var execFound = new URLSearchParams(cursearch).get("execution_id");
if (execFound === null) { var sessionToken = new URLSearchParams(cursearch).get("session_token");
toast(`You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..`) if (execFound === null && sessionToken === null) {
setTimeout(() => { toast(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds..`)
window.location.pathname = "/workflows"; setTimeout(() => {
}, 2000); window.location.pathname = "/workflows";
} }, 2000);
} else if (sessionToken !== null && workflow_id === "3abdfb21-b40f-4e50-b855-ac0d62f83cbe") {
toast(`Injecting session token and reloading workflow..`)
setTimeout(() => {
setCookie("session_token", sessionToken, { path: "/" });
window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe";
}, 2000);
}
} }
} }
@@ -3573,20 +3581,28 @@ const AngularWorkflow = (defaultprops) => {
const onNodeDragStop = (event, selectedAction) => { const onNodeDragStop = (event, selectedAction) => {
const nodedata = event.target.data(); const nodedata = event.target.data();
if (nodedata.id === selectedAction.id) { if (nodedata.id === selectedAction.id) {
return; //console.log("Same node, return")
return
} }
if (nodedata.finished === false) { if (nodedata.finished === false) {
return; //console.log("Node is not finished, return")
return
} }
const connected = event.target.connectedEdges().jsons() const connected = event.target.connectedEdges().jsons()
if (connected.length > 0 && connected !== undefined) { if (connected.length > 0 && connected !== undefined) {
for (let connectkey in connected) { for (let connectkey in connected) {
const edge = connected[connectkey] const edge = connected[connectkey]
//console.log("EDGE:", edge) if (edge.data.decorator && edge.data.label === releaseToConnectLabel) {
// Transform to normal edge
//const edge = edgeBase.json() const currentedge = cy.getElementById(edge.data.id)
if (currentedge !== undefined && currentedge !== null) {
currentedge.data("decorator", false)
currentedge.data("label", "")
}
continue
}
const sourcenode = cy.getElementById(edge.data.source) const sourcenode = cy.getElementById(edge.data.source)
const destinationnode = cy.getElementById(edge.data.target) const destinationnode = cy.getElementById(edge.data.target)
@@ -3804,26 +3820,141 @@ const AngularWorkflow = (defaultprops) => {
} }
if (nodedata.id === selectedAction.id) { if (nodedata.id === selectedAction.id) {
return; return
} }
if ((nodedata.trigger_type === "SUBFLOW" || nodedata.trigger_type === "USERINPUT" || nodedata.type === "ACTION") && !nodedata.isStartNode) {
// Check if it already has any non-decorator branches attached to it
const branches = cy.elements('edge').jsons()
var branchFound = false
var decoratorIds = []
for (var branchkey in branches) {
if (branches[branchkey].data.source === nodedata.id || branches[branchkey].data.target === nodedata.id) {
/* if (branches[branchkey].data.decorator === true) {
// Tried looking for the closest node by position. aStar path not working entirely.
console.log("NODE: ", event.target)
const closestNode = cy.elements().aStar({
root: nodedata.id,
goal: 'node',
directed: false,
})
if (closestNode.found) { // Add the source/destination
console.log("No closest node found for: ", nodedata.id) if (branches[branchkey].data.source === nodedata.id) {
} else { decoratorIds.push(branches[branchkey].data.target)
console.log("Closest: ", closestNode) } else {
} decoratorIds.push(branches[branchkey].data.source)
*/ }
continue
}
branchFound = true
break
}
}
if (!branchFound) {
//console.log("Found action during drag. Checking closest nodes as it doesn't have a valid branch")
var closestNode = null
var minDistance = 300
const draggedNode = event.target
const allnodes = cy.nodes().jsons()
for (var nodekey in allnodes) {
const node = allnodes[nodekey]
if (node.data.id === nodedata.id) {
continue
}
// Decorators
if (node.data.attachedTo !== undefined) {
continue
}
if (node.position === undefined || node.position === null || node.position.x === undefined || node.position.y === undefined) {
continue
}
if (node.data.type !== "ACTION" && node.data.type !== "TRIGGER") {
continue
}
const distance = Math.sqrt(
Math.pow(draggedNode.position('x') - node.position.x, 2) +
Math.pow(draggedNode.position('y') - node.position.y, 2)
)
if (decoratorIds.includes(node.data.id)) {
//console.log("Found existing decorator for: ", node.data.app_name, "Distance: ", distance)
if (distance > 300) {
// Remove the branch
const edgeToRemove = cy.getElementById(branches[branchkey].data.id)
if (edgeToRemove !== null && edgeToRemove !== undefined) {
//console.log("Removing edge: ", edgeToRemove)
edgeToRemove.remove()
//decoratorIds.splice(decoratorIds.indexOf(node.data.id), 1)
break
}
}
}
if (distance < minDistance) {
minDistance = distance
closestNode = node
}
}
if (closestNode !== null && closestNode !== undefined) {
//console.log("Closest node app: ", closestNode.data.app_name, "Distance: ", minDistance)
/*
if (decoratorIds.length > 0) {
console.log("Decorators already exists. If within distance of 15 add to existing, otherwise remove old and add new: ", decoratorIds)
for (var decoratorkey in decoratorIds) {
const decoratorEdge = cy.getElementById(decoratorIds[decoratorkey])
if (decoratorEdge === null || decoratorEdge === undefined) {
continue
}
const sourceNode = cy.getElementById(decoratorEdge.data.source)
const targetNode = cy.getElementById(decoratorEdge.data.target)
const distance = Math.sqrt(
Math.pow(draggedNode.position('x') - sourceNode.position('x'), 2) +
Math.pow(draggedNode.position('y') - sourceNode.position('y'), 2)
)
// Check plus minus 15 in distance from mindistance
if (distance > minDistance - 15 && distance < minDistance + 15) {
console.log("Within distance of 15, add to existing edge")
} else {
console.log("Outside distance of 15, remove old edge and add new")
}
}
}
*/
if (decoratorIds.length === 0) {
//const edgeCurve = calculateEdgeCurve(draggedNode.position(), closestNode.position)
//currentedge.style('control-point-distance', edgeCurve.distance)
//currentedge.style('control-point-weight', edgeCurve.weight)
const newId = uuidv4()
cy.add({
group: "edges",
data: {
decorator: true,
id: newId,
_id: newId,
source: closestNode.data.id,
target: nodedata.id,
label: releaseToConnectLabel,
conditions: [],
}
})
}
}
}
}
if ( if (
originalLocation.x === 0 && originalLocation.x === 0 &&
@@ -3882,11 +4013,11 @@ const AngularWorkflow = (defaultprops) => {
} }
// Ensure it only happens once // Ensure it only happens once
document.removeEventListener("mousemove", onMouseUpdate, false); document.removeEventListener("mousemove", onMouseUpdate, false)
}; }
document.addEventListener("mousemove", onMouseUpdate, false); document.addEventListener("mousemove", onMouseUpdate, false)
}; }
useBeforeunload(() => { useBeforeunload(() => {
@@ -3899,7 +4030,7 @@ const AngularWorkflow = (defaultprops) => {
document.removeEventListener("paste", handlePaste, true); document.removeEventListener("paste", handlePaste, true);
} }
} }
}); })
// Nodeselectbatching: // Nodeselectbatching:
// https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once // https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once
@@ -5012,11 +5143,10 @@ const AngularWorkflow = (defaultprops) => {
// Checks for errors in edges when they're added // Checks for errors in edges when they're added
const onEdgeAdded = (event) => { const onEdgeAdded = (event) => {
setLastSaved(false); const edge = event.target.data()
const edge = event.target.data(); //console.log("EDGE ADDED!: ", edge)
//console.log("edge added: ", edge)
if (edge.source === undefined && edge.target === undefined) { if (edge.source === undefined && edge.target === undefined) {
console.log("Edge source and target is undefined")
return return
} }
@@ -5030,6 +5160,7 @@ const AngularWorkflow = (defaultprops) => {
const sourcenode = cy.getElementById(edge.source) const sourcenode = cy.getElementById(edge.source)
const destinationnode = cy.getElementById(edge.target) const destinationnode = cy.getElementById(edge.target)
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) { if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
console.log("Source or destination node is undefined")
} else { } else {
//console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data()) //console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
if (sourcenode.data("type") === "TRIGGER") { if (sourcenode.data("type") === "TRIGGER") {
@@ -5042,10 +5173,10 @@ const AngularWorkflow = (defaultprops) => {
console.log("Node: ", targetedge) console.log("Node: ", targetedge)
if (targetedge !== -1) { if (targetedge !== -1) {
event.target.remove()
//console.log("Found branch already!") //console.log("Found branch already!")
toast.error("Triggers can have exactly one target node") toast.error("Triggers can have exactly one target node")
event.target.remove()
return return
@@ -5066,6 +5197,10 @@ const AngularWorkflow = (defaultprops) => {
} }
} }
if (edge.decorator === true) {
console.log("Doing nothing to branch because decorator")
return
}
var targetnode = workflow.triggers.findIndex( var targetnode = workflow.triggers.findIndex(
(data) => data.id === edge.target (data) => data.id === edge.target
@@ -5105,15 +5240,14 @@ const AngularWorkflow = (defaultprops) => {
} }
} }
if ( if (eventTarget.data("isDescriptor") === true || eventTarget.data("type") === "COMMENT") {
eventTarget.data("isDescriptor") === true ||
eventTarget.data("type") === "COMMENT"
) {
console.log("Removing because of descriptor or comment") console.log("Removing because of descriptor or comment")
event.target.remove(); event.target.remove()
return; return
} }
setLastSaved(false)
targetnode = -1; targetnode = -1;
// Check if: // Check if:
@@ -5121,38 +5255,51 @@ const AngularWorkflow = (defaultprops) => {
// dest == dest && source == source // dest == dest && source == source
// backend: check all children? to stop recursion // backend: check all children? to stop recursion
var found = false; var found = false;
for (let branchkey in workflow.branches) { const branches = cy.edges().jsons()
if (
workflow.branches[branchkey].destination_id === edge.source && const startNode = cy.nodes().jsons().find((node) => node.data.isStartNode === true)
workflow.branches[branchkey].source_id === edge.target var startnodeId = workflow.start
) { if (startNode !== undefined && startNode !== null) {
toast("A branch in the opposite direction already exists"); startnodeId = startNode.data.id
event.target.remove(); }
found = true;
break; //for (let branchkey in workflow.branches) {
} else if ( for (let branchkey in branches) {
workflow.branches[branchkey].destination_id === edge.target && const branch = branches[branchkey].data
workflow.branches[branchkey].source_id === edge.source
) { //if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
//toast("That branch already exists"); if (branch.target === edge.source && branch.source === edge.target) {
event.target.remove(); toast("A branch in the opposite direction already exists")
event.target.remove()
found = true
break
//} else if (workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source) {
} else if (branch.target === edge.target && branch.source === edge.source) {
if (branch.conditions === undefined) {
// Edgehandles
} else {
console.log("Removing because the same branch already exists")
event.target.remove()
found = true
break
}
} else if (edge.target === startnodeId) {
targetnode = workflow.triggers.findIndex((data) => data.id === edge.source)
found = true;
break;
} else if (edge.target === workflow.start) {
targetnode = workflow.triggers.findIndex(
(data) => data.id === edge.source
);
if (targetnode === -1) { if (targetnode === -1) {
if (targetnode.type !== "TRIGGER") { if (targetnode.type !== "TRIGGER") {
toast("Can't make arrow to starting node"); toast("Can't make arrow to starting node")
event.target.remove(); event.target.remove()
break; break
} }
found = true; found = true;
} }
} else if (edge.source === workflow.branches[branchkey].source_id) { //} else if (edge.source === workflow.branches[branchkey].source_id) {
} else if (edge.source === branch.source) {
// FIXME: Verify multi-target for triggers // FIXME: Verify multi-target for triggers
// 1. Check if destination exists // 1. Check if destination exists
// 2. Check if source is a trigger // 2. Check if source is a trigger
@@ -5196,7 +5343,6 @@ const AngularWorkflow = (defaultprops) => {
newdst !== null newdst !== null
) { ) {
const dstdata = RunAutocompleter(newdst.data()); const dstdata = RunAutocompleter(newdst.data());
//console.log("DST Autocompleter: ", dstdata);
} }
var newbranch = { var newbranch = {
@@ -8036,7 +8182,6 @@ const AngularWorkflow = (defaultprops) => {
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
width: imagesize, width: imagesize,
height: imagesize, height: imagesize,
pointerEvents: "none",
// Stretch if necessary // Stretch if necessary
objectFit: "cover", objectFit: "cover",
// Center the object // Center the object
@@ -10715,11 +10860,101 @@ const AngularWorkflow = (defaultprops) => {
</Button> </Button>
{/* Check if dest is the same as start */} {/* Check if dest is the same as start */}
{conditionsDisabled ? {conditionsDisabled ?
<Typography variant="body1"> <Typography variant="body1">
Conditions are unavailable between triggers and the startnode. Conditions are unavailable between triggers and the startnode.
</Typography> </Typography>
: null} : null}
<div style={{position: "absolute", bottom: 10, width: "90%", margin: "auto", }}>
{/*
<Button
style={{ margin: "auto", marginTop: "10px" }}
color="secondary"
fullWidth
variant="outlined"
onClick={() => {
// Change Direction of the branch target/source
const foundBranch = cy.getElementById(selectedEdge.id)
if (foundBranch !== undefined && foundBranch !== null) {
console.log("BRANCH: ", foundBranch)
const source = foundBranch.data("source")
const target = foundBranch.data("target")
var branchdata = JSON.parse(JSON.stringify(foundBranch.data()))
console.log("BEFORE: ", branchdata)
const newid = uuidv4()
branchdata.source = target
branchdata.target = source
branchdata.id = newid
branchdata._id = newid
foundBranch.remove()
setTimeout(() => {
toast("Edge being added!")
cy.add({
group: "edges",
source: target,
target: source,
data: branchdata,
})
}, 2500)
}
}}
fullWidth
>
<DeleteIcon style={{marginRight: 10, }}/>
Change Direction
</Button>
<Button
style={{ margin: "auto", }}
color="secondary"
fullWidth
variant="outlined"
onClick={() => {
// Delete the branch
}}
fullWidth
>
Re-attach branch
</Button>
<Button
style={{ margin: "auto", }}
color="secondary"
fullWidth
variant="outlined"
onClick={() => {
// Delete the branch
}}
fullWidth
>
Disable Path
</Button>
*/}
<Button
style={{ margin: "auto", marginTop: 50, }}
color="secondary"
fullWidth
variant="outlined"
onClick={() => {
// Delete the branch
const foundBranch = cy.getElementById(selectedEdge.id)
if (foundBranch !== undefined && foundBranch !== null) {
foundBranch.remove()
}
setConditionsModalOpen(false)
setSelectedEdge({})
}}
fullWidth
>
<DeleteIcon style={{marginRight: 10, }}/>
Delete Branch
</Button>
</div>
</div> </div>
); );
}; };
@@ -11483,7 +11718,7 @@ const AngularWorkflow = (defaultprops) => {
return transformedData; return transformedData;
}; }
const AppAuthSelector = ({ appAuthData }) => { const AppAuthSelector = ({ appAuthData }) => {
const [selectedAuth, setSelectedAuth] = useState(""); const [selectedAuth, setSelectedAuth] = useState("");
@@ -11496,6 +11731,7 @@ const AngularWorkflow = (defaultprops) => {
const handleShowingValue = (appName) => { const handleShowingValue = (appName) => {
let mappingWithName = {} let mappingWithName = {}
let listWithValues = workflow.triggers[selectedTriggerIndex].parameters[5]?.value.split(";").filter(e => e).map(e => e.split("=")) let listWithValues = workflow.triggers[selectedTriggerIndex].parameters[5]?.value.split(";").filter(e => e).map(e => e.split("="))
console.log("LIST WITH VALUES: ", listWithValues) console.log("LIST WITH VALUES: ", listWithValues)
for (let i = 0; i < listWithValues.length; i++) { for (let i = 0; i < listWithValues.length; i++) {
mappingWithName[listWithValues[i][0]] = listWithValues[i][1] mappingWithName[listWithValues[i][0]] = listWithValues[i][1]
@@ -12180,7 +12416,7 @@ const AngularWorkflow = (defaultprops) => {
data: newbranch, data: newbranch,
}; };
cy.add(cybranch); cy.add(cybranch)
} }
console.log("Value to be set: ", e.target.value); console.log("Value to be set: ", e.target.value);
@@ -12722,25 +12958,27 @@ const AngularWorkflow = (defaultprops) => {
</div> </div>
</div> </div>
{/*
<div> <div>
<div> <div>
<div className="app"> <div className="app">
<div style={{ display: "flex", marginTop: 10 }}> <div style={{ display: "flex", marginTop: 10 }}>
<div style={{ flex: "10" }}> <div style={{ flex: "10" }}>
<b>Auth Override</b> <b>Auth Override</b>
</div> </div>
</div> </div>
<div style={{ display: "flex", marginTop: 10 }}> <div style={{ display: "flex", marginTop: 10 }}>
<div style={{ flex: "10", marginLeft: 10 }}> <div style={{ flex: "10", marginLeft: 10 }}>
<AppAuthSelector appAuthData={appAuthentication} /> <AppAuthSelector appAuthData={appAuthentication} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
*/}
</div> </div>
); )
} }
return null; return null;
@@ -14697,12 +14935,8 @@ const AngularWorkflow = (defaultprops) => {
right: 0, right: 0,
left: isMobile ? 20 : leftBarSize + 20, left: isMobile ? 20 : leftBarSize + 20,
top: isMobile ? 30 : appBarSize + 20, top: isMobile ? 30 : appBarSize + 20,
pointerEvents: "none",
} }
const TopCytoscapeBar = (props) => { const TopCytoscapeBar = (props) => {
if (workflow.public === true) { if (workflow.public === true) {
return null return null
@@ -14718,7 +14952,7 @@ const AngularWorkflow = (defaultprops) => {
<div style={topBarStyle}> <div style={topBarStyle}>
<div style={{ <div style={{
margin: "0px 10px 0px 10px", margin: "0px 10px 0px 10px",
pointerEvents: "none", pointerevents: "none",
}}> }}>
<Breadcrumbs <Breadcrumbs
aria-label="breadcrumb" aria-label="breadcrumb"
@@ -14743,7 +14977,6 @@ const AngularWorkflow = (defaultprops) => {
</Link> </Link>
<h2 style={{ <h2 style={{
margin: 0, margin: 0,
pointerEvents: "none",
}}>{workflow.name}</h2> }}>{workflow.name}</h2>
</Breadcrumbs> </Breadcrumbs>
@@ -15244,6 +15477,8 @@ const AngularWorkflow = (defaultprops) => {
} }
} }
/*
// Infinitely annoying. Need a new bind
if (( event.ctrlKey || event.metaKey ) && event.shiftKey) { if (( event.ctrlKey || event.metaKey ) && event.shiftKey) {
console.log("Shift key pressed") console.log("Shift key pressed")
if (!workflow.public && executionModalOpen) { if (!workflow.public && executionModalOpen) {
@@ -15255,7 +15490,8 @@ const AngularWorkflow = (defaultprops) => {
setExecutionModalView(0); setExecutionModalView(0);
} }
} }
}; */
}
document.addEventListener('keydown', handleKeyDown); document.addEventListener('keydown', handleKeyDown);
@@ -17033,7 +17269,7 @@ const AngularWorkflow = (defaultprops) => {
</div> </div>
{foundnotifications > 0 ? {foundnotifications > 0 ?
<Tooltip title={"This workflow created " + foundnotifications + " notification(s)"} placement="top"> <Tooltip title={"This workflow created " + foundnotifications + " notification(s). Click to explore them."} placement="top">
<ErrorOutlineIcon <ErrorOutlineIcon
style={{color: "rgba(255,255,255,0.4)", marginTop: 10, marginRight: 10, }} style={{color: "rgba(255,255,255,0.4)", marginTop: 10, marginRight: 10, }}
onClick={(e) => { onClick={(e) => {
@@ -17968,10 +18204,11 @@ const AngularWorkflow = (defaultprops) => {
return "The queries or data sent to the API is most likely wrong (400). Check the body of the result for more information." return "The queries or data sent to the API is most likely wrong (400). Check the body of the result for more information."
} }
/*
if (result.status === 200 || result.status === 201 || result.status === 204) { if (result.status === 200 || result.status === 201 || result.status === 204) {
return "It looks like the result was successful! If it didn't work, make sure to check if the body you are sending was correct." return "It looks like the result was successful! If it didn't work, make sure to check if the body you are sending was correct."
} }
*/
// Validate and check for newlines // Validate and check for newlines
if (result.success !== false) { if (result.success !== false) {
@@ -19477,8 +19714,9 @@ const AngularWorkflow = (defaultprops) => {
<div style={{ color: "white" }}>Configuration options for {selectedOption}</div> <div style={{ color: "white" }}>Configuration options for {selectedOption}</div>
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
{selectedOption === "Kafka Queue" && (
<> {selectedOption === "Kafka Queue" ?
<div>
<b>Topic</b> <b>Topic</b>
<TextField <TextField
id="topic" id="topic"
@@ -19539,8 +19777,24 @@ const AngularWorkflow = (defaultprops) => {
placeholder={"earliest"} placeholder={"earliest"}
defaultValue={(selectedTrigger?.parameters?.find(param => param.name === "auto_offset_reset")?.value) || ''} defaultValue={(selectedTrigger?.parameters?.find(param => param.name === "auto_offset_reset")?.value) || ''}
/> */} /> */}
</> </div>
)} : null}
<TextField
id="bootstrap_servers"
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
}}
InputProps={{
style: {},
}}
fullWidth
color="primary"
placeholder={"broker1.example.com:9092,192.168.1.100:9092"}
defaultValue={(selectedTrigger?.parameters?.find(param => param.name === "bootstrap_servers")?.value) || ''}
/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
@@ -20005,49 +20259,53 @@ const AngularWorkflow = (defaultprops) => {
//cy.remove('*') //cy.remove('*')
setElements([]) setElements([])
// Remove all edges
cy.edges().remove()
cy.nodes().remove()
//return
} }
// Remove all cy nodes // Remove all cy nodes
setTimeout(() => { setTimeout(() => {
setupGraph(newrevision) setupGraph(newrevision)
}, 100)
// Re-adding cytoscape triggers
if (cy !== undefined && cy !== null) {
cy.on("select", "node", (e) => {
onNodeSelect(e, appAuthentication);
});
cy.on("select", "edge", (e) => onEdgeSelect(e));
cy.on("unselect", (e) => onUnselect(e));
cy.on("add", "node", (e) => onNodeAdded(e));
cy.on("add", "edge", (e) => onEdgeAdded(e));
cy.on("remove", "node", (e) => onNodeRemoved(e));
cy.on("remove", "edge", (e) => onEdgeRemoved(e));
cy.on("mouseover", "edge", (e) => onEdgeHover(e));
cy.on("mouseout", "edge", (e) => onEdgeHoverOut(e));
cy.on("mouseover", "node", (e) => onNodeHover(e));
cy.on("mouseout", "node", (e) => onNodeHoverOut(e));
// Handles dragging
cy.on("drag", "node", (e) => onNodeDrag(e, selectedAction));
cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction));
cy.on("cxttap", "node", (e) => onCtxTap(e));
// Re-adding cytoscape triggers if (selectedAction.id !== undefined && selectedAction.id !== null && selectedAction.id !== "") {
if (cy !== undefined && cy !== null) { setTimeout(() => {
cy.on("select", "node", (e) => { const foundaction = cy.$id(selectedAction.id)
onNodeSelect(e, appAuthentication); if (foundaction !== undefined && foundaction !== null) {
}); foundaction.select()
cy.on("select", "edge", (e) => onEdgeSelect(e)); }
}, 250)
cy.on("unselect", (e) => onUnselect(e)); }
cy.on("add", "node", (e) => onNodeAdded(e));
cy.on("add", "edge", (e) => onEdgeAdded(e));
cy.on("remove", "node", (e) => onNodeRemoved(e));
cy.on("remove", "edge", (e) => onEdgeRemoved(e));
cy.on("mouseover", "edge", (e) => onEdgeHover(e));
cy.on("mouseout", "edge", (e) => onEdgeHoverOut(e));
cy.on("mouseover", "node", (e) => onNodeHover(e));
cy.on("mouseout", "node", (e) => onNodeHoverOut(e));
// Handles dragging
cy.on("drag", "node", (e) => onNodeDrag(e, selectedAction));
cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction));
cy.on("cxttap", "node", (e) => onCtxTap(e));
if (selectedAction.id !== undefined && selectedAction.id !== null && selectedAction.id !== "") {
setTimeout(() => {
const foundaction = cy.$id(selectedAction.id)
if (foundaction !== undefined && foundaction !== null) {
foundaction.select()
}
}, 250)
} }
} }, 100)
+35 -5
View File
@@ -102,6 +102,35 @@ export const CopyToClipboard = (props) => {
) )
} }
export const Paragrah = (props) => {
const element = React.createElement(
`p`,
{},
props.children,
)
if (props.children[0] != undefined) {
if(typeof props.children[0] === "string") {
if (props.children[0].includes('.mp4')) {
return (
<div>
<video width="640" height="480" controls>
<source src={`${props.children[0]}`} type="video/mp4" />
</video>
</div>
)
}
}
}
return (
<div class="sdf">
{element}
</div>
)
}
export const OuterLink = (props) => { export const OuterLink = (props) => {
if (props.href.includes("http") || props.href.includes("mailto")) { if (props.href.includes("http") || props.href.includes("mailto")) {
return ( return (
@@ -581,8 +610,8 @@ const Docs = (defaultprops) => {
position: "sticky", position: "sticky",
top: 50, top: 50,
paddingTop: "0.25em", paddingTop: "0.25em",
minHeight: "93vh", minHeight: "95vh",
maxHeight: "93vh", maxHeight: "95vh",
overflowX: "hidden", overflowX: "hidden",
overflowY: "auto", overflowY: "auto",
zIndex: 1000, zIndex: 1000,
@@ -901,7 +930,7 @@ const Docs = (defaultprops) => {
<div style={{ textAlign: "left" }}> <div style={{ textAlign: "left" }}>
<Typography variant="h6" style={headerStyle} >Tutorial</Typography> <Typography variant="h6" style={headerStyle} >Tutorial</Typography>
<Typography variant="body1"> <Typography variant="body1">
<b>Dive in.</b> Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the <Link to="/docs/getting-started" style={hrefStyle2}>getting started</Link> section to give it a go! <b>Dive in.</b> Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the <Link to="/docs/getting_started" style={hrefStyle2}>getting started</Link> section to give it a go!
</Typography> </Typography>
<Typography variant="h6" style={headerStyle}>Why Shuffle?</Typography> <Typography variant="h6" style={headerStyle}>Why Shuffle?</Typography>
@@ -940,7 +969,8 @@ const Docs = (defaultprops) => {
h4: Heading, h4: Heading,
h5: Heading, h5: Heading,
h6: Heading, h6: Heading,
a: OuterLink, a: OuterLink,
p: Paragrah,
} }
@@ -1188,7 +1218,7 @@ const Docs = (defaultprops) => {
// Padding and zIndex etc set because of footer in cloud. // Padding and zIndex etc set because of footer in cloud.
const loadedCheck = ( const loadedCheck = (
<div style={{ minHeight: 1000, paddingTop: "60px", zIndex: 50000, maxWidth: 1920, minWidth: isMobile ? null : 1366, margin: "auto", }}> <div style={{ minHeight: 1000, zIndex: 50000, maxWidth: 1920, minWidth: isMobile ? null : 1366, margin: "auto", }}>
<BrowserView>{postDataBrowser}</BrowserView> <BrowserView>{postDataBrowser}</BrowserView>
<MobileView>{postDataMobile}</MobileView> <MobileView>{postDataMobile}</MobileView>
</div> </div>
+93 -31
View File
@@ -10,6 +10,8 @@ import theme from '../theme.jsx';
//import { useAlert //import { useAlert
import { ToastContainer, toast } from "react-toastify" import { ToastContainer, toast } from "react-toastify"
//import "./CollapsibleList.css"
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import AuthenticationWindow from "../components/AuthenticationWindow.jsx"; import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
import { base64_decode, appCategories } from "../views/AppCreator.jsx"; import { base64_decode, appCategories } from "../views/AppCreator.jsx";
@@ -47,8 +49,6 @@ const SetAuthentication = (props) => {
} }
console.log("App: ", app)
const getApp = (appid) => { const getApp = (appid) => {
if (serverside === true) { if (serverside === true) {
return; return;
@@ -100,7 +100,16 @@ const SetAuthentication = (props) => {
useEffect(() => { useEffect(() => {
// Find the ID for the app from the "app_id" query // Find the ID for the app from the "app_id" query
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const appid = urlParams.get("app_id"); const appid = urlParams.get("app_id")
const orgsession = urlParams.get("auth")
if (!serverside && orgsession !== null) {
// Set the orgsession to be a cookie for __session cookie
setTimeout(() => {
document.cookie = "__session=" + orgsession + "; path=/; max-age=1800"; // Cookie expires in 30 min
}, 1000)
}
if (appid === null) { if (appid === null) {
setLoadFail( setLoadFail(
<span> <span>
@@ -121,6 +130,12 @@ const SetAuthentication = (props) => {
} }
}, []); }, []);
const [expandedIndex, setExpandedIndex] = useState(null);
const handleToggle = (index) => {
setExpandedIndex(expandedIndex === index ? null : index);
};
// Handle: // Handle:
// 1. Check for org_id, authentication, and app keys in queries // 1. Check for org_id, authentication, and app keys in queries
// 2. Load the app auth info from the orgs' apps // 2. Load the app auth info from the orgs' apps
@@ -128,43 +143,90 @@ const SetAuthentication = (props) => {
// Make sure to test both private and public apps // Make sure to test both private and public apps
const appname = app.name !== undefined ? app.name : ""; const appname = app.name !== undefined ? app.name : "";
const appLink = "/apps/" + app.id || "";
console.log("App: ", app)
return ( return (
<div style={{width: 1000, margin: "auto", marginTop: 50, }}> <div style={{width: 1000, margin: "auto", marginTop: 50, }}>
{loadFail !== "" ? {loadFail !== "" ?
loadFail loadFail
: :
<div> <><div>
<Typography variant="h4" style={{marginBottom: 20,}}> <Typography variant="h4" style={{ marginBottom: 20, }}>
Configure {appname} Authentication A Shuffle Organization has invited you to: Configure <a href={appLink} target="_blank" style={{ color: '#FF8444', textDecoration: 'none' }}>{appname}</a> Authentication
</Typography> </Typography>
{app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ?
null {/* What does this mean box */}
: <Typography variant="h6" style={{ marginBottom: 20, }}>
app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ? What does this mean?
<AuthenticationOauth2 </Typography>
selectedApp={app} <Typography variant="body1" style={{ marginBottom: 20, }}>
selectedAction={{ A Shuffle Organization has invited you to configure authentication for this app so that they can use this authentication in one of their workflows.
"app_name": app.name, </Typography>
"app_id": app.id,
"app_version": app.version, <Typography variant="h6">
"large_image": app.large_image, Authenticate Here:
}} </Typography>
authenticationType={app.authentication}
isCloud={true} <Typography variant="body1" style={{ marginBottom: 20, }}>
authButtonOnly={true} {app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ?
getAppAuthentication={undefined} null
/>
: :
<AuthenticationWindow app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ?
globalUrl={globalUrl} <AuthenticationOauth2
selectedApp={app} selectedApp={app}
authFieldsOnly={true} selectedAction={{
getAppAuthentication={undefined} "app_name": app.name,
appAuthentication={appAuthentication} "app_id": app.id,
/> "app_version": app.version,
} "large_image": app.large_image,
}}
authenticationType={app.authentication}
isCloud={true}
authButtonOnly={true}
getAppAuthentication={undefined} />
:
<AuthenticationWindow
globalUrl={globalUrl}
selectedApp={app}
authFieldsOnly={true}
getAppAuthentication={undefined}
appAuthentication={appAuthentication} />}
</Typography>
<Typography variant="h6" style={{ marginBottom: 20, }}>
What can they do with this?
</Typography>
<Typography variant="body1" style={{ marginBottom: 20, }}>
You can check the actions they want to use <a href={appLink} target="_blank" style={{ color: '#FF8444', textDecoration: 'none' }}>here</a>.
</Typography>
<Typography variant="body1" style={{ marginBottom: 20, }}>
{/* Add a box below */}
<div className="collapsible-container">
<div className="collapsible-list">
{app.actions?.map((item, index) => (
<div key={index} className="collapsible-item">
<div className="collapsible-label" onClick={() => handleToggle(index)}>
{item.label}
</div>
{expandedIndex === index && (
<div className="collapsible-description">
{item.description}
</div>
)}
</div>
))}
</div>
</div>
</Typography>
</div> </div>
<>
<div style={{ height: 100, }}></div>
</>
</>
} }
</div> </div>
) )
+1 -1
View File
@@ -4080,7 +4080,7 @@ const Workflows = (props) => {
{publishModal} {publishModal}
{workflowDownloadModalOpen} {workflowDownloadModalOpen}
{!drawerOpen ? <div style={{position: "fixed", top: 64, right: -5, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, }}> {!drawerOpen ? <div style={{ position: "fixed", top: 64, right: -5, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, }}>
<Tooltip title={`Getting started`} placement="bottom"> <Tooltip title={`Getting started`} placement="bottom">
<IconButton onClick={() => { <IconButton onClick={() => {
setDrawerOpen(true) setDrawerOpen(true)
+4
View File
@@ -693,6 +693,10 @@ func deployWorker(image string, identifier string, env []string, executionReques
env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String())) env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String()))
// FIXME: When a service account is used, the account is also mounted in the pod
// The volume mount location is:
// /var/run/secrets/kubernetes.io/serviceaccount
// Look for if there is a default service account in use // Look for if there is a default service account in use
if len(os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) > 0 { if len(os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) > 0 {
log.Printf("[DEBUG] Using Kubernetes service account %s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) log.Printf("[DEBUG] Using Kubernetes service account %s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT"))
+2 -1
View File
@@ -2456,13 +2456,14 @@ func downloadDockerImageBackend(client *http.Client, imageName string) error {
return nil return nil
} }
log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages)
downloadedImages = append(downloadedImages, imageName) downloadedImages = append(downloadedImages, imageName)
data := fmt.Sprintf(`{"name": "%s"}`, imageName) data := fmt.Sprintf(`{"name": "%s"}`, imageName)
dockerImgUrl := fmt.Sprintf("%s/api/v1/get_docker_image", baseUrl) dockerImgUrl := fmt.Sprintf("%s/api/v1/get_docker_image", baseUrl)
log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. Data sent: %#v, All images: %#v", imageName, baseUrl, data, downloadedImages)
req, err := http.NewRequest( req, err := http.NewRequest(
"POST", "POST",
dockerImgUrl, dockerImgUrl,