Filesync w 2.0.0-rc3
This commit is contained in:
@@ -100,6 +100,8 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
|
||||
const [ExampleBody, setExampleBody] = useState({});
|
||||
const [filteredActions, setFilteredActions] = useState([]);
|
||||
|
||||
const [firstSendDone, setFirstSendDone] = useState(false)
|
||||
|
||||
const getJsonObject = (properties) => {
|
||||
|
||||
let jsonObject = {};
|
||||
@@ -1425,18 +1427,20 @@ const ActionsList = memo(({
|
||||
};
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const query = e.target.value;
|
||||
setSearchQuery(query);
|
||||
const query = e?.target?.value?.toLowerCase().replaceAll("_", " ");
|
||||
|
||||
setSearchQuery(query)
|
||||
if (query.length === 0) {
|
||||
setVisibleActions(actions);
|
||||
setVisibleActions(actions)
|
||||
} else {
|
||||
setVisibleActions(
|
||||
actions.filter((action) =>
|
||||
action.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
actions?.filter((action) =>
|
||||
action?.name?.toLowerCase()?.replaceAll("_", " ")?.includes(searchQuery)
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '350px', width: "25%", overflow: 'hidden', marginLeft: !(isLoggedIn || isLoaded) ? 5 : 0}}>
|
||||
<div style={{ borderBottom: '1px solid #494949', paddingTop: 10, paddingBottom: 10}}>
|
||||
@@ -1465,7 +1469,7 @@ const ActionsList = memo(({
|
||||
</div>
|
||||
<TextField
|
||||
value={searchQuery}
|
||||
placeholder="Search here"
|
||||
placeholder="Search endpoints"
|
||||
onChange={handleSearch}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
@@ -2035,6 +2039,7 @@ const Action = memo((
|
||||
}, [editorRef.current]);
|
||||
|
||||
|
||||
const actionname = action?.name.charAt(0).toUpperCase() + action?.name.slice(1).replaceAll("_", " ")
|
||||
return (
|
||||
<div
|
||||
ref={actionRef}
|
||||
@@ -2042,6 +2047,7 @@ const Action = memo((
|
||||
data-index={index}
|
||||
key={action.name.replace(/ /g, "-").replace(/_/g, "-")}
|
||||
style={{
|
||||
marginTop: 50,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
minHeight: 400,
|
||||
@@ -2066,7 +2072,7 @@ const Action = memo((
|
||||
color: "rgba(241, 241, 241, 1)",
|
||||
}}
|
||||
>
|
||||
{action.name}
|
||||
{actionname}
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
@@ -2259,6 +2265,13 @@ const Action = memo((
|
||||
}}
|
||||
disabled={disableExecuteButton}
|
||||
onClick={() => {
|
||||
/*
|
||||
if (!firstSendDone) {
|
||||
setFirstSendDone(true)
|
||||
setCurTab(2)
|
||||
}
|
||||
*/
|
||||
|
||||
if (actionUrl.length === 0) {
|
||||
toast.error("URL cannot be empty");
|
||||
return;
|
||||
|
||||
@@ -215,8 +215,8 @@ const Billing = memo((props) => {
|
||||
|
||||
var priceItem = "price_1MROFrDzMUgUjxHShcSxgHO1"
|
||||
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billing&payment=failure`
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
|
||||
var checkoutObject = {
|
||||
lineItems: [
|
||||
{
|
||||
@@ -2361,7 +2361,8 @@ const Billing = memo((props) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginTop: 40, marginLeft: 10 }}>
|
||||
{isCloud ? (
|
||||
<div style={{ marginTop: 40, marginLeft: 10 }}>
|
||||
<Typography
|
||||
style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
|
||||
>
|
||||
@@ -2545,6 +2546,7 @@ const Billing = memo((props) => {
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
): null}
|
||||
</div>
|
||||
<div style={{ marginTop: 40, display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography
|
||||
|
||||
@@ -20,6 +20,10 @@ import {
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
Skeleton,
|
||||
Chip,
|
||||
Checkbox,
|
||||
MenuItem,
|
||||
DialogContent,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
@@ -46,6 +50,8 @@ import {
|
||||
Business as BusinessIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
VisibilityOff as VisibilityOffIcon,
|
||||
CheckBox,
|
||||
Key,
|
||||
} from "@mui/icons-material";
|
||||
import { validateJson, } from "../views/Workflows.jsx";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
@@ -68,7 +74,7 @@ const scrollStyle2 = {
|
||||
|
||||
|
||||
const CacheView = memo((props) => {
|
||||
const { globalUrl, userdata, serverside, orgId, isSelectedDataStore } = props;
|
||||
const { globalUrl, userdata, serverside, orgId, isSelectedDataStore, selectedOrganization } = props;
|
||||
const [orgCache, setOrgCache] = React.useState("");
|
||||
const [listCache, setListCache] = React.useState([]);
|
||||
const [addCache, setAddCache] = React.useState("");
|
||||
@@ -82,12 +88,16 @@ const CacheView = memo((props) => {
|
||||
const [editCache, setEditCache] = React.useState(false);
|
||||
const [cachedLoaded, setCachedLoaded] = React.useState(false);
|
||||
const [show, setShow] = useState({});
|
||||
const [showDistributionPopup, setShowDistributionPopup] = useState(false);
|
||||
const [selectedSubOrg, setSelectedSubOrg] = useState([]);
|
||||
const [selectedCacheKey, setSelectedCacheKey] = useState("");
|
||||
useEffect(() => {
|
||||
if(orgId?.length >0){
|
||||
listOrgCache(orgId);
|
||||
}
|
||||
}, [orgId]);
|
||||
|
||||
|
||||
const listOrgCache = (orgId) => {
|
||||
fetch(globalUrl + `/api/v1/orgs/${orgId}/list_cache`, {
|
||||
method: "GET",
|
||||
@@ -214,7 +224,7 @@ const CacheView = memo((props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setAddCache(responseJson);
|
||||
toast("New Cache Added Successfully!");
|
||||
toast("New key Added Successfully!");
|
||||
listOrgCache(orgId);
|
||||
setModalOpen(false);
|
||||
})
|
||||
@@ -296,7 +306,7 @@ const CacheView = memo((props) => {
|
||||
>
|
||||
<DialogTitle>
|
||||
<span style={{ color: "white" }}>
|
||||
{ editCache ? "Edit Cache" : "Add Cache" }
|
||||
{ editCache ? "Edit Key" : "Add Key" }
|
||||
</span>
|
||||
</DialogTitle>
|
||||
<div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: "#212121", }}>
|
||||
@@ -368,6 +378,7 @@ const CacheView = memo((props) => {
|
||||
style={{ borderRadius: "2px", fontSize: 16, color: "#ff8544", textTransform:"none" }}
|
||||
onClick={() => {
|
||||
setModalOpen(false)
|
||||
setKey("")
|
||||
setValue("")
|
||||
setDataValue({})
|
||||
}}
|
||||
@@ -380,7 +391,7 @@ const CacheView = memo((props) => {
|
||||
style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }}
|
||||
onClick={() => {
|
||||
{editCache ? editOrgCache(orgId) : addOrgCache(orgId)}
|
||||
|
||||
setKey("")
|
||||
setValue("")
|
||||
setDataValue({})
|
||||
}}
|
||||
@@ -392,9 +403,175 @@ const CacheView = memo((props) => {
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const handleSelectSubOrg = (id, action) => {
|
||||
if (action === "all") {
|
||||
const childOrgs = userdata.orgs.filter(
|
||||
(data) => data.creator_org === userdata.active_org.id
|
||||
);
|
||||
setSelectedSubOrg((prev) => {
|
||||
if (prev.length === childOrgs.length) {
|
||||
// If all child orgs are already selected, clear the selection
|
||||
return [];
|
||||
} else {
|
||||
// Otherwise, select all child org IDs
|
||||
return childOrgs.map((data) => data.id);
|
||||
}
|
||||
});
|
||||
} else if (action === "none") {
|
||||
setSelectedSubOrg([]);
|
||||
} else {
|
||||
setSelectedSubOrg((prev) => {
|
||||
if (prev.includes(id)) {
|
||||
return prev.filter((data) => data !== id);
|
||||
} else {
|
||||
return [...prev, id];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const changeDistribution = (id, selectedSubOrg) => {
|
||||
|
||||
editFileConfig(id, [...new Set(selectedSubOrg)])
|
||||
}
|
||||
|
||||
const editFileConfig = (id, selectedSubOrg, cacheKey) => {
|
||||
const data = {
|
||||
Key: id,
|
||||
action: "suborg_distribute",
|
||||
selected_suborgs: selectedSubOrg,
|
||||
}
|
||||
console.log("data: ", data);
|
||||
|
||||
const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/config`;
|
||||
|
||||
fetch(url, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
toast("Failed overwriting datastore");
|
||||
} else {
|
||||
toast("Successfully updated datastore!");
|
||||
setTimeout(() => {
|
||||
listOrgCache(orgId);
|
||||
setShowDistributionPopup(false);
|
||||
}, 1000);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
toast("Err: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const cacheDistributionModal = showDistributionPopup ? (
|
||||
<Dialog
|
||||
open={showDistributionPopup}
|
||||
onClose={() => setShowDistributionPopup(false)}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
|
||||
border: theme?.palette?.DialogStyle?.border,
|
||||
fontFamily: theme?.typography?.fontFamily,
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
zIndex: 1000,
|
||||
minWidth: "600px",
|
||||
minHeight: "320px",
|
||||
overflow: "auto",
|
||||
'& .MuiDialogContent-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
'& .MuiDialogTitle-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Select sub-org to distribute files
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
|
||||
<MenuItem value="all" onClick={()=> {handleSelectSubOrg(null, "all")}}>All</MenuItem>
|
||||
{userdata.orgs.map((data, index) => {
|
||||
if (data.creator_org !== userdata.active_org.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const imagesize = 22;
|
||||
const imageStyle = {
|
||||
width: imagesize,
|
||||
height: imagesize,
|
||||
pointerEvents: "none",
|
||||
marginRight: 10,
|
||||
marginLeft: data.id === userdata.active_org.id ? 0 : 20,
|
||||
};
|
||||
|
||||
const image = data.image === "" ? (
|
||||
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
|
||||
) : (
|
||||
<img alt={data.name} src={data.image} style={imageStyle} />
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={index}
|
||||
value={data.id}
|
||||
onClick={() => handleSelectSubOrg(data.id)}
|
||||
style={{ display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selectedSubOrg.includes(data.id)}
|
||||
/>
|
||||
{image}
|
||||
<span style={{ marginLeft: 8 }}>{data.name}</span>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
|
||||
<div style={{ display: "flex", marginTop: 20 }}>
|
||||
<Button
|
||||
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
|
||||
onClick={() => setShowDistributionPopup(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544", marginLeft: 10 }}
|
||||
onClick={() => {
|
||||
changeDistribution(selectedCacheKey, selectedSubOrg);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div style={{paddingBottom: isSelectedDataStore?null:250, minHeight: 1000, boxSizing: "border-box", width: isSelectedDataStore? "100%" :null, transition: "width 0.3s ease", padding:isSelectedDataStore?"27px 10px 27px 27px":null, height: isSelectedDataStore?"100%":null, color: isSelectedDataStore?'#ffffff':null, backgroundColor: isSelectedDataStore?'#212121':null, borderTopRightRadius: isSelectedDataStore?'8px':null, borderBottomRightRadius: isSelectedDataStore?'8px':null, borderLeft: "1px solid #494949" }}>
|
||||
{modalView}
|
||||
{cacheDistributionModal}
|
||||
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
|
||||
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
|
||||
<div style={{ marginTop: isSelectedDataStore?null:20, marginBottom: 20 }}>
|
||||
@@ -422,7 +599,7 @@ const CacheView = memo((props) => {
|
||||
setValue("")
|
||||
}}
|
||||
>
|
||||
Add Cache
|
||||
Add Key
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 16, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
|
||||
@@ -459,7 +636,7 @@ const CacheView = memo((props) => {
|
||||
overflowX: "auto",
|
||||
}}>
|
||||
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore?"1px solid #494949":null, display: "table-row"}}>
|
||||
{["Key", "Value", "Actions", "Updated"].map((header, index) => (
|
||||
{["Key", "Value", "Actions", "Updated", "Distribution"].map((header, index) => (
|
||||
<ListItemText
|
||||
key={index}
|
||||
primary={header}
|
||||
@@ -483,7 +660,7 @@ const CacheView = memo((props) => {
|
||||
backgroundColor: "#212121",
|
||||
}}
|
||||
>
|
||||
{Array(4)
|
||||
{Array(5)
|
||||
.fill()
|
||||
.map((_, colIndex) => (
|
||||
<ListItemText
|
||||
@@ -507,9 +684,21 @@ const CacheView = memo((props) => {
|
||||
</ListItem>
|
||||
))
|
||||
: listCache?.length === 0 ? (
|
||||
<Typography style={{ textAlign: "center", marginTop: 20, marginBottom: 20, minWidth: 1000, }}>
|
||||
No Keys Found
|
||||
</Typography>
|
||||
<ListItem style={{ display: "table-row" }}>
|
||||
{Array(5).fill().map((_, index) => (
|
||||
<ListItemText
|
||||
key={index}
|
||||
style={{
|
||||
display: "table-cell",
|
||||
padding: "8px",
|
||||
textAlign: index === 0 ? "center" : "left",
|
||||
}}
|
||||
primary={index === 2 ? "No key found." : null}
|
||||
colSpan={index === 0 ? 5 : undefined}
|
||||
/>
|
||||
))}
|
||||
</ListItem>
|
||||
|
||||
): listCache?.map((data, index) => {
|
||||
var bgColor = isSelectedDataStore? "#212121":"#27292d";
|
||||
if (index % 2 === 0) {
|
||||
@@ -517,6 +706,7 @@ const CacheView = memo((props) => {
|
||||
}
|
||||
|
||||
const validate = validateJson(data.value);
|
||||
const isDistributed = data?.suborg_distribution?.length > 0 ? true : false;
|
||||
return (
|
||||
<ListItem key={index} style={{display:'table-row', backgroundColor: bgColor, maxHeight: 300, overflow: "auto", borderBottomLeftRadius: listCache?.length - 1 === index ? 8 : 0, borderBottomRightRadius: listCache?.length - 1 === index ? 8 : 0,}}>
|
||||
<ListItemText
|
||||
@@ -576,13 +766,14 @@ const CacheView = memo((props) => {
|
||||
primary={(
|
||||
<span style={{ display: "inline" }}>
|
||||
<Tooltip
|
||||
title="Edit item"
|
||||
title={data?.org_id !== selectedOrganization.id ? "You can not edit this cache as it is controlled by parent organization." : "Edit this key" }
|
||||
style={{}}
|
||||
aria-label={"Edit"}
|
||||
>
|
||||
<span>
|
||||
<IconButton
|
||||
style={{ padding: "6px" }}
|
||||
disabled={data.org_id !== selectedOrganization.id ? true : false}
|
||||
onClick={() => {
|
||||
setEditCache(true)
|
||||
setDataValue({
|
||||
@@ -598,14 +789,14 @@ const CacheView = memo((props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={"Public URL (types: text, raw, json)"}
|
||||
title={data?.org_id !== selectedOrganization.id ? "You can not access public URL for this key as it is controlled by parent organization." : "Public URL (types: text, raw, json)" }
|
||||
style={{ marginLeft: 0, }}
|
||||
aria-label={"Public URL"}
|
||||
>
|
||||
<span>
|
||||
<IconButton
|
||||
style={{ padding: "6px" }}
|
||||
disabled={data.public_authorization === undefined || data.public_authorization === null || data.public_authorization === "" ? true : false}
|
||||
disabled={data.public_authorization === undefined || data.public_authorization === null || data.public_authorization === "" || data.org_id !== selectedOrganization.id ? true : false}
|
||||
onClick={() => {
|
||||
window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank");
|
||||
}}
|
||||
@@ -616,18 +807,40 @@ const CacheView = memo((props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={"Delete item"}
|
||||
title={data?.org_id !== selectedOrganization.id ? "You can not delete this key as it is controlled by parent organization." : "Delete this key" }
|
||||
aria-label={"Delete"}
|
||||
>
|
||||
<span>
|
||||
<IconButton
|
||||
style={{ padding: "6px" }}
|
||||
disabled={data.org_id !== selectedOrganization.id ? true : false}
|
||||
onClick={() => {
|
||||
deleteCache(orgId, data.key);
|
||||
//deleteFile(orgId);
|
||||
}}
|
||||
>
|
||||
<img src="/icons/deleteIcon.svg" alt="delete" />
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{
|
||||
stroke: data.org_id === selectedOrganization.id ? "#fd4c62" : "#c8c8c8",
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M5 7.20001H6.6H19.4"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<path
|
||||
d="M17.7996 7.2V18.4C17.7996 18.8243 17.631 19.2313 17.331 19.5314C17.0309 19.8314 16.624 20 16.1996 20H8.19961C7.77526 20 7.3683 19.8314 7.06824 19.5314C6.76818 19.2313 6.59961 18.8243 6.59961 18.4V7.2M8.99961 7.2V5.6C8.99961 5.17565 9.16818 4.76869 9.46824 4.46863C9.7683 4.16857 10.1753 4 10.5996 4H13.7996C14.224 4 14.6309 4.16857 14.931 4.46863C15.231 4.76869 15.3996 5.17565 15.3996 5.6V7.2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -642,6 +855,44 @@ const CacheView = memo((props) => {
|
||||
}}
|
||||
primary={new Date(data.edited * 1000).toISOString()}
|
||||
/>
|
||||
{selectedOrganization.id !== undefined && data?.org_id !== selectedOrganization.id ?
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip
|
||||
title="Parent organization controlled datastore. You can use, but not modify this key. Contact an admin of your parent organization if you need changes to this."
|
||||
placement="top"
|
||||
>
|
||||
<Chip
|
||||
label={"Parent"}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }}
|
||||
/>
|
||||
:
|
||||
<Tooltip
|
||||
title="Distributed to sub-organizations. This means the sub organizations can use this datastore key, but can not modify it."
|
||||
placement="top"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={ userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" )? true : false}
|
||||
checked={isDistributed}
|
||||
style={{ }}
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setShowDistributionPopup(true)
|
||||
if(data?.suborg_distribution?.length > 0){
|
||||
setSelectedSubOrg(data.suborg_distribution)
|
||||
}else{
|
||||
setSelectedSubOrg([])
|
||||
}
|
||||
setSelectedCacheKey(data.key)
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
@@ -653,4 +904,4 @@ const CacheView = memo((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
export default memo(CacheView);
|
||||
export default memo(CacheView);
|
||||
|
||||
@@ -36,7 +36,8 @@ const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isD
|
||||
}
|
||||
|
||||
const action = folderDisabled ? "enable_folder" : "disable_folder";
|
||||
const url = `${globalUrl}/api/v1/detections/${action}`;
|
||||
//const url = `${globalUrl}/api/v1/detections/${detectionType}/selected_rules/${action}`;
|
||||
const url = `${globalUrl}/api/v1/detections/sigma/selected_rules/${action}`;
|
||||
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
@@ -258,7 +259,7 @@ const DetectionExplorer = (props) => {
|
||||
rule.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
|
||||
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.data_lake?.enabled === true).length : 0
|
||||
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.archived === false && env?.data_lake?.enabled === true).length : 0
|
||||
|
||||
return (
|
||||
<Container>
|
||||
@@ -284,7 +285,8 @@ const DetectionExplorer = (props) => {
|
||||
</Typography>
|
||||
|
||||
<div style={{display: "flex", }}>
|
||||
{workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
|
||||
|
||||
{/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
|
||||
<div style={{display: "flex", }}>
|
||||
<div style={{minWidth: 400, maxWidth: 400, }}>
|
||||
<WorkflowValidationTimeline
|
||||
@@ -313,7 +315,7 @@ const DetectionExplorer = (props) => {
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
:
|
||||
: */}
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
@@ -331,11 +333,11 @@ const DetectionExplorer = (props) => {
|
||||
detectionWorkflowId === "" ? `Connect to ${detectionInfo?.category}` :
|
||||
isDetectionValid ? `Connected to ${detectionInfo?.category}` : `Fix ${detectionInfo?.category} connection`}
|
||||
</Button>
|
||||
}
|
||||
{/**/}
|
||||
|
||||
{detectionInfo?.category === "SIGMA" || detectionInfo?.category === "SIEM" ?
|
||||
<Tooltip title={`You have ${lakeNodes} available Data Lake node(s)`}>
|
||||
<a href="/admin?tab=environments" style={{textDecoration: "none", color: "inherit", }} target="_blank" rel="noreferrer">
|
||||
<a href="/admin?tab=Locations" style={{textDecoration: "none", color: "inherit", }} target="_blank" rel="noreferrer">
|
||||
<FmdGoodIcon style={{marginLeft: 15, marginTop: 5, color: lakeNodes > 0 ? green : red}} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
@@ -137,6 +137,8 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
|
||||
setResponseValue(e.target.value)
|
||||
|
||||
toast.error("The automatic response system is NOT available for you yet. Please contact support@shuffler.io if you want to try this feature.")
|
||||
|
||||
// FIXME: Handle:
|
||||
// 1. Get the current cache for the detection
|
||||
// 2. Create a new mapping for Detection -> Response
|
||||
@@ -187,7 +189,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
<Switch
|
||||
checked={isEnabled && !folderDisabled}
|
||||
onChange={handleSwitchChange}
|
||||
disabled={false}
|
||||
disabled={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -647,7 +647,7 @@ const EditWorkflow = (props) => {
|
||||
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
||||
userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ?
|
||||
<Typography variant="body2" style={{ marginTop: 10, color: "rgba(255,255,255,0.7)" }}>
|
||||
Your organization does not have any suborgs yet OR your user may not have access to available suborgs. Please <a href="/admin?tab=suborgs" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">make one</a> or get access to suborgs by another admin, then try again.
|
||||
Your organization does not have any suborgs yet OR your user may not have access to available suborgs. Please <a href="/admin?tab=tenants" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">make one</a> or get access to suborgs by another admin, then try again.
|
||||
</Typography>
|
||||
:
|
||||
<Typography variant="body2" style={{ marginTop: 10, color: "rgba(255,255,255,0.7)" }}>
|
||||
@@ -738,7 +738,7 @@ const EditWorkflow = (props) => {
|
||||
})}
|
||||
</Select>
|
||||
:
|
||||
<Link to={"/admin?tab=suborgs"} style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">
|
||||
<Link to={"/admin?tab=tenants"} style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">
|
||||
<Typography variant="body2" style={{ marginTop: 10, }}>
|
||||
Create a sub-org to distribute workflows to suborgs.
|
||||
</Typography>
|
||||
@@ -753,7 +753,7 @@ const EditWorkflow = (props) => {
|
||||
Git Backup Repository
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ textAlign: "left", marginTop: 5, }} color="textSecondary">
|
||||
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides your <a href="/admin?admin_tab=organization" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">default backup repository</a>. <a href="/docs/configuration#environment-variables" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">Credentials are encrypted.</a> Creates <a href="/admin?admin_tab=priorities" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">notifications</a> if it fails.
|
||||
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides your <a href="/admin?admin_tab=org_config" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">default backup repository</a>. <a href="/docs/configuration#environment-variables" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">Credentials are encrypted.</a> Creates <a href="/admin?admin_tab=notifications" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">notifications</a> if it fails.
|
||||
</Typography>
|
||||
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||
<Grid item xs={6} style={{}}>
|
||||
|
||||
@@ -1567,7 +1567,7 @@ const Files = memo((props) => {
|
||||
placement="top"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" ? true : false}
|
||||
disabled={userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" ) ? true : false}
|
||||
checked={isDistributed}
|
||||
style={{ }}
|
||||
color="secondary"
|
||||
|
||||
@@ -869,8 +869,8 @@ const LicencePopup = (props) => {
|
||||
:
|
||||
shuffleVariant === 0 ? "price_1PZPSSEJjT17t98NLJoTMYja" : "price_1PZPQuEJjT17t98N3yORUtd9"
|
||||
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
|
||||
const failUrl = `${window.location.origin}/pricing?admin_tab=billing&payment=failure`
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
|
||||
|
||||
console.log("Priceitem: ", priceItem, shuffleVariant)
|
||||
var checkoutObject = {
|
||||
@@ -888,7 +888,7 @@ const LicencePopup = (props) => {
|
||||
}
|
||||
|
||||
if (stripe === undefined || stripe === null || stripe.redirectToCheckout === undefined) {
|
||||
window.open("https://shuffler.io/admin?admin_tab=billing&payment=stripe_error", "_self")
|
||||
window.open("https://shuffler.io/admin?admin_tab=billingstats&payment=stripe_error", "_self")
|
||||
}
|
||||
|
||||
stripe.redirectToCheckout(checkoutObject)
|
||||
|
||||
@@ -465,7 +465,7 @@ const Header = (props) => {
|
||||
</Link>
|
||||
|
||||
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
|
||||
<Link to="/admin?admin_tab=priorities" style={hrefStyle}>
|
||||
<Link to="/admin?admin_tab=notifications" style={hrefStyle}>
|
||||
<MenuItem
|
||||
onClick={(event) => {
|
||||
handleClose();
|
||||
@@ -1107,7 +1107,7 @@ const Header = (props) => {
|
||||
);
|
||||
})}
|
||||
<Divider />
|
||||
<Link to="/admin?tab=suborgs" style={hrefStyle}>
|
||||
<Link to="/admin?tab=tenants" style={hrefStyle}>
|
||||
<MenuItem
|
||||
key={"add suborgs"}
|
||||
style={{
|
||||
|
||||
@@ -92,6 +92,7 @@ import {
|
||||
SquareFoot as SquareFootIcon,
|
||||
Storage as StorageIcon,
|
||||
Check as CheckIcon,
|
||||
PriorityHigh as PriorityHighIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
export const useStyles = makeStyles({
|
||||
@@ -165,6 +166,7 @@ const ParsedAction = (props) => {
|
||||
|
||||
expansionModalOpen,
|
||||
setExpansionModalOpen,
|
||||
fixExample,
|
||||
|
||||
listCache,
|
||||
setActiveDialog,
|
||||
@@ -258,7 +260,7 @@ const ParsedAction = (props) => {
|
||||
|
||||
if (param.required === false && param.name.startsWith("${") && param.name.endsWith("}")) {
|
||||
// Check if it's a required param
|
||||
param.autocompleted = false
|
||||
param.autocompleted = true
|
||||
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
||||
if (selectedAction.required_body_fields.includes(param.name)) {
|
||||
param.required = true
|
||||
@@ -281,6 +283,7 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
|
||||
if (param.field_active === true) {
|
||||
param.autocompleted = true
|
||||
generated_optional.push(param)
|
||||
continue
|
||||
}
|
||||
@@ -298,8 +301,8 @@ const ParsedAction = (props) => {
|
||||
const newparams = auth
|
||||
.concat(bodyfield)
|
||||
.concat(required)
|
||||
.concat(special_optional)
|
||||
.concat(generated_optional)
|
||||
.concat(special_optional)
|
||||
.concat(optional)
|
||||
|
||||
var newkeyorder = []
|
||||
@@ -1492,6 +1495,7 @@ const ParsedAction = (props) => {
|
||||
newAppname = newAppname.replaceAll("_", " ")
|
||||
}
|
||||
|
||||
var optionalFound = false
|
||||
return (
|
||||
<div style={appApiViewStyle} id="parsed_action_view">
|
||||
|
||||
@@ -3122,91 +3126,6 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
{/*
|
||||
<ButtonGroup fullWidth>
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={
|
||||
"Show all body fields instead of the body itself"
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
variant={hideBody === true ? "outlined" : "contained"}
|
||||
color="primary"
|
||||
style={{textTransform: "none",}}
|
||||
onClick={() => {
|
||||
// Set localstorage
|
||||
localStorage.setItem("hideBody", "true")
|
||||
|
||||
setHideBody(false)
|
||||
const updatedParameters = selectedActionParameters.map((param) => {
|
||||
if (param.name === "body") {
|
||||
return {
|
||||
...param,
|
||||
id: "UNTOGGLED",
|
||||
}
|
||||
}
|
||||
|
||||
if (param.description === openApiFieldDesc) {
|
||||
// Check required fields here
|
||||
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
||||
// Look for the field name in the required_body_fields
|
||||
if (selectedAction.required_body_fields.includes(param.name)) {
|
||||
param.required = true
|
||||
} else {
|
||||
param.required = false
|
||||
}
|
||||
}
|
||||
|
||||
return { ...param, field_active: true }
|
||||
}
|
||||
|
||||
return param
|
||||
})
|
||||
|
||||
setSelectedActionParameters(updatedParameters)
|
||||
}}>
|
||||
Simplified
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={
|
||||
"Show the body as it is"
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
variant={hideBody === true ? "contained" : "outlined" }
|
||||
color="primary"
|
||||
style={{textTransform: "none",}}
|
||||
onClick={() => {
|
||||
localStorage.setItem("hideBody", "false")
|
||||
setHideBody(true)
|
||||
// Make sure the body field is shown
|
||||
const updatedParameters = selectedActionParameters.map((param) => {
|
||||
if (param.name === "body") {
|
||||
return {
|
||||
...param,
|
||||
id: "TOGGLED",
|
||||
}
|
||||
}
|
||||
|
||||
if (param.description === openApiFieldDesc) {
|
||||
return { ...param, field_active: false }
|
||||
}
|
||||
|
||||
return param
|
||||
})
|
||||
|
||||
setSelectedActionParameters(updatedParameters)
|
||||
}}>
|
||||
Advanced
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
*/}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -3551,7 +3470,7 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
)
|
||||
|
||||
// Finds headers from a string to be used for autocompletion
|
||||
const findHeaders = (inputdata) => {
|
||||
@@ -4087,9 +4006,24 @@ const ParsedAction = (props) => {
|
||||
};
|
||||
|
||||
var parsedPaths = [];
|
||||
if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
const coverColor = "#82ccc3"
|
||||
//menuPosition.left -= 50
|
||||
@@ -4270,8 +4204,14 @@ const ParsedAction = (props) => {
|
||||
data.variant = "STATIC_VALUE"
|
||||
}
|
||||
|
||||
const isFirstOptional = optionalFound === false && data.configuration === false && data.required === false ? true : false
|
||||
if (optionalFound === false && data.configuration === false && data.required === false) {
|
||||
optionalFound = true
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={data.name}>
|
||||
<div key={data.name} style={{marginTop: isFirstOptional ? 55 : 5, }}>
|
||||
{isFirstOptional ? <Divider style={{backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null}
|
||||
{showButtonField === true ? hideBodyButtonValue : null}
|
||||
<div
|
||||
style={{ marginTop: 20, marginBottom: 0, display: "flex" }}
|
||||
@@ -4297,6 +4237,20 @@ const ParsedAction = (props) => {
|
||||
) : null}
|
||||
|
||||
{hasAutocomplete === true ?
|
||||
data.field_active === true ?
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title={"This is an advanced field, simplified to make it easier to use. NOT required according to the API documentation."}
|
||||
placement="top"
|
||||
>
|
||||
<PriorityHighIcon
|
||||
style={{
|
||||
color: "rgba(255,255,255,0.5)" ,
|
||||
marginRight: 0,
|
||||
}}/>
|
||||
</Tooltip>
|
||||
|
||||
:
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title={"Field was autocompleted by Shuffle based on previous actions (same fields or parent nodes)"}
|
||||
@@ -4316,7 +4270,7 @@ const ParsedAction = (props) => {
|
||||
title={"Explore your keys in Datastore"}
|
||||
placement="top"
|
||||
>
|
||||
<a href="/admin?tab=cache" target="_blank" style={{textDecoration: "none"}}>
|
||||
<a href="/admin?tab=datastore" target="_blank" style={{textDecoration: "none"}}>
|
||||
<StorageIcon style={{
|
||||
color: "#FF8544",
|
||||
marginRight: 10,
|
||||
@@ -4351,14 +4305,16 @@ const ParsedAction = (props) => {
|
||||
parsedvalue = ""
|
||||
}
|
||||
|
||||
//console.log("Required fields: ", selectedActionParameters[count])
|
||||
|
||||
setEditorData({
|
||||
"name": data.name,
|
||||
"value": parsedvalue,
|
||||
"value": fixExample(parsedvalue),
|
||||
"field_number": count,
|
||||
"actionlist": actionlist,
|
||||
"field_id": clickedFieldId,
|
||||
|
||||
"example": selectedActionParameters[count].example,
|
||||
"example": fixExample(selectedActionParameters[count].example),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -387,7 +387,7 @@ const SearchData = props => {
|
||||
if (responseJson.success === false) {
|
||||
toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io for more info`)
|
||||
} else {
|
||||
toast(`App successfully ${type}d. Please refresh the page to use it.`)
|
||||
toast(`App successfully ${type}d. It may now be used in your workflows.`)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
@@ -114,7 +114,9 @@ const CodeEditor = (props) => {
|
||||
editorData,
|
||||
|
||||
setAiQueryModalOpen,
|
||||
fullScreenMode
|
||||
fullScreenMode,
|
||||
environment,
|
||||
fixExample,
|
||||
} = props
|
||||
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
@@ -189,8 +191,27 @@ const CodeEditor = (props) => {
|
||||
tmpVariables.push('$' + actionlist[i].autocomplete.toLowerCase())
|
||||
|
||||
var parsedPaths = []
|
||||
if (typeof actionlist[i].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[i].example, "");
|
||||
if (actionlist[i].type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof actionlist[i].value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(actionlist[i].value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, skip parsing
|
||||
continue
|
||||
}
|
||||
} else if (typeof actionlist[i].value === "object") {
|
||||
// Direct object/array value
|
||||
parsedPaths = GetParsedPaths(actionlist[i].value, "");
|
||||
}
|
||||
} else {
|
||||
// Handle regular action results
|
||||
if (typeof actionlist[i].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[i].example, "");
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in parsedPaths) {
|
||||
@@ -596,7 +617,7 @@ const CodeEditor = (props) => {
|
||||
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 variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
|
||||
|
||||
if (!variable_occurence) {
|
||||
continue;
|
||||
@@ -664,6 +685,75 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
var code_lines = value.split('\n')
|
||||
for (var i = 0; i < code_lines.length; i++) {
|
||||
var current_code_line = code_lines[i]
|
||||
|
||||
// Look for REPLACE_ME
|
||||
var variable_occurence = current_code_line.match(/REPLACE_ME/g)
|
||||
|
||||
if (!variable_occurence) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var new_occurences = variable_occurence.filter((occurrence) => occurrence[0])
|
||||
variable_occurence = new_occurences
|
||||
|
||||
// Find the start position of REPLACE_ME and highlight it
|
||||
var dollar_occurence = []
|
||||
for (let ch = 0; ch < current_code_line.length; ch++) {
|
||||
// Not allowing it then lol
|
||||
if (ch + 9 >= current_code_line.length) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Rofl - at least it is specific
|
||||
if (current_code_line[ch] === 'R' && current_code_line[ch + 1] === 'E' && current_code_line[ch + 2] === 'P' && current_code_line[ch + 3] === 'L' && current_code_line[ch + 4] === 'A' && current_code_line[ch + 5] === 'C' && current_code_line[ch + 6] === 'E' && current_code_line[ch + 7] === '_' && current_code_line[ch + 8] === 'M' && current_code_line[ch + 9] === 'E') {
|
||||
dollar_occurence.push(ch)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (variable_occurence.length === 0) {
|
||||
//value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #282828; border-radius: 0px; color: #b8bb26"})
|
||||
//value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #; border-radius: 0px; color: inherit"})
|
||||
}
|
||||
|
||||
for (let occ = 0; occ < variable_occurence.length; occ++) {
|
||||
const fixedVariable = variable_occurence[occ]
|
||||
|
||||
var startCh = dollar_occurence[occ]
|
||||
var endCh = dollar_occurence[occ] + 10
|
||||
try {
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i,
|
||||
endCol: endCh,
|
||||
className: "bad-marker",
|
||||
type: "text",
|
||||
})
|
||||
} catch (e) {
|
||||
console.log("Error in color highlighting: ", e);
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i,
|
||||
endCol: endCh,
|
||||
className: "bad-marker",
|
||||
type: "text",
|
||||
})
|
||||
}
|
||||
|
||||
setMarkers(newMarkers)
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
console.log("Error in color highlighting: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
setMarkers(newMarkers)
|
||||
}
|
||||
|
||||
@@ -720,6 +810,34 @@ const CodeEditor = (props) => {
|
||||
const fixedVariable = fixVariable(found[i])
|
||||
|
||||
var valuefound = false
|
||||
|
||||
// First check if it's a workflow variable
|
||||
if (actionlist !== undefined && actionlist.length > 0) {
|
||||
const workflowVar = actionlist?.find(item =>
|
||||
item.type === "workflow_variable" &&
|
||||
`$${item.autocomplete.toLowerCase()}` === fixedVariable.toLowerCase()
|
||||
)
|
||||
|
||||
if (workflowVar && workflowVar.example) {
|
||||
valuefound = true
|
||||
try {
|
||||
// Try to parse the example value if it's stored as a JSON string
|
||||
if (typeof workflowVar.example === "string" &&
|
||||
(workflowVar.example.startsWith("[") || workflowVar.example.startsWith("{"))) {
|
||||
const parsedExample = JSON.parse(workflowVar.example)
|
||||
input = input.replace(found[i], JSON.stringify(parsedExample), -1)
|
||||
} else {
|
||||
input = input.replace(found[i], workflowVar.example, -1)
|
||||
}
|
||||
continue
|
||||
} catch (e) {
|
||||
console.log("Error parsing workflow variable:", e)
|
||||
input = input.replace(found[i], workflowVar.example, -1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < actionlist.length; j++) {
|
||||
if (fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()) {
|
||||
continue
|
||||
@@ -750,9 +868,26 @@ const CodeEditor = (props) => {
|
||||
var shouldbreak = false
|
||||
for (var k = 0; k < actionlist.length; k++) {
|
||||
var parsedPaths = []
|
||||
if (typeof actionlist[k].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].example, "");
|
||||
}
|
||||
|
||||
// Handle both workflow variables and regular actions
|
||||
if (actionlist[k].type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(actionlist[k].value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].value === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].example, "");
|
||||
}
|
||||
|
||||
for (var key in parsedPaths) {
|
||||
const fullpath = "$" + actionlist[k].autocomplete.toLowerCase() + parsedPaths[key].autocomplete.toLowerCase()
|
||||
@@ -766,7 +901,22 @@ const CodeEditor = (props) => {
|
||||
|
||||
var new_input = ""
|
||||
try {
|
||||
new_input = FindJsonPath(fullpath, actionlist[k].example)
|
||||
const sourceData = actionlist[k].type === "workflow_variable" ?
|
||||
(() => {
|
||||
// Try to parse the value if it's a JSON string
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
return JSON.parse(actionlist[k].value);
|
||||
} catch (e) {
|
||||
// If parsing fails, return the original string value
|
||||
return actionlist[k].value;
|
||||
}
|
||||
}
|
||||
return actionlist[k].value;
|
||||
})() :
|
||||
actionlist[k].example;
|
||||
|
||||
new_input = FindJsonPath(fullpath, sourceData)
|
||||
} catch (e) {
|
||||
console.log("ERR IN INPUT: ", e)
|
||||
}
|
||||
@@ -789,8 +939,9 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
input = input.replace(fixedVariable, new_input, -1)
|
||||
input = input.replace(found[i], new_input, -1)
|
||||
// Replace both the fixed and original variable to handle both #0 and # cases
|
||||
input = input.replace(found[i], new_input)
|
||||
input = input.replace(fixedVariable, new_input)
|
||||
|
||||
shouldbreak = true
|
||||
break
|
||||
@@ -876,8 +1027,12 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
if (edited === false) {
|
||||
if (!item.value.includes("{%") && !item.value.includes("{{")) {
|
||||
setlocalcodedata(localcodedata + " | " + item.value + " }}")
|
||||
if (item.value.includes("{%") || item.value.includes("{{")) {
|
||||
if (!item.value.includes("}}") && !item.value.includes("%}")) {
|
||||
setlocalcodedata(localcodedata + " | " + item.value + " }}")
|
||||
} else {
|
||||
setlocalcodedata(localcodedata + item.value)
|
||||
}
|
||||
} else {
|
||||
setlocalcodedata(localcodedata + item.value)
|
||||
}
|
||||
@@ -899,7 +1054,7 @@ const CodeEditor = (props) => {
|
||||
const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : selectedAction.name === "execute_bash" ? "execute_bash" : "repeat_back_to_me"
|
||||
const params = actionname === "execute_python" ? [{ "name": "code", "value": inputdata }] : actionname === "execute_bash" ? [{ "name": "code", "value": inputdata }, { "name": "shuffle_input", "value": "", }] : [{ "name": "call", "value": inputdata }]
|
||||
|
||||
const actiondata = { "description": "Repeats the call parameter", "id": "", "name": actionname, "label": "", "node_type": "", "environment": "", "sharing": false, "private_id": "", "public_id": "", "app_id": appid, "tags": null, "authentication": [], "tested": false, "parameters": params, "execution_variable": { "description": "", "id": "", "name": "", "value": "" }, "returns": { "description": "", "example": "", "id": "", "schema": { "type": "string" } }, "authentication_id": "", "example": "", "auth_not_required": false, "source_workflow": "", "run_magic_output": false, "run_magic_input": false, "execution_delay": 0, "app_name": "Shuffle Tools", "app_version": "1.2.0", "selectedAuthentication": {} }
|
||||
const actiondata = { "description": "Repeats the call parameter", "id": "", "name": actionname, "label": "", "node_type": "", "environment": environment?.Name, "sharing": false, "private_id": "", "public_id": "", "app_id": appid, "tags": null, "authentication": [], "tested": false, "parameters": params, "execution_variable": { "description": "", "id": "", "name": "", "value": "" }, "returns": { "description": "", "example": "", "id": "", "schema": { "type": "string" } }, "authentication_id": "", "example": "", "auth_not_required": false, "source_workflow": "", "run_magic_output": false, "run_magic_input": false, "execution_delay": 0, "app_name": "Shuffle Tools", "app_version": "1.2.0", "selectedAuthentication": {} }
|
||||
|
||||
setExecutionResult({
|
||||
"valid": false,
|
||||
@@ -1396,7 +1551,22 @@ const CodeEditor = (props) => {
|
||||
};
|
||||
|
||||
var parsedPaths = [];
|
||||
if (typeof innerdata.example === "object") {
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
@@ -1585,7 +1755,13 @@ const CodeEditor = (props) => {
|
||||
}}
|
||||
disabled={editorData === undefined || editorData.example === undefined || editorData.example === null || editorData.example.length === 0}
|
||||
onClick={() => {
|
||||
setlocalcodedata(editorData.example)
|
||||
if (fixExample !== undefined) {
|
||||
const newExample = fixExample(editorData.example)
|
||||
setlocalcodedata(newExample)
|
||||
} else {
|
||||
console.log("No fix example available!")
|
||||
setlocalcodedata(editorData.example)
|
||||
}
|
||||
}}
|
||||
color="secondary"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user