Merge branch '2.0.0' into main
This commit is contained in:
@@ -3,7 +3,7 @@ name: dockerbuild
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 2.0.0
|
||||
paths:
|
||||
- "**"
|
||||
- "!.github/**"
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
- "!docker-compose.yml"
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-nightly
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -19,23 +19,23 @@ jobs:
|
||||
include:
|
||||
- app: frontend
|
||||
path: frontend
|
||||
version: 1.4.0
|
||||
version: nightly
|
||||
experimental: true
|
||||
- app: backend
|
||||
path: backend
|
||||
version: 1.4.0
|
||||
version: nightly
|
||||
experimental: true
|
||||
- app: app_sdk
|
||||
path: backend/app_sdk
|
||||
version: 1.4.0
|
||||
version: nightly
|
||||
experimental: true
|
||||
- app: orborus
|
||||
path: functions/onprem/orborus
|
||||
version: 1.4.0
|
||||
version: nightly
|
||||
experimental: true
|
||||
- app: worker
|
||||
path: functions/onprem/worker
|
||||
version: 1.4.0
|
||||
version: nightly
|
||||
experimental: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -77,11 +77,11 @@ jobs:
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
tags: |
|
||||
ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }}
|
||||
ghcr.io/shuffle/shuffle-${{ matrix.app }}:latest
|
||||
ghcr.io/shuffle/shuffle-${{ matrix.app }}:nightly
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:latest
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:nightly
|
||||
frikky/shuffle-${{ matrix.app }}:${{ matrix.version }}
|
||||
frikky/shuffle-${{ matrix.app }}:latest
|
||||
frikky/shuffle-${{ matrix.app }}:nightly
|
||||
frikky/shuffle:${{ matrix.app }}
|
||||
|
||||
- name: Image digest
|
||||
|
||||
+86
-58
@@ -928,7 +928,7 @@ class AppBase:
|
||||
#self.action = action
|
||||
|
||||
loopnames = []
|
||||
#self.logger.info(f"Baseparams to check!!: {baseparams}")
|
||||
self.logger.info(f"Baseparams to check: {baseparams}")
|
||||
for key, value in baseparams.items():
|
||||
check_value = ""
|
||||
for param in self.original_action["parameters"]:
|
||||
@@ -991,20 +991,33 @@ class AppBase:
|
||||
except:
|
||||
pass
|
||||
|
||||
#self.logger.info(f"MERGE: {should_merge}")
|
||||
#self.logger.info(f"VALUE LENGTH: {len(value)}")
|
||||
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:
|
||||
# FIXME: This broke some shit for a single item fml
|
||||
# Necessary as override again :(
|
||||
if len(value) == 1:
|
||||
baseparams[key] = value[0]
|
||||
|
||||
#if "#" in check_value:
|
||||
# should_merge = True
|
||||
else:
|
||||
#if len(value) > 1:
|
||||
if not should_merge:
|
||||
self.logger.info("[DEBUG] Adding WITHOUT looping list")
|
||||
else:
|
||||
if len(value) not in listlengths:
|
||||
listlengths.append(len(value))
|
||||
#listlength
|
||||
|
||||
listitems.append(
|
||||
{
|
||||
@@ -1018,24 +1031,31 @@ class AppBase:
|
||||
#self.logger.info(f"{value} is not a list")
|
||||
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:
|
||||
self.logger.info("[DEBUG] NO multiplier. Running a single iteration.")
|
||||
paramlist.append(baseparams)
|
||||
|
||||
#elif len(listitems) == 1:
|
||||
elif len(listlengths) == 1:
|
||||
self.logger.info("All subitems are the same length")
|
||||
|
||||
for item in listitems:
|
||||
# This loops should always be length 1
|
||||
for key, value in item.items():
|
||||
if isinstance(value, int):
|
||||
if len(paramlist) == value:
|
||||
for subloop in range(value):
|
||||
baseitem = copy.deepcopy(baseparams)
|
||||
paramlist[subloop][key] = baseparams[key][subloop]
|
||||
else:
|
||||
for subloop in range(value):
|
||||
baseitem = copy.deepcopy(baseparams)
|
||||
baseitem[key] = baseparams[key][subloop]
|
||||
paramlist.append(baseitem)
|
||||
if not isinstance(value, int):
|
||||
continue
|
||||
|
||||
if len(paramlist) == value:
|
||||
for subloop in range(value):
|
||||
baseitem = copy.deepcopy(baseparams)
|
||||
paramlist[subloop][key] = baseparams[key][subloop]
|
||||
else:
|
||||
for subloop in range(value):
|
||||
baseitem = copy.deepcopy(baseparams)
|
||||
baseitem[key] = baseparams[key][subloop]
|
||||
paramlist.append(baseitem)
|
||||
|
||||
else:
|
||||
newlength = 1
|
||||
@@ -1046,10 +1066,14 @@ class AppBase:
|
||||
self.logger.info("[DEBUG] Newlength of array: %d. Lists: %s" % (newlength, all_lists))
|
||||
# Get the cartesian product of the arrays
|
||||
#cartesian = await self.cartesian_product(all_lists)
|
||||
cartesian = self.cartesian_product(all_lists)
|
||||
newlist = []
|
||||
for item in cartesian:
|
||||
newlist.append(list(item))
|
||||
try:
|
||||
cartesian = self.cartesian_product(all_lists)
|
||||
newlist = []
|
||||
for item in cartesian:
|
||||
newlist.append(list(item))
|
||||
except Exception as e:
|
||||
self.logger.info(f"[ERROR] Error in cartesian product: {e}")
|
||||
newlist = []
|
||||
|
||||
newobject = {}
|
||||
for subitem in range(len(newlist)):
|
||||
@@ -1059,7 +1083,7 @@ class AppBase:
|
||||
|
||||
paramlist.append(baseitem)
|
||||
|
||||
#self.logger.info("PARAMLIST: %s" % paramlist)
|
||||
self.logger.info("CARTESIAN PARAMLIST: %s" % paramlist)
|
||||
|
||||
#newlist[subitem[0]]
|
||||
#if len(newlist) > 0:
|
||||
@@ -1070,14 +1094,14 @@ class AppBase:
|
||||
#self.logger.info("Listlengths: %s" % listlengths)
|
||||
#paramlist = [baseparams]
|
||||
|
||||
#self.logger.info("[INFO] Return paramlist: %s" % paramlist)
|
||||
#self.logger.info("[INFO] Return paramlist (1): %s" % paramlist)
|
||||
return paramlist
|
||||
|
||||
|
||||
# Runs recursed versions with inner loops and such
|
||||
#async 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
|
||||
|
||||
newparams = {}
|
||||
@@ -1085,29 +1109,30 @@ class AppBase:
|
||||
if isinstance(value, list) and len(value) > 0:
|
||||
self.logger.info(f"[DEBUG] In list check for {key}")
|
||||
|
||||
try:
|
||||
# Added skip for body (OpenAPI) which uses data= in requests
|
||||
# Can be screwed up if they name theirs body too
|
||||
if key != "body":
|
||||
value[0] = json.loads(value[0])
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
pass
|
||||
except TypeError as e:
|
||||
pass
|
||||
for value_index in range(len(value)):
|
||||
try:
|
||||
# Added skip for body (OpenAPI) which uses data= in requests
|
||||
# Can be screwed up if they name theirs body too
|
||||
if key != "body":
|
||||
value[value_index] = json.loads(value[value_index])
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
pass
|
||||
except TypeError as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list):
|
||||
try:
|
||||
loop_wrapper[key] += 1
|
||||
except Exception as e:
|
||||
self.logger.info("[WARNING] Exception in loop wrapper: {e}")
|
||||
loop_wrapper[key] = 1
|
||||
#if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list):
|
||||
# try:
|
||||
# loop_wrapper[key] += 1
|
||||
# except Exception as e:
|
||||
# self.logger.info(f"[WARNING] Exception in loop wrapper: {e}")
|
||||
# loop_wrapper[key] = 1
|
||||
|
||||
newparams[key] = value[0]
|
||||
has_loop = True
|
||||
else:
|
||||
# newparams[key] = value[0]
|
||||
# has_loop = True
|
||||
#else:
|
||||
#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:
|
||||
self.logger.info(f"[WARNING] Error in baseparams list: {e}")
|
||||
newparams[key] = value
|
||||
@@ -1119,19 +1144,18 @@ class AppBase:
|
||||
#ret = await self.run_recursed_items(func, newparams, loop_wrapper)
|
||||
ret = self.run_recursed_items(func, newparams, loop_wrapper)
|
||||
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)")
|
||||
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)")
|
||||
|
||||
# 1. Find the loops that are required and create new multipliers
|
||||
# If here: check for multipliers within this scope.
|
||||
ret = []
|
||||
#param_multiplier = await self.get_param_multipliers(newparams)
|
||||
param_multiplier = self.get_param_multipliers(newparams)
|
||||
|
||||
#self.logger.info("PARAM MULTIPLIER: %s" % param_multiplier)
|
||||
|
||||
# FIXME: This does a deduplication of the data
|
||||
new_params = self.validate_unique_fields(param_multiplier)
|
||||
#self.logger.info(f"NEW PARAMS: {new_params}")
|
||||
if len(new_params) == 0:
|
||||
self.logger.info("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE")
|
||||
self.action_result = {
|
||||
@@ -1151,9 +1175,10 @@ class AppBase:
|
||||
return
|
||||
else:
|
||||
#subparams = new_params
|
||||
#self.logger.info(f"NEW PARAMS: {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:
|
||||
# params = new_params[0]
|
||||
#else:
|
||||
@@ -1205,7 +1230,8 @@ class AppBase:
|
||||
|
||||
tmp = json.dumps({
|
||||
"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:
|
||||
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:
|
||||
#ret = ret[0]
|
||||
self.logger.info("[DEBUG] DONT make list of 1 into 0!!")
|
||||
@@ -1953,11 +1979,11 @@ class AppBase:
|
||||
try:
|
||||
#self.logger.info(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}")
|
||||
# data = data.replace("\'", "\"")
|
||||
data = data.replace("True", "true")
|
||||
data = data.replace("False", "false")
|
||||
data = data.replace("None", "null")
|
||||
data = data.replace("\"", "\\\"")
|
||||
data = data.replace("'", "\"")
|
||||
data = data.replace("True", "true", -1)
|
||||
data = data.replace("False", "false", -1)
|
||||
data = data.replace("None", "null", -1)
|
||||
data = data.replace("\"", "\\\"", -1)
|
||||
data = data.replace("'", "\"", -1)
|
||||
|
||||
tmp_len = json.loads(data, parse_float=str, parse_int=str, parse_constant=str)
|
||||
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 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):
|
||||
parse_string = parse_string.replace(f"{custom_casting[0]}({c_parentheses})",
|
||||
json.dumps(inner_result))
|
||||
parse_string = parse_string.replace(f"{custom_casting[0]}({c_parentheses})", json.dumps(inner_result), 1)
|
||||
else:
|
||||
parse_string = inner_result
|
||||
break
|
||||
@@ -3432,6 +3457,7 @@ class AppBase:
|
||||
|
||||
# Loops in general goes in here to be parsed out as one->multi
|
||||
if len(actualitem) > 0:
|
||||
self.logger.info(f"[INFO] Found {len(actualitem)} items in {parameter['name']}. MULTI EXEC.")
|
||||
multiexecution = True
|
||||
|
||||
handled = False
|
||||
@@ -3612,10 +3638,12 @@ class AppBase:
|
||||
#self.logger.info()
|
||||
|
||||
if not multiexecution:
|
||||
self.logger.info("NOT MULTI EXEC")
|
||||
# Runs a single iteration here
|
||||
new_params = self.validate_unique_fields(params)
|
||||
if isinstance(new_params, list) and len(new_params) == 1:
|
||||
params = new_params[0]
|
||||
#params = new_params
|
||||
else:
|
||||
#self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE")
|
||||
self.action_result["status"] = "SKIPPED"
|
||||
@@ -3663,7 +3691,7 @@ class AppBase:
|
||||
newres = {
|
||||
"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.",
|
||||
"details": found_error,
|
||||
"details": f"{found_error}",
|
||||
}
|
||||
break
|
||||
|
||||
@@ -3897,7 +3925,7 @@ class AppBase:
|
||||
self.action_result["result"] = json.dumps({
|
||||
"success": False,
|
||||
"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)
|
||||
else:
|
||||
@@ -3925,7 +3953,7 @@ class AppBase:
|
||||
self.action_result["result"] = json.dumps({
|
||||
"success": False,
|
||||
"reason": f"Request error - failing silently. Details in detail section",
|
||||
"details": e,
|
||||
"details": f"{e}",
|
||||
})
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
self.action_result["result"] = f"Request error: {e}"
|
||||
@@ -3942,7 +3970,7 @@ class AppBase:
|
||||
self.action_result["result"] = json.dumps({
|
||||
"success": False,
|
||||
"reason": f"General exception in the app. See shuffle action logs for more details.",
|
||||
"details": e,
|
||||
"details": f"{e}",
|
||||
})
|
||||
|
||||
# Send the result :)
|
||||
|
||||
@@ -117,7 +117,7 @@ const CacheView = (props) => {
|
||||
|
||||
|
||||
const deleteCache = (orgId, key) => {
|
||||
toast("Attempting to delete Cache");
|
||||
//toast("Attempting to delete Cache");
|
||||
|
||||
// method: "DELETE",
|
||||
const method = "POST"
|
||||
|
||||
@@ -477,7 +477,7 @@ const ConfigureWorkflow = (props) => {
|
||||
setConfigureWorkflowModalOpen(false)
|
||||
}
|
||||
|
||||
setRequiredTriggers(requiredTriggers)
|
||||
//setRequiredTriggers(requiredTriggers)
|
||||
setRequiredVariables(requiredVariables)
|
||||
setRequiredActions(newactions)
|
||||
}
|
||||
|
||||
@@ -62,8 +62,7 @@ const Header = (props) => {
|
||||
userdata,
|
||||
isMobile,
|
||||
serverside,
|
||||
curpath,
|
||||
billingInfo
|
||||
billingInfo,
|
||||
} = props;
|
||||
|
||||
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
|
||||
@@ -114,6 +113,10 @@ const Header = (props) => {
|
||||
window.location.host === "shuffler.io" ||
|
||||
window.location.host === "localhost:5002";
|
||||
|
||||
const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string")
|
||||
? window.location.pathname
|
||||
: "";
|
||||
|
||||
const clearNotifications = () => {
|
||||
// Don't really care about the logout
|
||||
|
||||
@@ -784,7 +787,7 @@ const Header = (props) => {
|
||||
</Link>
|
||||
</ListItem>
|
||||
</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" }}>
|
||||
<Link to="/usecases" style={hrefStyle}>
|
||||
<Button
|
||||
@@ -849,9 +852,27 @@ const Header = (props) => {
|
||||
</Button>
|
||||
</Link>
|
||||
</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
|
||||
style={{ flex: 2, display: "flex", alignItems: "flex-start", }}
|
||||
style={{ flex: 2, display: "flex", alignItems: "flex-start", padding: 0, }}
|
||||
component="nav"
|
||||
>
|
||||
<div style={{ maxWidth: 70, minWidth: 70, }} />
|
||||
@@ -1514,14 +1535,15 @@ const Header = (props) => {
|
||||
*/
|
||||
|
||||
const topbarHeight = showTopbar ? 40 : 0
|
||||
const topbar = !showTopbar ? null :
|
||||
curpath === "/" || curpath.includes("/docs/") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" ?
|
||||
const topbar = !isCloud || !showTopbar ? null :
|
||||
curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/training" ?
|
||||
<span style={{ zIndex: 50001, }}>
|
||||
<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", }}>
|
||||
Shuffle 1.4 is out! Read more about
|
||||
<u>
|
||||
<a href="https://github.com/Shuffle/Shuffle" style={{ color: "inherit", }} onClick={() => {
|
||||
{/* Shuffle 1.4.0 is out! Read more about */}
|
||||
Shuffle now offers
|
||||
{/* <u>
|
||||
<a href="https://github.com/Shuffle/Shuffle/releases/tag/v1.4.0" target="_blank" style={{ color: "inherit", }} onClick={() => {
|
||||
ReactGA.event({
|
||||
category: "landingpage",
|
||||
action: "click_header_features",
|
||||
@@ -1557,16 +1579,16 @@ const Header = (props) => {
|
||||
Pricing
|
||||
</span>
|
||||
</u>
|
||||
and
|
||||
and */}
|
||||
<u>
|
||||
<span onClick={() => {
|
||||
ReactGA.event({
|
||||
category: "landingpage",
|
||||
action: "click_header_creators",
|
||||
action: "click_header_training",
|
||||
label: "",
|
||||
})
|
||||
|
||||
navigate("/creators")
|
||||
navigate("/training")
|
||||
|
||||
//if (window.drift !== undefined) {
|
||||
// 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)
|
||||
//}
|
||||
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
|
||||
Earning as a Creator
|
||||
Public Training!
|
||||
</span>
|
||||
</u>
|
||||
</Typography>
|
||||
@@ -1586,8 +1608,8 @@ const Header = (props) => {
|
||||
:
|
||||
null
|
||||
|
||||
return !isMobile ?
|
||||
<div style={{ marginTop: 0, }}>
|
||||
return !isMobile ? (
|
||||
<div style={{ marginTop: 0 }}>
|
||||
<AppBar
|
||||
color="transparent"
|
||||
elevation={0}
|
||||
@@ -1599,17 +1621,24 @@ const Header = (props) => {
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
}}
|
||||
>
|
||||
|
||||
{topbar}
|
||||
|
||||
<div style={{ position: "sticky", top: 0, }}>
|
||||
{loginTextBrowser}
|
||||
</div>
|
||||
{modalView}
|
||||
</AppBar>
|
||||
</div>
|
||||
:
|
||||
<div
|
||||
style={{
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
minHeight: 68,
|
||||
maxHeight: 68,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
}}
|
||||
>
|
||||
{loginTextBrowser}
|
||||
</div>
|
||||
{modalView}
|
||||
</AppBar>
|
||||
</div>
|
||||
) : (
|
||||
<MobileView>{loginTextMobile}</MobileView>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
ButtonBase,
|
||||
Tooltip,
|
||||
Select,
|
||||
Autocomplete,
|
||||
MenuItem,
|
||||
Divider,
|
||||
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)) {
|
||||
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,
|
||||
}]
|
||||
|
||||
|
||||
if (authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0) {
|
||||
if (authenticationType.grant_type === "client_credentials") {
|
||||
parsedFields.push({
|
||||
@@ -502,6 +514,8 @@ const AuthenticationOauth2 = (props) => {
|
||||
getAppAuthentication(true, true, true);
|
||||
}
|
||||
|
||||
toast("Authentication successful!")
|
||||
|
||||
// This is more a guess than anything
|
||||
// Should be handled in getAppAuthentication()
|
||||
// in the parent component to make it accurate,
|
||||
@@ -635,9 +649,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
};
|
||||
|
||||
const handleScopeChange = (event) => {
|
||||
const {
|
||||
target: { value },
|
||||
} = event;
|
||||
const {target: { value }} = event;
|
||||
|
||||
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 : (
|
||||
<div style={{width: "100%", marginTop: 10, display: "flex"}}>
|
||||
<span>
|
||||
<Select
|
||||
<Autocomplete
|
||||
multiple
|
||||
underline={false}
|
||||
value={selectedScopes}
|
||||
label="Scopes"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
@@ -987,23 +999,28 @@ const AuthenticationOauth2 = (props) => {
|
||||
minWidth: 300,
|
||||
maxWidth: 300,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
handleScopeChange(e)
|
||||
onChange={(e, value) => {
|
||||
//handleScopeChange(e)
|
||||
setSelectedScopes(typeof value === "string" ? value.split(",") : value);
|
||||
}}
|
||||
fullWidth
|
||||
input={<Input id="select-multiple-native" />}
|
||||
renderValue={(selected) => selected.join(", ")}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{allscopes.map((data, index) => {
|
||||
options={allscopes}
|
||||
getOptionLabel={(option) => option}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<MenuItem key={index} value={data}>
|
||||
<Checkbox checked={selectedScopes.indexOf(data) > -1} />
|
||||
<ListItemText primary={data} />
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<div>
|
||||
{/*<Checkbox checked={selectedScopes.indexOf(data) > -1} />*/}
|
||||
<TextField
|
||||
{...params}
|
||||
label="Search Scopes"
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</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 :
|
||||
@@ -1039,7 +1056,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
"autoClose": 1500,
|
||||
})
|
||||
|
||||
handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes);
|
||||
handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes, undefined, true);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
|
||||
@@ -621,7 +621,7 @@ const ParsedAction = (props) => {
|
||||
var helperText = ""
|
||||
var looperText = ""
|
||||
//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) {
|
||||
var new_occurences = []
|
||||
|
||||
@@ -821,7 +821,7 @@ const RuntimeDebugger = (props) => {
|
||||
}}
|
||||
onChange={(e)=>{handleQueryChange(e)}}
|
||||
color="primary"
|
||||
placeholder="Search Workflow Name, Status, Execution Argument, Results.."
|
||||
placeholder="Filter by Workflow Name, Status, Execution Argument, Results.."
|
||||
id="shuffle_search_field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -580,12 +580,12 @@ const CodeEditor = (props) => {
|
||||
|
||||
// var session = localcodedata.getSession();
|
||||
//var code_lines = localcodedata.split('\n')
|
||||
var code_lines = value.split('\n')
|
||||
|
||||
var newMarkers = []
|
||||
var code_lines = value.split('\n')
|
||||
for (var i = 0; i < code_lines.length; i++) {
|
||||
var current_code_line = code_lines[i];
|
||||
var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g);
|
||||
var current_code_line = code_lines[i]
|
||||
var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g);
|
||||
|
||||
if (!variable_occurence) {
|
||||
continue;
|
||||
@@ -623,15 +623,26 @@ const CodeEditor = (props) => {
|
||||
|
||||
var startCh = dollar_occurence[occ]
|
||||
var endCh = dollar_occurence[occ] + dollar_occurence_len[occ]
|
||||
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i,
|
||||
endCol: endCh,
|
||||
className: correctVariable ? "good-marker" : "bad-marker",
|
||||
type: "text",
|
||||
})
|
||||
try {
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i+1,
|
||||
endCol: endCh+1,
|
||||
className: correctVariable ? "good-marker" : "bad-marker",
|
||||
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)
|
||||
}
|
||||
@@ -643,7 +654,7 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
setMarkers(newMarkers)
|
||||
};
|
||||
}
|
||||
|
||||
const replaceVariables = (swapVariable) => {
|
||||
// var updatedCode = localcodedata.slice(0,index) + "$" + str + localcodedata.slice(index+currentVariable.length+1,)
|
||||
@@ -686,7 +697,7 @@ const CodeEditor = (props) => {
|
||||
|
||||
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)
|
||||
|
||||
// Whelp this is inefficient af. Single loop pls
|
||||
// When the found array is empty.
|
||||
@@ -1448,34 +1459,11 @@ const CodeEditor = (props) => {
|
||||
</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
|
||||
style={{
|
||||
height: 50,
|
||||
width: 50,
|
||||
marginLeft: 100,
|
||||
}}
|
||||
disabled={isAiLoading}
|
||||
onClick={() => {
|
||||
@@ -1536,7 +1524,7 @@ const CodeEditor = (props) => {
|
||||
setCurrentLine(cursorPosition.row)
|
||||
findIndex(cursorPosition.row, cursorPosition.column)
|
||||
|
||||
highlight_variables(localcodedata)
|
||||
//highlight_variables(localcodedata)
|
||||
//console.log("VALUE CURSOR: ", value)
|
||||
}}
|
||||
onChange={(value, editor) => {
|
||||
|
||||
@@ -378,7 +378,7 @@ const svgSize = 24;
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
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 alert = useAlert()
|
||||
let navigate = useNavigate();
|
||||
@@ -540,6 +540,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
})
|
||||
|
||||
// New for generated stuff
|
||||
const releaseToConnectLabel = "Release to Connect"
|
||||
const integrationApps = [{
|
||||
"id": "integration",
|
||||
"name": "Integration Framework",
|
||||
@@ -1357,20 +1358,20 @@ const AngularWorkflow = (defaultprops) => {
|
||||
return;
|
||||
}
|
||||
|
||||
trigger.parameters = [];
|
||||
trigger.parameters = []
|
||||
|
||||
const topic = document.getElementById('topic')?.value;
|
||||
const bootstrapServers = document.getElementById('bootstrap_servers')?.value;
|
||||
const groupId = document.getElementById('group_id')?.value;
|
||||
const topic = document.getElementById('topic')?.value
|
||||
const bootstrapServers = document.getElementById('bootstrap_servers')?.value
|
||||
const groupId = document.getElementById('group_id')?.value
|
||||
//const autoOffsetReset = document.getElementById('auto_offset_reset')?.value;
|
||||
|
||||
if(topic) {
|
||||
trigger.parameters.push({
|
||||
name: "topic",
|
||||
value: topic
|
||||
});
|
||||
})
|
||||
} else {
|
||||
toast("please enter the topic name");
|
||||
toast("Please enter the topic name");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3102,12 +3103,19 @@ const AngularWorkflow = (defaultprops) => {
|
||||
// don't redirect if it exists
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
var execFound = new URLSearchParams(cursearch).get("execution_id");
|
||||
if (execFound === null) {
|
||||
toast(`You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..`)
|
||||
setTimeout(() => {
|
||||
window.location.pathname = "/workflows";
|
||||
}, 2000);
|
||||
}
|
||||
var sessionToken = new URLSearchParams(cursearch).get("session_token");
|
||||
if (execFound === null && sessionToken === null) {
|
||||
toast(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds..`)
|
||||
setTimeout(() => {
|
||||
window.location.pathname = "/workflows";
|
||||
}, 2000);
|
||||
} else if (sessionToken !== null && workflow_id === "3abdfb21-b40f-4e50-b855-ac0d62f83cbe") {
|
||||
toast(`Injecting session token and reloading workflow..`)
|
||||
setTimeout(() => {
|
||||
setCookie("session_token", sessionToken, { path: "/" });
|
||||
window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe";
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3573,20 +3581,28 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const onNodeDragStop = (event, selectedAction) => {
|
||||
const nodedata = event.target.data();
|
||||
if (nodedata.id === selectedAction.id) {
|
||||
return;
|
||||
//console.log("Same node, return")
|
||||
return
|
||||
}
|
||||
|
||||
if (nodedata.finished === false) {
|
||||
return;
|
||||
//console.log("Node is not finished, return")
|
||||
return
|
||||
}
|
||||
|
||||
const connected = event.target.connectedEdges().jsons()
|
||||
if (connected.length > 0 && connected !== undefined) {
|
||||
for (let connectkey in connected) {
|
||||
const edge = connected[connectkey]
|
||||
//console.log("EDGE:", edge)
|
||||
|
||||
//const edge = edgeBase.json()
|
||||
if (edge.data.decorator && edge.data.label === releaseToConnectLabel) {
|
||||
// Transform to normal edge
|
||||
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 destinationnode = cy.getElementById(edge.data.target)
|
||||
@@ -3804,26 +3820,141 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
/*
|
||||
// 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 (branches[branchkey].data.decorator === true) {
|
||||
|
||||
if (closestNode.found) {
|
||||
console.log("No closest node found for: ", nodedata.id)
|
||||
} else {
|
||||
console.log("Closest: ", closestNode)
|
||||
}
|
||||
*/
|
||||
// Add the source/destination
|
||||
if (branches[branchkey].data.source === nodedata.id) {
|
||||
decoratorIds.push(branches[branchkey].data.target)
|
||||
} 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 (
|
||||
originalLocation.x === 0 &&
|
||||
@@ -3882,11 +4013,11 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
// 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(() => {
|
||||
@@ -3899,7 +4030,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
document.removeEventListener("paste", handlePaste, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Nodeselectbatching:
|
||||
// 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
|
||||
const onEdgeAdded = (event) => {
|
||||
setLastSaved(false);
|
||||
const edge = event.target.data();
|
||||
|
||||
//console.log("edge added: ", edge)
|
||||
const edge = event.target.data()
|
||||
//console.log("EDGE ADDED!: ", edge)
|
||||
if (edge.source === undefined && edge.target === undefined) {
|
||||
console.log("Edge source and target is undefined")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5030,6 +5160,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const sourcenode = cy.getElementById(edge.source)
|
||||
const destinationnode = cy.getElementById(edge.target)
|
||||
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
|
||||
console.log("Source or destination node is undefined")
|
||||
} else {
|
||||
//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") {
|
||||
@@ -5042,10 +5173,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
|
||||
console.log("Node: ", targetedge)
|
||||
if (targetedge !== -1) {
|
||||
event.target.remove()
|
||||
|
||||
//console.log("Found branch already!")
|
||||
toast.error("Triggers can have exactly one target node")
|
||||
event.target.remove()
|
||||
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(
|
||||
(data) => data.id === edge.target
|
||||
@@ -5105,15 +5240,14 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
eventTarget.data("isDescriptor") === true ||
|
||||
eventTarget.data("type") === "COMMENT"
|
||||
) {
|
||||
if (eventTarget.data("isDescriptor") === true || eventTarget.data("type") === "COMMENT") {
|
||||
console.log("Removing because of descriptor or comment")
|
||||
event.target.remove();
|
||||
return;
|
||||
event.target.remove()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
setLastSaved(false)
|
||||
targetnode = -1;
|
||||
|
||||
// Check if:
|
||||
@@ -5121,38 +5255,51 @@ const AngularWorkflow = (defaultprops) => {
|
||||
// dest == dest && source == source
|
||||
// backend: check all children? to stop recursion
|
||||
var found = false;
|
||||
for (let branchkey in workflow.branches) {
|
||||
if (
|
||||
workflow.branches[branchkey].destination_id === edge.source &&
|
||||
workflow.branches[branchkey].source_id === edge.target
|
||||
) {
|
||||
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
|
||||
) {
|
||||
//toast("That branch already exists");
|
||||
event.target.remove();
|
||||
const branches = cy.edges().jsons()
|
||||
|
||||
const startNode = cy.nodes().jsons().find((node) => node.data.isStartNode === true)
|
||||
var startnodeId = workflow.start
|
||||
if (startNode !== undefined && startNode !== null) {
|
||||
startnodeId = startNode.data.id
|
||||
}
|
||||
|
||||
//for (let branchkey in workflow.branches) {
|
||||
for (let branchkey in branches) {
|
||||
const branch = branches[branchkey].data
|
||||
|
||||
//if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
|
||||
if (branch.target === edge.source && branch.source === edge.target) {
|
||||
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.type !== "TRIGGER") {
|
||||
toast("Can't make arrow to starting node");
|
||||
event.target.remove();
|
||||
break;
|
||||
toast("Can't make arrow to starting node")
|
||||
event.target.remove()
|
||||
break
|
||||
}
|
||||
|
||||
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
|
||||
// 1. Check if destination exists
|
||||
// 2. Check if source is a trigger
|
||||
@@ -5196,7 +5343,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
newdst !== null
|
||||
) {
|
||||
const dstdata = RunAutocompleter(newdst.data());
|
||||
//console.log("DST Autocompleter: ", dstdata);
|
||||
}
|
||||
|
||||
var newbranch = {
|
||||
@@ -8036,7 +8182,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
width: imagesize,
|
||||
height: imagesize,
|
||||
pointerEvents: "none",
|
||||
// Stretch if necessary
|
||||
objectFit: "cover",
|
||||
// Center the object
|
||||
@@ -10715,11 +10860,101 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Button>
|
||||
|
||||
{/* Check if dest is the same as start */}
|
||||
{conditionsDisabled ?
|
||||
<Typography variant="body1">
|
||||
Conditions are unavailable between triggers and the startnode.
|
||||
</Typography>
|
||||
: null}
|
||||
{conditionsDisabled ?
|
||||
<Typography variant="body1">
|
||||
Conditions are unavailable between triggers and the startnode.
|
||||
</Typography>
|
||||
: 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>
|
||||
);
|
||||
};
|
||||
@@ -11483,7 +11718,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
|
||||
return transformedData;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
const AppAuthSelector = ({ appAuthData }) => {
|
||||
const [selectedAuth, setSelectedAuth] = useState("");
|
||||
@@ -11496,6 +11731,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const handleShowingValue = (appName) => {
|
||||
let mappingWithName = {}
|
||||
let listWithValues = workflow.triggers[selectedTriggerIndex].parameters[5]?.value.split(";").filter(e => e).map(e => e.split("="))
|
||||
|
||||
console.log("LIST WITH VALUES: ", listWithValues)
|
||||
for (let i = 0; i < listWithValues.length; i++) {
|
||||
mappingWithName[listWithValues[i][0]] = listWithValues[i][1]
|
||||
@@ -12180,7 +12416,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
data: newbranch,
|
||||
};
|
||||
|
||||
cy.add(cybranch);
|
||||
cy.add(cybranch)
|
||||
}
|
||||
|
||||
console.log("Value to be set: ", e.target.value);
|
||||
@@ -12722,25 +12958,27 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<div>
|
||||
<div>
|
||||
<div className="app">
|
||||
<div style={{ display: "flex", marginTop: 10 }}>
|
||||
<div style={{ flex: "10" }}>
|
||||
<b>Auth Override</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app">
|
||||
<div style={{ display: "flex", marginTop: 10 }}>
|
||||
<div style={{ flex: "10" }}>
|
||||
<b>Auth Override</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", marginTop: 10 }}>
|
||||
<div style={{ flex: "10", marginLeft: 10 }}>
|
||||
<AppAuthSelector appAuthData={appAuthentication} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: 10 }}>
|
||||
<div style={{ flex: "10", marginLeft: 10 }}>
|
||||
<AppAuthSelector appAuthData={appAuthentication} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -14697,12 +14935,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
right: 0,
|
||||
left: isMobile ? 20 : leftBarSize + 20,
|
||||
top: isMobile ? 30 : appBarSize + 20,
|
||||
pointerEvents: "none",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const TopCytoscapeBar = (props) => {
|
||||
if (workflow.public === true) {
|
||||
return null
|
||||
@@ -14718,7 +14952,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
<div style={topBarStyle}>
|
||||
<div style={{
|
||||
margin: "0px 10px 0px 10px",
|
||||
pointerEvents: "none",
|
||||
pointerevents: "none",
|
||||
}}>
|
||||
<Breadcrumbs
|
||||
aria-label="breadcrumb"
|
||||
@@ -14743,7 +14977,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Link>
|
||||
<h2 style={{
|
||||
margin: 0,
|
||||
pointerEvents: "none",
|
||||
}}>{workflow.name}</h2>
|
||||
</Breadcrumbs>
|
||||
|
||||
@@ -15244,6 +15477,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Infinitely annoying. Need a new bind
|
||||
if (( event.ctrlKey || event.metaKey ) && event.shiftKey) {
|
||||
console.log("Shift key pressed")
|
||||
if (!workflow.public && executionModalOpen) {
|
||||
@@ -15255,7 +15490,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
setExecutionModalView(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
*/
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
@@ -17033,7 +17269,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</div>
|
||||
|
||||
{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
|
||||
style={{color: "rgba(255,255,255,0.4)", marginTop: 10, marginRight: 10, }}
|
||||
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."
|
||||
}
|
||||
|
||||
/*
|
||||
if (result.status === 200 || result.status === 201 || result.status === 204) {
|
||||
return "It looks like the result was successful! If it didn't work, make sure to check if the body you are sending was correct."
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// Validate and check for newlines
|
||||
if (result.success !== false) {
|
||||
@@ -19477,8 +19714,9 @@ const AngularWorkflow = (defaultprops) => {
|
||||
<div style={{ color: "white" }}>Configuration options for {selectedOption}</div>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
{selectedOption === "Kafka Queue" && (
|
||||
<>
|
||||
|
||||
{selectedOption === "Kafka Queue" ?
|
||||
<div>
|
||||
<b>Topic</b>
|
||||
<TextField
|
||||
id="topic"
|
||||
@@ -19539,8 +19777,24 @@ const AngularWorkflow = (defaultprops) => {
|
||||
placeholder={"earliest"}
|
||||
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>
|
||||
<DialogActions>
|
||||
<Button
|
||||
@@ -20005,49 +20259,53 @@ const AngularWorkflow = (defaultprops) => {
|
||||
|
||||
//cy.remove('*')
|
||||
setElements([])
|
||||
|
||||
// Remove all edges
|
||||
cy.edges().remove()
|
||||
cy.nodes().remove()
|
||||
//return
|
||||
}
|
||||
|
||||
// Remove all cy nodes
|
||||
setTimeout(() => {
|
||||
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 (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));
|
||||
|
||||
|
||||
if (selectedAction.id !== undefined && selectedAction.id !== null && selectedAction.id !== "") {
|
||||
setTimeout(() => {
|
||||
const foundaction = cy.$id(selectedAction.id)
|
||||
if (foundaction !== undefined && foundaction !== null) {
|
||||
foundaction.select()
|
||||
}
|
||||
}, 250)
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
if (props.href.includes("http") || props.href.includes("mailto")) {
|
||||
return (
|
||||
@@ -581,8 +610,8 @@ const Docs = (defaultprops) => {
|
||||
position: "sticky",
|
||||
top: 50,
|
||||
paddingTop: "0.25em",
|
||||
minHeight: "93vh",
|
||||
maxHeight: "93vh",
|
||||
minHeight: "95vh",
|
||||
maxHeight: "95vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
@@ -901,7 +930,7 @@ const Docs = (defaultprops) => {
|
||||
<div style={{ textAlign: "left" }}>
|
||||
<Typography variant="h6" style={headerStyle} >Tutorial</Typography>
|
||||
<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 variant="h6" style={headerStyle}>Why Shuffle?</Typography>
|
||||
@@ -940,7 +969,8 @@ const Docs = (defaultprops) => {
|
||||
h4: Heading,
|
||||
h5: 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.
|
||||
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>
|
||||
<MobileView>{postDataMobile}</MobileView>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,8 @@ import theme from '../theme.jsx';
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
|
||||
//import "./CollapsibleList.css"
|
||||
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
|
||||
import { base64_decode, appCategories } from "../views/AppCreator.jsx";
|
||||
@@ -47,8 +49,6 @@ const SetAuthentication = (props) => {
|
||||
|
||||
}
|
||||
|
||||
console.log("App: ", app)
|
||||
|
||||
const getApp = (appid) => {
|
||||
if (serverside === true) {
|
||||
return;
|
||||
@@ -100,7 +100,16 @@ const SetAuthentication = (props) => {
|
||||
useEffect(() => {
|
||||
// Find the ID for the app from the "app_id" query
|
||||
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) {
|
||||
setLoadFail(
|
||||
<span>
|
||||
@@ -121,6 +130,12 @@ const SetAuthentication = (props) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [expandedIndex, setExpandedIndex] = useState(null);
|
||||
|
||||
const handleToggle = (index) => {
|
||||
setExpandedIndex(expandedIndex === index ? null : index);
|
||||
};
|
||||
|
||||
// Handle:
|
||||
// 1. Check for org_id, authentication, and app keys in queries
|
||||
// 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
|
||||
|
||||
const appname = app.name !== undefined ? app.name : "";
|
||||
const appLink = "/apps/" + app.id || "";
|
||||
|
||||
console.log("App: ", app)
|
||||
|
||||
return (
|
||||
<div style={{width: 1000, margin: "auto", marginTop: 50, }}>
|
||||
{loadFail !== "" ?
|
||||
loadFail
|
||||
:
|
||||
<div>
|
||||
<Typography variant="h4" style={{marginBottom: 20,}}>
|
||||
Configure {appname} Authentication
|
||||
<><div>
|
||||
<Typography variant="h4" style={{ marginBottom: 20, }}>
|
||||
A Shuffle Organization has invited you to: Configure <a href={appLink} target="_blank" style={{ color: '#FF8444', textDecoration: 'none' }}>{appname}</a> Authentication
|
||||
</Typography>
|
||||
{app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ?
|
||||
null
|
||||
:
|
||||
app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ?
|
||||
<AuthenticationOauth2
|
||||
selectedApp={app}
|
||||
selectedAction={{
|
||||
"app_name": app.name,
|
||||
"app_id": app.id,
|
||||
"app_version": app.version,
|
||||
"large_image": app.large_image,
|
||||
}}
|
||||
authenticationType={app.authentication}
|
||||
isCloud={true}
|
||||
authButtonOnly={true}
|
||||
getAppAuthentication={undefined}
|
||||
/>
|
||||
|
||||
{/* What does this mean box */}
|
||||
<Typography variant="h6" style={{ marginBottom: 20, }}>
|
||||
What does this mean?
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{ marginBottom: 20, }}>
|
||||
A Shuffle Organization has invited you to configure authentication for this app so that they can use this authentication in one of their workflows.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h6">
|
||||
Authenticate Here:
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" style={{ marginBottom: 20, }}>
|
||||
{app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ?
|
||||
null
|
||||
:
|
||||
<AuthenticationWindow
|
||||
globalUrl={globalUrl}
|
||||
selectedApp={app}
|
||||
authFieldsOnly={true}
|
||||
getAppAuthentication={undefined}
|
||||
appAuthentication={appAuthentication}
|
||||
/>
|
||||
}
|
||||
app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ?
|
||||
<AuthenticationOauth2
|
||||
selectedApp={app}
|
||||
selectedAction={{
|
||||
"app_name": app.name,
|
||||
"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 style={{ height: 100, }}></div>
|
||||
</>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -4080,7 +4080,7 @@ const Workflows = (props) => {
|
||||
{publishModal}
|
||||
{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">
|
||||
<IconButton onClick={() => {
|
||||
setDrawerOpen(true)
|
||||
|
||||
@@ -693,6 +693,10 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
||||
|
||||
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
|
||||
if len(os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) > 0 {
|
||||
log.Printf("[DEBUG] Using Kubernetes service account %s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT"))
|
||||
|
||||
@@ -2456,13 +2456,14 @@ func downloadDockerImageBackend(client *http.Client, imageName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages)
|
||||
|
||||
downloadedImages = append(downloadedImages, imageName)
|
||||
|
||||
data := fmt.Sprintf(`{"name": "%s"}`, imageName)
|
||||
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(
|
||||
"POST",
|
||||
dockerImgUrl,
|
||||
|
||||
Reference in New Issue
Block a user