Merge branch 'Shuffle:1.4.0' into 1.4.0
This commit is contained in:
@@ -40,12 +40,12 @@ cytoscape.use(edgehandles);
|
||||
export const findSpecificApp = (framework, inputcategory) => {
|
||||
// Get the frameworkinfo for the org and fill in
|
||||
if (framework === undefined || framework === null) {
|
||||
console.log("findSpecificApp: framework is null")
|
||||
//console.log("findSpecificApp: framework is null")
|
||||
return null
|
||||
}
|
||||
|
||||
if (inputcategory === undefined || inputcategory === null) {
|
||||
console.log("findSpecificApp: category is null")
|
||||
//console.log("findSpecificApp: category is null")
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@ export const findSpecificApp = (framework, inputcategory) => {
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
name: "IAM :default",
|
||||
large_image: parsedDatatypeImages["IAM"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
@@ -654,7 +654,7 @@ const AppFramework = (props) => {
|
||||
|
||||
|
||||
const handleLoadNextSuggestion = (frameworkData) => {
|
||||
console.log("Should check for next apps to load from App suggestion model")
|
||||
//console.log("Should check for next apps to load from App suggestion model")
|
||||
//fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||
//credentials: "include",
|
||||
//cors: "no-cors",
|
||||
@@ -720,7 +720,7 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
const showRecommendations = (changed, frameworkData) => {
|
||||
console.log("Inside recommendation loader")
|
||||
//console.log("Inside recommendation loader")
|
||||
setChangedApp(changed)
|
||||
handleLoadNextSuggestion(frameworkData)
|
||||
|
||||
@@ -761,7 +761,7 @@ const AppFramework = (props) => {
|
||||
//console.log("APptype, changed, framework: ", apptype.toLowerCase(), alternativeChanged.toLowerCase(), changed.toLowerCase(), frameworkData)
|
||||
if (changed.toLowerCase() === apptype.toLowerCase() || changed.toLowerCase().includes(apptype.toLowerCase()) || alternativeChanged.toLowerCase() === apptype.toLowerCase() || alternativeChanged.toLowerCase().includes(apptype.toLowerCase())) {
|
||||
potential = true
|
||||
console.log("Potential: !", apptype)
|
||||
//console.log("Potential: !", apptype)
|
||||
|
||||
if (frameworkData[apptype] !== undefined && frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) {
|
||||
usecase.items[itemtype].app = frameworkData[apptype]
|
||||
@@ -819,10 +819,10 @@ const AppFramework = (props) => {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)")
|
||||
//console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)")
|
||||
}
|
||||
|
||||
console.log("Usecase: ", usecase)
|
||||
//console.log("Usecase: ", usecase)
|
||||
if (matches.length === usecase.items.length) {
|
||||
usecase.color = "#c51152"
|
||||
usecase.type = usecaseTypes[key].name
|
||||
@@ -833,8 +833,8 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
// FIXME: Check if a usecase has already been handled
|
||||
console.log("")
|
||||
console.log("GOT USECASES: ", showusecases)
|
||||
//console.log("")
|
||||
//console.log("GOT USECASES: ", showusecases)
|
||||
|
||||
// FIXME: Just showing one usecase at a time for now
|
||||
if (showusecases.length > 0) {
|
||||
@@ -894,38 +894,37 @@ const AppFramework = (props) => {
|
||||
parsedUsecase.process.push(edge.data)
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(parsedUsecase),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(parsedUsecase),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
toast("Failed to update framework for your org.")
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
toast("Updated usecase.")
|
||||
toast("Failed to update framework for your org.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
toast("Updated usecase.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
const activateApp = (appid) => {
|
||||
fetch(globalUrl+"/api/v1/apps/"+appid+"/activate", {
|
||||
@@ -957,45 +956,54 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
const setFrameworkItem = (data) => {
|
||||
console.log("Setting framework item: ", data, isCloud)
|
||||
if (!isCloud) {
|
||||
activateApp(data.id)
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
toast("Failed to update framework for your org.")
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed getting appframework: " + responseJson.reason)
|
||||
} else {
|
||||
toast("Failed to update framework for your org.")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (data.id === "remove") {
|
||||
frameworkData[data.type] = {}
|
||||
frameworkData[data.type.toLowerCase()] = {}
|
||||
} else {
|
||||
frameworkData[data.type] = data
|
||||
frameworkData[data.type.toLowerCase()] = data
|
||||
}
|
||||
|
||||
//setFrameworkLoaded(true)
|
||||
//setFrameworkData(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
setDiscoveryData({})
|
||||
if (setFrameworkData !== undefined) {
|
||||
setFrameworkData(frameworkData)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!window.location.pathname.includes("usecases")) {
|
||||
@@ -1058,7 +1066,6 @@ const AppFramework = (props) => {
|
||||
frameworkData[keys[key]] = submitValue
|
||||
}
|
||||
|
||||
console.log("Frameworkdata: ", frameworkData)
|
||||
setFrameworkData(frameworkData)
|
||||
|
||||
if (discoveryData.large_image !== undefined && discoveryData.large_image !== null && discoveryData.large_image.includes("storage.googleapis.com")) {
|
||||
@@ -1076,8 +1083,6 @@ const AppFramework = (props) => {
|
||||
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
|
||||
const imgSize = 50;
|
||||
var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData
|
||||
|
||||
@@ -1263,7 +1268,7 @@ const AppFramework = (props) => {
|
||||
|
||||
var parsedStyle2 = {
|
||||
"ghost": "yes",
|
||||
"border-width": "7px",
|
||||
"border-width": "7px",
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1399,7 +1404,6 @@ const AppFramework = (props) => {
|
||||
|
||||
const onNodeSelect = (event) => {
|
||||
var data = event.target.data();
|
||||
console.log("Node: ", data)
|
||||
if (data.id === "SHUFFLE") {
|
||||
event.target.unselect()
|
||||
return
|
||||
@@ -1465,6 +1469,15 @@ const AppFramework = (props) => {
|
||||
|
||||
const foundMiddleImage = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : '/images/Shuffle_logo.png'
|
||||
|
||||
const siemcheck = parsedFrameworkData.SIEM.large_image === undefined || (parsedFrameworkData.SIEM.name !== undefined && parsedFrameworkData.SIEM.name !== null && parsedFrameworkData.SIEM.name.includes(":default"))
|
||||
const iamcheck = parsedFrameworkData.IAM.large_image === undefined || (parsedFrameworkData.IAM.name !== undefined && parsedFrameworkData.IAM.name !== null && parsedFrameworkData.IAM.name.includes(":default"))
|
||||
const casescheck = parsedFrameworkData.Cases.large_image === undefined || (parsedFrameworkData.Cases.name !== undefined && parsedFrameworkData.Cases.name !== null && parsedFrameworkData.Cases.name.includes(":default"))
|
||||
const assetscheck = parsedFrameworkData.Assets.large_image === undefined || (parsedFrameworkData.Assets.name !== undefined && parsedFrameworkData.Assets.name !== null && parsedFrameworkData.Assets.name.includes(":default"))
|
||||
const intelcheck = parsedFrameworkData.Intel.large_image === undefined || (parsedFrameworkData.Intel.name !== undefined && parsedFrameworkData.Intel.name !== null && parsedFrameworkData.Intel.name.includes(":default"))
|
||||
const commscheck = parsedFrameworkData.Comms.large_image === undefined || (parsedFrameworkData.Comms.name !== undefined && parsedFrameworkData.Comms.name !== null && parsedFrameworkData.Comms.name.includes(":default"))
|
||||
const edrcheck = parsedFrameworkData["EDR & AV"].large_image === undefined || (parsedFrameworkData["EDR & AV"].name !== undefined && parsedFrameworkData["EDR & AV"].name !== null && parsedFrameworkData["EDR & AV"].name.includes(":default"))
|
||||
const networkcheck = parsedFrameworkData.Network.large_image === undefined || (parsedFrameworkData.Network.name !== undefined && parsedFrameworkData.Network.name !== null && parsedFrameworkData.Network.name.includes(":default"))
|
||||
|
||||
|
||||
const fontSize = `${12*scale}px`
|
||||
const defaultSize = `${85*scale}px`
|
||||
@@ -1484,11 +1497,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.Cases.name === undefined ? "" : parsedFrameworkData.Cases.name,
|
||||
description: parsedFrameworkData.Cases.description === undefined ? "" : parsedFrameworkData.Cases.description,
|
||||
app_id: parsedFrameworkData.Cases.id === undefined ? "" : parsedFrameworkData.Cases.id,
|
||||
text_margin_y: parsedFrameworkData.Cases.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.Cases.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.Cases.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: casescheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: casescheck ? `${32*scale}px` : "0px",
|
||||
margin_y: casescheck ? `${19*scale}px` : `0px`,
|
||||
width: casescheck ? iconSize : defaultSize,
|
||||
height: casescheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image,
|
||||
|
||||
label: securityFramework[0].text.toUpperCase(),
|
||||
@@ -1501,6 +1514,7 @@ const AppFramework = (props) => {
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
group: "nodes",
|
||||
data: {
|
||||
font_size: fontSize,
|
||||
@@ -1512,11 +1526,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.IAM.name === undefined ? "" : parsedFrameworkData.IAM.name,
|
||||
description: parsedFrameworkData.IAM.description === undefined ? "" : parsedFrameworkData.IAM.description,
|
||||
app_id: parsedFrameworkData.IAM.id === undefined ? "" : parsedFrameworkData.IAM.id,
|
||||
text_margin_y: parsedFrameworkData.IAM.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.IAM.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.IAM.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: iamcheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: iamcheck ? `${32*scale}px` : "0px",
|
||||
margin_y: iamcheck ? `${19*scale}px` : `0px`,
|
||||
width: iamcheck ? iconSize : defaultSize,
|
||||
height: iamcheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image,
|
||||
|
||||
label: securityFramework[3].text.toUpperCase(),
|
||||
@@ -1540,11 +1554,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.Assets.name === undefined ? "" : parsedFrameworkData.Assets.name,
|
||||
description: parsedFrameworkData.Assets.description === undefined ? "" : parsedFrameworkData.Assets.description,
|
||||
app_id: parsedFrameworkData.Assets.id === undefined ? "" : parsedFrameworkData.Assets.id,
|
||||
text_margin_y: parsedFrameworkData.Assets.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.Assets.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.Assets.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: assetscheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: assetscheck ? `${32*scale}px` : "0px",
|
||||
margin_y: assetscheck ? `${19*scale}px` : `0px`,
|
||||
width: assetscheck ? iconSize : defaultSize,
|
||||
height: assetscheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image,
|
||||
|
||||
label: securityFramework[2].text.toUpperCase(),
|
||||
@@ -1568,11 +1582,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.Intel.name === undefined ? "" : parsedFrameworkData.Intel.name,
|
||||
description: parsedFrameworkData.Intel.description === undefined ? "" : parsedFrameworkData.Intel.description,
|
||||
app_id: parsedFrameworkData.Intel.id === undefined ? "" : parsedFrameworkData.Intel.id,
|
||||
text_margin_y: parsedFrameworkData.Intel.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.Intel.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.Intel.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: intelcheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: intelcheck ? `${32*scale}px` : "0px",
|
||||
margin_y: intelcheck ? `${19*scale}px` : `0px`,
|
||||
width: intelcheck ? iconSize : defaultSize,
|
||||
height: intelcheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image,
|
||||
|
||||
label: securityFramework[4].text.toUpperCase(),
|
||||
@@ -1596,11 +1610,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.Comms.name === undefined ? "" : parsedFrameworkData.Comms.name,
|
||||
description: parsedFrameworkData.Comms.description === undefined ? "" : parsedFrameworkData.Comms.description,
|
||||
app_id: parsedFrameworkData.Comms.id === undefined ? "" : parsedFrameworkData.Comms.id,
|
||||
text_margin_y: parsedFrameworkData.Comms.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.Comms.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.Comms.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: commscheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: commscheck ? `${32*scale}px` : "0px",
|
||||
margin_y: commscheck ? `${19*scale}px` : `0px`,
|
||||
width: commscheck ? iconSize : defaultSize,
|
||||
height: commscheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image,
|
||||
|
||||
label: securityFramework[5].text.toUpperCase(),
|
||||
@@ -1624,11 +1638,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData["EDR & AV"].name === undefined ? "" : parsedFrameworkData["EDR & AV"].name,
|
||||
description: parsedFrameworkData["EDR & AV"].description === undefined ? "" : parsedFrameworkData["EDR & AV"].description,
|
||||
app_id: parsedFrameworkData["EDR & AV"].id === undefined ? "" : parsedFrameworkData["EDR & AV"].id,
|
||||
text_margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: edrcheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: edrcheck ? `${32*scale}px` : "0px",
|
||||
margin_y: edrcheck ? `${19*scale}px` : `0px`,
|
||||
width: edrcheck ? iconSize : defaultSize,
|
||||
height: edrcheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image,
|
||||
|
||||
label: securityFramework[7].text.toUpperCase(),
|
||||
@@ -1652,11 +1666,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.Network.name === undefined ? "" : parsedFrameworkData.Network.name,
|
||||
description: parsedFrameworkData.Network.description === undefined ? "" : parsedFrameworkData.Network.description,
|
||||
app_id: parsedFrameworkData.Network.id === undefined ? "" : parsedFrameworkData.Network.id,
|
||||
text_margin_y: parsedFrameworkData.Network.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.Network.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.Network.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: networkcheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: networkcheck ? `${32*scale}px` : "0px",
|
||||
margin_y: networkcheck ? `${19*scale}px` : `0px`,
|
||||
width: networkcheck ? iconSize : defaultSize,
|
||||
height: networkcheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image,
|
||||
label: securityFramework[6].text.toUpperCase(),
|
||||
id: securityFramework[6].text.toUpperCase(),
|
||||
@@ -1679,11 +1693,11 @@ const AppFramework = (props) => {
|
||||
name: parsedFrameworkData.SIEM.name === undefined ? "" : parsedFrameworkData.SIEM.name,
|
||||
description: parsedFrameworkData.SIEM.description === undefined ? "" : parsedFrameworkData.SIEM.description,
|
||||
app_id: parsedFrameworkData.SIEM.id === undefined ? "" : parsedFrameworkData.SIEM.id,
|
||||
text_margin_y: parsedFrameworkData.SIEM.large_image === undefined ? textMarginDefault : textMarginImage,
|
||||
margin_x: parsedFrameworkData.SIEM.large_image === undefined ? `${32*scale}px` : "0px",
|
||||
margin_y: parsedFrameworkData.SIEM.large_image === undefined ? `${19*scale}px` : `0px`,
|
||||
width: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize,
|
||||
height: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize,
|
||||
text_margin_y: siemcheck ? textMarginDefault : textMarginImage,
|
||||
margin_x: siemcheck ? `${32*scale}px` : "0px",
|
||||
margin_y: siemcheck ? `${19*scale}px` : `0px`,
|
||||
width: siemcheck ? iconSize : defaultSize,
|
||||
height: siemcheck ? iconSize : defaultSize,
|
||||
large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image,
|
||||
label: securityFramework[1].text.toUpperCase(),
|
||||
id: securityFramework[1].text.toUpperCase(),
|
||||
@@ -1988,6 +2002,7 @@ const AppFramework = (props) => {
|
||||
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
||||
return (
|
||||
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
||||
{/*
|
||||
<div style={{position: "absolute"}}>
|
||||
|
||||
<SuggestedWorkflows
|
||||
@@ -1999,9 +2014,11 @@ const AppFramework = (props) => {
|
||||
inputSearch={changedApp}
|
||||
apps={apps}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{injectedApps.map((apps, appindex) => {
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{/*injectedApps.map((apps, appindex) => {
|
||||
var categoryTop = 100
|
||||
var categoryLeft = 100
|
||||
|
||||
@@ -2028,8 +2045,11 @@ const AppFramework = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const scale = 0.9
|
||||
const offsetTop = -70
|
||||
const offsetLeft = 0
|
||||
return (
|
||||
<div key={appindex} style={{display: "flex", position: "absolute", top: categoryTop, left: categoryLeft, zIndex: 10010, }}>
|
||||
<div key={appindex} style={{display: "flex", position: "absolute", top: categoryTop+offsetTop, left: categoryLeft+offsetLeft, zIndex: 10010, }}>
|
||||
{apps.map((app, appIndex) => {
|
||||
return (
|
||||
<Chip
|
||||
@@ -2059,7 +2079,6 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
if (setFrameworkData !== undefined) {
|
||||
console.log("Setting frameworkdata")
|
||||
// Find discoveryData.id
|
||||
var keys = []
|
||||
for (const [key, value] of Object.entries(frameworkData)) {
|
||||
@@ -2088,7 +2107,7 @@ const AppFramework = (props) => {
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
})*/}
|
||||
|
||||
{showOptions === false ? null :
|
||||
<div style={{textAlign: "center",}}>
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
} from '@mui/material';
|
||||
|
||||
const AppSearchButtons = (props) => {
|
||||
const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props
|
||||
const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, getAppFramework, } = props
|
||||
|
||||
const ref = useRef()
|
||||
let navigate = useNavigate();
|
||||
@@ -48,8 +48,6 @@ const AppSearchButtons = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (newSelectedApp !== undefined && setMissing != undefined) {
|
||||
console.log("AppSearchButtons: setMissing is defined!")
|
||||
|
||||
const submitAppFramework = {
|
||||
"description": newSelectedApp.description,
|
||||
"id": newSelectedApp.objectID,
|
||||
@@ -60,11 +58,17 @@ const AppSearchButtons = (props) => {
|
||||
|
||||
setFrameworkItem(submitAppFramework)
|
||||
setMissing(newSelectedApp)
|
||||
|
||||
if (getAppFramework !== undefined) {
|
||||
setTimeout(() => {
|
||||
getAppFramework()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}, [newSelectedApp])
|
||||
|
||||
if (appType === undefined) {
|
||||
console.log("Apptype is required in AppSearchButtons")
|
||||
//console.log("Apptype is required in AppSearchButtons")
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ const ConfigureWorkflow = (props) => {
|
||||
const [checkStarted, setCheckStarted] = React.useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Required actions: ", requiredActions)
|
||||
if (requiredActions.length === 0) {
|
||||
console.log("No more actions? Set parent to done?")
|
||||
|
||||
if (setConfigurationFinished !== undefined) {
|
||||
setConfigurationFinished(true)
|
||||
}
|
||||
@@ -705,9 +702,9 @@ const ConfigureWorkflow = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
toast("Failed to set app authentication: " + responseJson.reason);
|
||||
} else {
|
||||
toast("App auth set for app " + app.name.replace("_", " "));
|
||||
toast("App authentication set for app " + app.name.replace("_", " "));
|
||||
setFinalized(true)
|
||||
setOpened(false)
|
||||
}
|
||||
@@ -731,6 +728,7 @@ const ConfigureWorkflow = (props) => {
|
||||
|
||||
parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
|
||||
|
||||
console.log("AUTH Action: ", action)
|
||||
return (
|
||||
<ListItem
|
||||
style={{padding: 0, display: "flex", flexDirection: "column", }}
|
||||
@@ -744,6 +742,25 @@ const ConfigureWorkflow = (props) => {
|
||||
<div style={{display: "flex", }}
|
||||
onClick={() => {
|
||||
setOpened(!opened);
|
||||
|
||||
// Scroll to it
|
||||
const element = document.getElementById("app-config");
|
||||
if (element) {
|
||||
// Scroll down 100px
|
||||
setTimeout(() => {
|
||||
element.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
top: 100,
|
||||
})
|
||||
|
||||
//element.scrollIntoView({
|
||||
// behavior: "smooth",
|
||||
// block: "center",
|
||||
// inline: "center"
|
||||
//});
|
||||
}, 250)
|
||||
}
|
||||
|
||||
}}
|
||||
>
|
||||
<div style={{display: "flex", flex: 7, }}>
|
||||
@@ -1318,7 +1335,7 @@ const ConfigureWorkflow = (props) => {
|
||||
</Typography>
|
||||
: null}
|
||||
|
||||
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 250, }}>
|
||||
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 350, }}>
|
||||
{requiredActions.map((data, index) => {
|
||||
|
||||
// AppWrapper = Default in a workflow, only shows with steps
|
||||
|
||||
@@ -146,8 +146,6 @@ const EditWorkflow = (props) => {
|
||||
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
console.log("PRIO: ", priority)
|
||||
|
||||
var upload = "";
|
||||
var total_count = 0
|
||||
|
||||
@@ -485,7 +483,7 @@ const EditWorkflow = (props) => {
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions style={{paddingRight: 100, }}>
|
||||
<DialogActions style={{paddingRight: 100, marginBottom: 100, }}>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
|
||||
@@ -16,6 +16,11 @@ import {
|
||||
Divider,
|
||||
Select,
|
||||
MenuItem,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
@@ -30,9 +35,8 @@ import {
|
||||
Add as AddIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
//import { useAlert
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
|
||||
import theme from "../theme.jsx";
|
||||
|
||||
const Files = (props) => {
|
||||
@@ -45,6 +49,13 @@ const Files = (props) => {
|
||||
const [fileContent, setFileContent] = React.useState("");
|
||||
const [openEditor, setOpenEditor] = React.useState(false);
|
||||
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
||||
const [loadFileModalOpen, setLoadFileModalOpen] = React.useState(false);
|
||||
|
||||
const [field1, setField1] = React.useState("");
|
||||
const [field2, setField2] = React.useState("");
|
||||
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards")
|
||||
const [downloadBranch, setDownloadBranch] = React.useState("main");
|
||||
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
|
||||
|
||||
//const alert = useAlert();
|
||||
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"]
|
||||
@@ -67,7 +78,7 @@ const Files = (props) => {
|
||||
|
||||
}
|
||||
|
||||
const runUpdateText = (text) =>{
|
||||
const runUpdateText = (text) =>{
|
||||
fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
@@ -76,17 +87,34 @@ const Files = (props) => {
|
||||
},
|
||||
body:text,
|
||||
credentials: "include",
|
||||
}).then((response) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Can't update file");
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
//console.log(text);
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
toast("Successfully updated file");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast("Error updating file: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
const getFiles = () => {
|
||||
fetch(globalUrl + "/api/v1/files", {
|
||||
const getFiles = (namespace) => {
|
||||
var parsedurl = `${globalUrl}/api/v1/files`
|
||||
if (namespace === undefined || namespace === "default") {
|
||||
|
||||
} else if (namespace !== undefined && namespace !== null && namespace !== "") {
|
||||
parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true`
|
||||
} else if (selectedNamespace !== undefined && selectedNamespace !== null && selectedNamespace !== "default" && selectedNamespace !== "") {
|
||||
parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedNamespace}?ids=true`
|
||||
}
|
||||
|
||||
fetch(parsedurl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -104,12 +132,23 @@ const Files = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.files !== undefined && responseJson.files !== null) {
|
||||
setFiles(responseJson.files);
|
||||
} else {
|
||||
setFiles(responseJson.files);
|
||||
} else if (responseJson.list !== undefined && responseJson.list !== null) {
|
||||
// Set the "namespace" field in all items
|
||||
if (namespace !== undefined && namespace !== null) {
|
||||
responseJson.list.forEach((item) => {
|
||||
item.namespace = namespace
|
||||
item.filename = item.name
|
||||
item.workflow_id = "global"
|
||||
})
|
||||
}
|
||||
|
||||
setFiles(responseJson.list);
|
||||
} else {
|
||||
setFiles([]);
|
||||
}
|
||||
|
||||
if (responseJson.namespaces !== undefined && responseJson.namespaces !== null) {
|
||||
if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileNamespaces.length === 0 || responseJson.namespaces.length > fileNamespaces.length)) {
|
||||
setFileNamespaces(responseJson.namespaces);
|
||||
}
|
||||
})
|
||||
@@ -119,9 +158,214 @@ const Files = (props) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getFiles();
|
||||
getFiles(selectedNamespace)
|
||||
}, []);
|
||||
|
||||
const importStandardsFromUrl = (url, folder) => {
|
||||
if (url === undefined || url === null || url.length < 5) {
|
||||
toast("Please enter a valid URL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (folder === undefined || folder === null || folder.length < 2) {
|
||||
toast("Please enter a valid folder name")
|
||||
return
|
||||
}
|
||||
|
||||
const parsedData = {
|
||||
url: url,
|
||||
path: folder,
|
||||
field_3: downloadBranch || "master",
|
||||
};
|
||||
|
||||
if (field1.length > 0) {
|
||||
parsedData["field_1"] = field1;
|
||||
}
|
||||
|
||||
if (field2.length > 0) {
|
||||
parsedData["field_2"] = field2;
|
||||
}
|
||||
|
||||
toast(`Getting files from url ${url}. This may take a while if the repository is large. Please wait...`);
|
||||
fetch(globalUrl + "/api/v1/files/download_remote", {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(parsedData),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
toast("Successfully loaded files from " + downloadUrl);
|
||||
setLoadFileModalOpen(false);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
} else {
|
||||
toast("Failed loading");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const handleGithubValidation = () => {
|
||||
importStandardsFromUrl(downloadUrl, downloadFolder);
|
||||
}
|
||||
|
||||
const fileDownloadModal = loadFileModalOpen ?
|
||||
<Dialog
|
||||
open={loadFileModalOpen}
|
||||
onClose={() => {}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Load Files from Github
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Files will be loaded from the repository and branch you specify, with the focus on files in one folder at a time. This is NOT recursive.
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
Repository URL (supported: github, gitlab, bitbucket)
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadUrl}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadUrl(e.target.value)}
|
||||
placeholder="https://github.com/shuffle/standards"
|
||||
fullWidth
|
||||
/>
|
||||
<div style={{ display: "flex" }}>
|
||||
<span>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Branch (default value is "main"):
|
||||
</span>
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadBranch}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadBranch(e.target.value)}
|
||||
placeholder="master"
|
||||
fullWidth
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Folder (can use / for subfolders):
|
||||
</span>
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadFolder}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadFolder(e.target.value)}
|
||||
placeholder="translation_standards"
|
||||
fullWidth
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Authentication (optional - private repos etc):
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setField1(e.target.value)}
|
||||
type="username"
|
||||
placeholder="Username / APIkey (optional)"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setField2(e.target.value)}
|
||||
type="password"
|
||||
placeholder="Password (optional)"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => setLoadFileModalOpen(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")}
|
||||
onClick={() => {
|
||||
handleGithubValidation();
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
: null
|
||||
|
||||
const deleteFile = (file) => {
|
||||
fetch(globalUrl + "/api/v1/files/" + file.id, {
|
||||
method: "DELETE",
|
||||
@@ -140,7 +384,7 @@ const Files = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success) {
|
||||
toast("Successfully deleted file " + file.name);
|
||||
toast("Successfully deleted file")
|
||||
} else if (
|
||||
responseJson.reason !== undefined &&
|
||||
responseJson.reason !== null
|
||||
@@ -254,7 +498,7 @@ const Files = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateFile = (filename, file) => {
|
||||
const handleCreateFile = (filename, file) => {
|
||||
var data = {
|
||||
filename: filename,
|
||||
org_id: selectedOrganization.id,
|
||||
@@ -355,7 +599,7 @@ const Files = (props) => {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
getFiles();
|
||||
getFiles()
|
||||
}, 2500);
|
||||
};
|
||||
|
||||
@@ -378,7 +622,21 @@ const Files = (props) => {
|
||||
}}
|
||||
onDrop={uploadFile}
|
||||
>
|
||||
<div>
|
||||
<div style={{position: "relative"}}>
|
||||
|
||||
<Tooltip color="primary" title={"Import files to Shuffle from Git"} placement="top">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
style={{position: "absolute", right: 0, top: 0, }}
|
||||
variant="text"
|
||||
onClick={() => setLoadFileModalOpen(true)}
|
||||
>
|
||||
<CloudDownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
{fileDownloadModal}
|
||||
|
||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||
<h2 style={{ display: "inline" }}>Files</h2>
|
||||
<span style={{ marginLeft: 25 }}>
|
||||
@@ -393,6 +651,9 @@ const Files = (props) => {
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
@@ -426,6 +687,8 @@ const Files = (props) => {
|
||||
<CachedIcon />
|
||||
</Button>
|
||||
|
||||
|
||||
|
||||
{fileNamespaces !== undefined &&
|
||||
fileNamespaces !== null &&
|
||||
fileNamespaces.length > 1 ? (
|
||||
@@ -442,8 +705,13 @@ const Files = (props) => {
|
||||
}}
|
||||
value={selectedNamespace}
|
||||
onChange={(event) => {
|
||||
console.log("CHANGE NAMESPACE: ", event.target);
|
||||
setSelectedNamespace(event.target.value);
|
||||
|
||||
if (event.target.value === "all" || event.target.value === "default") {
|
||||
getFiles()
|
||||
} else {
|
||||
getFiles(event.target.value)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{fileNamespaces.map((data, index) => {
|
||||
@@ -453,7 +721,7 @@ const Files = (props) => {
|
||||
value={data}
|
||||
style={{ color: "white" }}
|
||||
>
|
||||
{data}
|
||||
{data.replaceAll("_", " ")}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
@@ -487,6 +755,8 @@ const Files = (props) => {
|
||||
<AddIcon/>
|
||||
</Button>
|
||||
</Tooltip> }
|
||||
|
||||
|
||||
{renderTextBox && <TextField
|
||||
onKeyPress={(event)=>{
|
||||
handleKeyDown(event);
|
||||
@@ -504,7 +774,7 @@ const Files = (props) => {
|
||||
autoFocus
|
||||
/>}</div>
|
||||
|
||||
<CodeEditor
|
||||
<ShuffleCodeEditor
|
||||
isCloud={isCloud}
|
||||
expansionModalOpen={openEditor}
|
||||
setExpansionModalOpen={setOpenEditor}
|
||||
@@ -602,7 +872,7 @@ const Files = (props) => {
|
||||
/>
|
||||
<ListItemText
|
||||
primary={
|
||||
file.workflow_id === "global" ? (
|
||||
file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ?
|
||||
<IconButton
|
||||
disabled={file.workflow_id === "global"}
|
||||
>
|
||||
@@ -615,7 +885,7 @@ const Files = (props) => {
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
) : (
|
||||
: (
|
||||
<Tooltip
|
||||
title={"Go to workflow"}
|
||||
style={{}}
|
||||
|
||||
@@ -88,12 +88,6 @@ import {
|
||||
SquareFoot as SquareFootIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
//import CodeMirror from "@uiw/react-codemirror";
|
||||
//import "codemirror/keymap/sublime";
|
||||
//import "codemirror/theme/gruvbox-dark.css";
|
||||
//import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
borderColor: "#f85a3e !important",
|
||||
@@ -1040,22 +1034,28 @@ const ParsedAction = (props) => {
|
||||
};
|
||||
|
||||
|
||||
const returnHelperText = (name, value) => {
|
||||
if (name === undefined || value === undefined) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var helperText = ""
|
||||
//console.log("DATA: ", name, value)
|
||||
if (name.includes("url")) {
|
||||
if (value.includes("localhost") || value.includes("127.0.0.1")) {
|
||||
helperText = "Can't use localhost. Please change to an external IP or hostname."
|
||||
}
|
||||
}
|
||||
|
||||
return helperText
|
||||
const returnHelperText = (name, value) => {
|
||||
if (name === undefined || value === undefined || name === null || value === null || name === "" || value === "") {
|
||||
//console.log("RETURNING EMPTY")
|
||||
return ""
|
||||
}
|
||||
|
||||
var helperText = ""
|
||||
//console.log("DATA: ", name, value)
|
||||
if (name.includes("url")) {
|
||||
if (value.includes("localhost") || value.includes("127.0.0.1")) {
|
||||
helperText = "Can't use localhost. Please change to your external IP."
|
||||
}
|
||||
}
|
||||
|
||||
// Helpertext for openapi fields
|
||||
//if (helperText === "" && name === "body" && selectedApp.generated && selectedApp.activated) {
|
||||
// helperText = <span style={{color: "white", marginBottom: 5, marginleft: 5}}>
|
||||
//}
|
||||
|
||||
return helperText
|
||||
}
|
||||
|
||||
//console.log("AUTH: ", authenticationType)
|
||||
if (authenticationType !== undefined && authenticationType !== null && authenticationType.type === "oauth2") {
|
||||
/*
|
||||
@@ -1297,17 +1297,13 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
|
||||
var placeholder = "Value";
|
||||
if (
|
||||
data.example !== undefined &&
|
||||
data.example !== null &&
|
||||
data.example.length > 0
|
||||
) {
|
||||
if (data.example !== undefined && data.example !== null && data.example.length > 0) {
|
||||
|
||||
placeholder = data.example;
|
||||
|
||||
if (data.name === "url" && data.value !== undefined && data.value !== null && data.value.length === 0) {
|
||||
data.value = data.example;
|
||||
}
|
||||
|
||||
// In case of data.example
|
||||
if (data.value === undefined || data.value === null) {
|
||||
data.value = ""
|
||||
@@ -1642,7 +1638,6 @@ const ParsedAction = (props) => {
|
||||
),
|
||||
}}
|
||||
multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline}
|
||||
helperText={returnHelperText(data.name, data.value)}
|
||||
onClick={() => {
|
||||
/*
|
||||
setExpansionModalOpen(false);
|
||||
@@ -1680,18 +1675,7 @@ const ParsedAction = (props) => {
|
||||
//changeActionParameterCodemirror(event, count, data)
|
||||
changeActionParameter(event, count, data);
|
||||
}}
|
||||
helperText={baseHelperText.length > 0 ? baseHelperText :
|
||||
selectedApp.generated &&
|
||||
selectedApp.activated &&
|
||||
data.name === "body" ? (
|
||||
<span
|
||||
style={{ color: "white", marginBottom: 5, marginleft: 5 }}
|
||||
>
|
||||
{openApiHelperText}
|
||||
</span>
|
||||
) : data.name.startsWith("${") && data.name.endsWith("}") ?
|
||||
null : null
|
||||
}
|
||||
helperText={returnHelperText(data.name, data.value)}
|
||||
onBlur={(event) => {
|
||||
baseHelperText = calculateHelpertext(event.target.value)
|
||||
if (setLastSaved !== undefined) {
|
||||
|
||||
@@ -67,9 +67,15 @@ const Priorities = (props) => {
|
||||
})
|
||||
}
|
||||
|
||||
const dismissNotification = (alert_id) => {
|
||||
// Don't really care about the logout
|
||||
fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, {
|
||||
const dismissNotification = (alert_id, disabled) => {
|
||||
var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread`
|
||||
if (disabled === true) {
|
||||
notificationurl += "?disabled=true"
|
||||
} else if (disabled === false) {
|
||||
notificationurl += "?disabled=false"
|
||||
}
|
||||
|
||||
fetch(notificationurl , {
|
||||
credentials: "include",
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -85,12 +91,50 @@ const Priorities = (props) => {
|
||||
})
|
||||
.then(function (responseJson) {
|
||||
if (responseJson.success === true) {
|
||||
const newNotifications = notifications.filter(
|
||||
(data) => data.id !== alert_id
|
||||
);
|
||||
console.log("NEW NOTIFICATIONS: ", newNotifications);
|
||||
// Mark current one as read
|
||||
var newNotifications = notifications.map((notification) => {
|
||||
if (notification.id === alert_id) {
|
||||
notification.read = true
|
||||
}
|
||||
|
||||
if (setNotifications !== undefined) {
|
||||
return notification
|
||||
})
|
||||
|
||||
|
||||
if (disabled === true) {
|
||||
toast("Notification disabled, and will not be shown again.")
|
||||
|
||||
newNotifications = newNotifications.map((notification) => {
|
||||
if (notification.id === alert_id) {
|
||||
notification.ignored = true
|
||||
}
|
||||
|
||||
return notification
|
||||
})
|
||||
|
||||
console.log("NEW NOTIFICATIONS: ", newNotifications);
|
||||
} else if (disabled === false) {
|
||||
toast("Notification re-enabled successfully")
|
||||
|
||||
newNotifications = newNotifications.map((notification) => {
|
||||
if (notification.id === alert_id) {
|
||||
notification.ignored = false
|
||||
}
|
||||
|
||||
return notification
|
||||
})
|
||||
|
||||
} else {
|
||||
toast("Notification dismissed successfully")
|
||||
}
|
||||
|
||||
//const newNotifications = notifications.filter(
|
||||
// (data) => data.id !== alert_id
|
||||
//)
|
||||
|
||||
//console.log("NEW NOTIFICATIONS: ", newNotifications);
|
||||
|
||||
if (setNotifications !== undefined && newNotifications !== undefined) {
|
||||
setNotifications(newNotifications)
|
||||
}
|
||||
} else {
|
||||
@@ -174,6 +218,14 @@ const Priorities = (props) => {
|
||||
style={{marginRight: 15, height: 25, }}
|
||||
/>
|
||||
: null}
|
||||
{data.ignored === true ?
|
||||
<Chip
|
||||
label={"Disabled"}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
style={{marginRight: 15, height: 25, }}
|
||||
/>
|
||||
: null}
|
||||
{data.read === false ?
|
||||
<Chip
|
||||
label={"Unread"}
|
||||
@@ -227,6 +279,22 @@ const Priorities = (props) => {
|
||||
Dismiss
|
||||
</Button>
|
||||
) : null}
|
||||
<Tooltip title="Disabling a notification makes it so similar notifications to this one will NOT be re-opened. It will NOT forward notifications to your notification workflow, but WILL still keep counting." placement="top">
|
||||
<Button
|
||||
color="secondary"
|
||||
variant={data.ignored === true ? "contained" : "outlined"}
|
||||
style={{ marginTop: 15, }}
|
||||
onClick={() => {
|
||||
if (data.ignored === true) {
|
||||
dismissNotification(data.id, false)
|
||||
} else {
|
||||
dismissNotification(data.id, true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{data.ignored === true ? "Re-enable" : "Disable"}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
|
||||
<Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}>
|
||||
|
||||
@@ -32,7 +32,6 @@ const Priority = (props) => {
|
||||
let newdescription = priority.description
|
||||
const descsplit = priority.description.split("&")
|
||||
if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) {
|
||||
console.log("descsplit: ", descsplit)
|
||||
if (descsplit[1] === "") {
|
||||
const item = findSpecificApp(appFramework, descsplit[0])
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ import cystyle from "../defaultCytoscapeStyle.jsx";
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
const CytoscapeWrapper = (props) => {
|
||||
const { globalUrl, inworkflow } = props;
|
||||
const { globalUrl, inworkflow, height, width } = props;
|
||||
|
||||
const [elements, setElements] = useState([]);
|
||||
const [workflow, setWorkflow] = useState(inworkflow);
|
||||
const [cy, setCy] = React.useState();
|
||||
const bodyWidth = 200;
|
||||
const bodyHeight = 150;
|
||||
const bodyWidth = height === undefined ? 1000 : width
|
||||
const bodyHeight = width === undefined ? 1000 : height
|
||||
|
||||
const setupGraph = () => {
|
||||
const actions = workflow.actions.map((action) => {
|
||||
@@ -76,29 +76,6 @@ const CytoscapeWrapper = (props) => {
|
||||
};
|
||||
|
||||
// This is an attempt at prettier edges. The numbers are weird to work with.
|
||||
/*
|
||||
//http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html
|
||||
const sourcenode = actions.find(node => node.data._id === branch.source_id)
|
||||
const destinationnode = actions.find(node => node.data._id === branch.destination_id)
|
||||
if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) {
|
||||
//node.data._id = action["id"]
|
||||
console.log("SOURCE: ", sourcenode.position)
|
||||
console.log("DESTINATIONNODE: ", destinationnode.position)
|
||||
|
||||
var opposite = true
|
||||
if (sourcenode.position.x > destinationnode.position.x) {
|
||||
opposite = false
|
||||
} else {
|
||||
opposite = true
|
||||
}
|
||||
|
||||
edge.style = {
|
||||
'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"],
|
||||
'control-point-weight': ['0.3', '0.7'],
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return edge;
|
||||
});
|
||||
|
||||
@@ -135,9 +112,10 @@ const CytoscapeWrapper = (props) => {
|
||||
elements={elements}
|
||||
minZoom={0.35}
|
||||
maxZoom={2.0}
|
||||
zoom={1.0}
|
||||
style={{
|
||||
width: bodyWidth - 15,
|
||||
height: bodyHeight - 5,
|
||||
width: bodyWidth,
|
||||
height: bodyHeight,
|
||||
backgroundColor: surfaceColor,
|
||||
}}
|
||||
stylesheet={cystyle}
|
||||
|
||||
@@ -75,9 +75,15 @@ const RuntimeDebugger = (props) => {
|
||||
|
||||
setTotalCount(0)
|
||||
|
||||
var starttime = ""
|
||||
var endtime = ""
|
||||
var count = 0
|
||||
var starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString()
|
||||
var endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString()
|
||||
try {
|
||||
starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString()
|
||||
endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString()
|
||||
} catch (e) {
|
||||
toast("Invalid date format", { type: "error" })
|
||||
}
|
||||
|
||||
var maxworkflows = 5
|
||||
|
||||
@@ -88,6 +94,11 @@ const RuntimeDebugger = (props) => {
|
||||
}
|
||||
|
||||
const workflowId = workflows[key].id
|
||||
|
||||
if (workflowId === undefined || workflowId === null || workflowId === "") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Fetch the data for the workflow
|
||||
var url = `${globalUrl}/api/v1/workflows/${workflowId}/executions/count`
|
||||
if (starttime !== "") {
|
||||
@@ -133,7 +144,6 @@ const RuntimeDebugger = (props) => {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Setting total count: ", count)
|
||||
setTotalCount(count)
|
||||
}, maxworkflows*300)
|
||||
}
|
||||
@@ -578,7 +588,7 @@ const RuntimeDebugger = (props) => {
|
||||
onClick={() => {
|
||||
window.open(`${globalUrl}/api/v1/workflows/search/${params.row.id}`, "_blank")
|
||||
}}
|
||||
disabled={!userdata.support}
|
||||
disabled={userdata.region_url !== "https://shuffler.io"}
|
||||
>
|
||||
<InsightsIcon fontSize="small" />
|
||||
</IconButton>
|
||||
|
||||
@@ -57,7 +57,6 @@ import { padding, textAlign } from '@mui/system';
|
||||
import data from '../frameworkStyle.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
|
||||
|
||||
|
||||
@@ -84,37 +83,6 @@ const pythonFilters = [
|
||||
{"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``},
|
||||
]
|
||||
|
||||
/*
|
||||
const shuffleTheme = createTheme({
|
||||
theme: 'dark',
|
||||
settings: {
|
||||
background: "rgba(40,40,40, 1)",
|
||||
foreground: '#75baff',
|
||||
caret: '#5d00ff',
|
||||
selection: '#036dd626',
|
||||
selectionMatch: '#036dd626',
|
||||
lineHighlight: '#8a91991a',
|
||||
gutterForeground: '#8a919966',
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.comment, color: '#787b8099' },
|
||||
{ tag: t.variableName, color: '#0080ff' },
|
||||
{ tag: [t.string, t.special(t.brace)], color: '#5c6166' },
|
||||
{ tag: t.number, color: '#5c6166' },
|
||||
{ tag: t.bool, color: '#5c6166' },
|
||||
{ tag: t.null, color: '#5c6166' },
|
||||
{ tag: t.keyword, color: '#5c6166' },
|
||||
{ tag: t.operator, color: '#5c6166' },
|
||||
{ tag: t.className, color: '#5c6166' },
|
||||
{ tag: t.definition(t.typeName), color: '#5c6166' },
|
||||
{ tag: t.typeName, color: '#5c6166' },
|
||||
{ tag: t.angleBracket, color: '#5c6166' },
|
||||
{ tag: t.tagName, color: '#5c6166' },
|
||||
{ tag: t.attributeName, color: '#5c6166' },
|
||||
],
|
||||
});
|
||||
*/
|
||||
|
||||
const CodeEditor = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
|
||||
@@ -5,6 +5,7 @@ import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import AppSearchButtons from "../components/AppSearchButtons.jsx";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import RenderCytoscape from "../components/RenderCytoscape.jsx";
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
@@ -29,7 +30,7 @@ import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
|
||||
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
|
||||
|
||||
const WorkflowTemplatePopup = (props) => {
|
||||
const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage } = props;
|
||||
const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage, getAppFramework, } = props;
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
@@ -39,17 +40,41 @@ const WorkflowTemplatePopup = (props) => {
|
||||
const [workflow, setWorkflow] = useState({});
|
||||
const [showLoginButton, setShowLoginButton] = useState(false);
|
||||
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
||||
|
||||
const [missingSource, setMissingSource] = React.useState(undefined)
|
||||
const [missingDestination, setMissingDestination] = React.useState(undefined);
|
||||
|
||||
const [configurationFinished, setConfigurationFinished] = React.useState(false);
|
||||
const [appSetupDone, setAppSetupDone] = React.useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
}, [missingSource, missingDestination])
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const [requestSent, setRequestSent] = React.useState(false)
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
let navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
if (modalOpen !== true) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!srcapp.includes(":default") && !dstapp.includes(":default")) {
|
||||
if (appSetupDone === false && setAppSetupDone !== undefined) {
|
||||
setAppSetupDone(true)
|
||||
}
|
||||
|
||||
getGeneratedWorkflow()
|
||||
}
|
||||
|
||||
if (missingSource !== undefined && missingDestination !== undefined) {
|
||||
if (appSetupDone === false && setAppSetupDone !== undefined) {
|
||||
setAppSetupDone(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (getAppFramework !== undefined) {
|
||||
setTimeout(() => {
|
||||
getAppFramework()
|
||||
}, 500)
|
||||
}
|
||||
}, [modalOpen, missingSource, missingDestination])
|
||||
|
||||
|
||||
const imagestyleWrapper = {
|
||||
height: 40,
|
||||
@@ -272,6 +297,9 @@ const WorkflowTemplatePopup = (props) => {
|
||||
// middle:[]
|
||||
// name: "Email analysis"
|
||||
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
|
||||
if (requestSent === true) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (srcapp.includes(":default") || dstapp.includes(":default")) {
|
||||
@@ -309,9 +337,8 @@ const WorkflowTemplatePopup = (props) => {
|
||||
},
|
||||
}
|
||||
|
||||
setRequestSent(true)
|
||||
const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge`
|
||||
//const url = `https://shuffler.io/api/v1/workflows/merge`
|
||||
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -324,6 +351,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
//console.log("Status not 200 for framework!");
|
||||
setRequestSent(false)
|
||||
}
|
||||
|
||||
setWorkflowLoading(false)
|
||||
@@ -341,6 +369,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
|
||||
if (responseJson.success === false) {
|
||||
//console.log("Error in workflow template: ", responseJson.error);
|
||||
setRequestSent(false)
|
||||
|
||||
const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance."
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
|
||||
@@ -366,10 +395,21 @@ const WorkflowTemplatePopup = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setRequestSent(false)
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
if (modalOpen === true && !srcapp.includes(":default") && !dstapp.includes(":default")) {
|
||||
if (appSetupDone === false && setAppSetupDone !== undefined) {
|
||||
setAppSetupDone(true)
|
||||
}
|
||||
|
||||
if (workflow.id === undefined && workflowLoading === false && errorMessage === "") {
|
||||
getGeneratedWorkflow()
|
||||
}
|
||||
}
|
||||
|
||||
const isFinished = () => {
|
||||
// Look for configuration fields being done in the current modal
|
||||
// 1. Start by finding the modal
|
||||
@@ -388,6 +428,13 @@ const WorkflowTemplatePopup = (props) => {
|
||||
}
|
||||
|
||||
const ModalView = () => {
|
||||
if (modalOpen === false) {
|
||||
return null
|
||||
}
|
||||
|
||||
const divHeight = 500
|
||||
const divWidth = 500
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
anchor={"left"}
|
||||
@@ -475,9 +522,9 @@ const WorkflowTemplatePopup = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
{(missingSource !== undefined || missingDestination !== undefined) ?
|
||||
{(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ?
|
||||
<Typography variant="body1" style={{marginTop: 75, marginBottom: 10, }}>
|
||||
{"Find relevevant Apps for this Usecase"}
|
||||
{"Find relevant Apps for this Usecase"}
|
||||
</Typography>
|
||||
: null}
|
||||
|
||||
@@ -491,6 +538,8 @@ const WorkflowTemplatePopup = (props) => {
|
||||
AppImage={missingSource.image}
|
||||
|
||||
setMissing={setMissingSource}
|
||||
|
||||
getAppFramework={getAppFramework}
|
||||
/>
|
||||
</div>
|
||||
: null}
|
||||
@@ -505,6 +554,8 @@ const WorkflowTemplatePopup = (props) => {
|
||||
AppImage={missingDestination.image}
|
||||
|
||||
setMissing={setMissingDestination}
|
||||
|
||||
getAppFramework={getAppFramework}
|
||||
/>
|
||||
</div>
|
||||
: null}
|
||||
@@ -521,12 +572,29 @@ const WorkflowTemplatePopup = (props) => {
|
||||
setConfigurationFinished={setConfigurationFinished}
|
||||
/>
|
||||
|
||||
|
||||
{/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id !== "" ?
|
||||
<div style={{position: "fixed", right: "5%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", height: divHeight, width: divWidth, borderRadius: theme.palette.borderRadius, }}>
|
||||
<RenderCytoscape
|
||||
inworkflow={workflow}
|
||||
height={divHeight}
|
||||
width={divWidth}
|
||||
/>
|
||||
</div>
|
||||
: null*/}
|
||||
|
||||
{errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
|
||||
<Tooltip title="Workflow generated!" placement="top">
|
||||
<span style={{display: "flex", }}>
|
||||
<Tooltip title="Click to explore the workflow" placement="top">
|
||||
<span
|
||||
style={{position: "fixed", display: "flex", right: "10%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: "15px 30px 15px 30px", backgroundColor: theme.palette.platformColor, cursor: "pointer", }}
|
||||
onClick={() => {
|
||||
// Open in new tab
|
||||
window.open("/workflows/" + workflow.id, "_blank")
|
||||
}}
|
||||
>
|
||||
{/*<CheckIcon color="primary" sx={{ borderRadius: 4 }} /> */}
|
||||
<Typography variant="h6" style={{ marginLeft: 20, }}>
|
||||
Workflow generated!
|
||||
<Typography variant="h5" style={{ }}>
|
||||
Workflow Successfully Generated!
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -648,6 +716,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import { makeStyles } from "@mui/styles";
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import countries from "../components/Countries.jsx";
|
||||
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import getLocalCodeData from "../components/ShuffleCodeEditor.jsx";
|
||||
import CacheView from "../components/CacheView.jsx";
|
||||
|
||||
import {
|
||||
@@ -1867,16 +1865,24 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
{selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginBottom: 0, marginTop: 10 }}>
|
||||
Only the name of the auth can be modified for Oauth2. Please remake the authentication to change the fields like Client ID, Secret, Scopes etc.
|
||||
Only the name and url can be modified for Oauth2/OpenID connect. Please remake the authentication if you want to change the other fields like Client ID, Secret, Scopes etc.
|
||||
</Typography>
|
||||
</div>
|
||||
:
|
||||
selectedAuthentication.fields.map((data, index) => {
|
||||
: null }
|
||||
|
||||
{selectedAuthentication.fields.map((data, index) => {
|
||||
var fieldname = data.key.replaceAll("_", " ")
|
||||
if (fieldname.endsWith(" basic")) {
|
||||
fieldname = fieldname.substring(0, fieldname.length - 6)
|
||||
}
|
||||
|
||||
if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") {
|
||||
if (selectedAuthentication.fields[index].key !== "url") {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//console.log("DATA: ", data, selectedAuthentication)
|
||||
return (
|
||||
<div key={index}>
|
||||
@@ -3415,24 +3421,26 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
// using request id or trace id
|
||||
<ListItem key={index} style={{ backgroundColor: index % 2 === 0 ? "#1f2023" : "#27292d" }}>
|
||||
<ListItemText
|
||||
primary={new Date(data.start_time.seconds * 1000).toISOString().slice(0, 10)}
|
||||
primary={new Date(data.start_time.seconds * 1000).toLocaleString('en-US', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
})}
|
||||
style={{
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
minWidth: 200,
|
||||
maxWidth: 200,
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={data.status}
|
||||
primary={data.referrer}
|
||||
style={{
|
||||
minWidth: 70,
|
||||
maxWidth: 70,
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={data.method}
|
||||
style={{
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
minWidth: 300,
|
||||
maxWidth: 300,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
@@ -3441,6 +3449,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
minWidth: 700,
|
||||
maxWidth: 700,
|
||||
overflow: "hidden",
|
||||
marginLeft: 10,
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
@@ -4406,7 +4415,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Default"
|
||||
style={{ minWidth: 100, maxWidth: 100 }}
|
||||
style={{ minWidth: 110, maxWidth: 110, }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Actions"
|
||||
@@ -4550,8 +4559,8 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
/>
|
||||
<ListItemText
|
||||
style={{
|
||||
minWidth: 80,
|
||||
maxWidth: 80,
|
||||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
primary={environment.default ? "true" : null}
|
||||
@@ -4559,7 +4568,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
{environment.default ? null : (
|
||||
<Button
|
||||
variant="outlined"
|
||||
style={{ marginLeft: 0, marginRight: 0, }}
|
||||
style={{ marginLeft: 0, marginRight: 0, maxWidth: 150, }}
|
||||
onClick={() => setDefaultEnvironment(environment)}
|
||||
color="primary"
|
||||
>
|
||||
|
||||
@@ -121,7 +121,7 @@ import * as edgehandles from "cytoscape-edgehandles";
|
||||
import CytoscapeComponent from "react-cytoscapejs";
|
||||
import Draggable from "react-draggable";
|
||||
import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
|
||||
|
||||
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
|
||||
@@ -13366,6 +13366,67 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Link>
|
||||
<h2 style={{ margin: 0 }}>{workflow.name}</h2>
|
||||
</Breadcrumbs>
|
||||
|
||||
{workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id ? null :
|
||||
<Typography variant="body1">
|
||||
<b>Warning</b>: Change <span
|
||||
style={{color: "#f85a3e", cursor: "pointer"}}
|
||||
onClick={() => {
|
||||
toast("Changing to correct organization. Please wait a few seconds.")
|
||||
|
||||
localStorage.setItem("globalUrl", "");
|
||||
localStorage.setItem("getting_started_sidebar", "open");
|
||||
fetch(`${globalUrl}/api/v1/orgs/${workflow.org_id}/change`, {
|
||||
mode: "cors",
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
method: "POST",
|
||||
body: JSON.stringify({"org_id": workflow.org_id}),
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.status !== 200) {
|
||||
console.log("Error in response");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(function (responseJson) {
|
||||
console.log("In here?")
|
||||
if (responseJson.success === true) {
|
||||
if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) {
|
||||
console.log("Region Change: ", responseJson.region_url);
|
||||
localStorage.setItem("globalUrl", responseJson.region_url);
|
||||
//globalUrl = responseJson.region_url
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
|
||||
toast("Successfully changed active organization - refreshing!");
|
||||
} else {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
|
||||
toast(responseJson.reason);
|
||||
} else {
|
||||
toast("Failed changing org. Try again or contact support@shuffler.io if this persists.");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error changing: ", error);
|
||||
//removeCookie("session_token", {path: "/"})
|
||||
})
|
||||
|
||||
|
||||
|
||||
}}
|
||||
>Active Organization</span> to edit this Workflow.
|
||||
</Typography>
|
||||
}
|
||||
</div>
|
||||
<div style={{display: "flex", marginLeft: 10, }}>
|
||||
{parentWorkflows.slice(0,5).map((wf, index) => {
|
||||
@@ -13744,9 +13805,64 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</div>
|
||||
: null
|
||||
|
||||
|
||||
|
||||
const RightsideBar = () => {
|
||||
const [hovered, setHovered] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === '/') {
|
||||
event.preventDefault(); // Prevent default browser behavior (like opening search bar)
|
||||
if (!workflow.public && !executionRequestStarted) {
|
||||
executeWorkflow(executionText, workflow.start, lastSaved);
|
||||
}
|
||||
}
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === "'") {
|
||||
// Check if Ctrl (Windows/Linux) or Command (Mac) key is pressed along with '/'
|
||||
if (!workflow.public && !executionModalOpen) {
|
||||
setExecutionModalOpen(true);
|
||||
getWorkflowExecution(props.match.params.key, "");
|
||||
} else if (!workflow.public && executionModalOpen) {
|
||||
setExecutionModalOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === "]") {
|
||||
console.log("Show workflow revisions key pressed")
|
||||
if (!workflow.public) {
|
||||
setShowWorkflowRevisions(true)
|
||||
setSelectedRevision(workflow)
|
||||
//setOriginalWorkflow(workflow)
|
||||
}
|
||||
}
|
||||
|
||||
if (( event.ctrlKey || event.metaKey ) && event.key === ";") {
|
||||
if (!workflow.public && executionModalOpen) {
|
||||
getWorkflowExecution(props.match.params.key, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (( event.ctrlKey || event.metaKey ) && event.shiftKey) {
|
||||
console.log("Shift key pressed")
|
||||
if (!workflow.public && executionModalOpen) {
|
||||
setExecutionRunning(false);
|
||||
stop()
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
const newitem = removeParam("execution_id", cursearch);
|
||||
navigate(curpath + newitem)
|
||||
setExecutionModalView(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -13802,7 +13918,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip color="primary" title="Test Execution" placement="top">
|
||||
<Tooltip color="primary" title="Test Execution (Ctrl + /)" placement="top">
|
||||
<span>
|
||||
<Button
|
||||
disabled={
|
||||
@@ -14000,7 +14116,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={`Show executions (${workflowExecutions.length})`}
|
||||
title={`Show executions (${workflowExecutions.length}) (Ctrl + ')`}
|
||||
placement="top-start"
|
||||
>
|
||||
<span>
|
||||
@@ -14069,7 +14185,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title="Show Workflow Revision History (Beta)"
|
||||
title="Show Workflow Revision History (Beta) (Ctrl + ])"
|
||||
placement="top"
|
||||
>
|
||||
<span>
|
||||
@@ -14080,8 +14196,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
variant={"outlined"}
|
||||
onClick={() => {
|
||||
setShowWorkflowRevisions(true)
|
||||
setSelectedRevision(workflow)
|
||||
//setOriginalWorkflow(workflow)
|
||||
setSelectedRevision(workflow)
|
||||
//setOriginalWorkflow(workflow)
|
||||
}}
|
||||
>
|
||||
<RestoreIcon />
|
||||
@@ -14283,6 +14399,52 @@ const AngularWorkflow = (defaultprops) => {
|
||||
//return null;
|
||||
};
|
||||
|
||||
const unPublishWorkflow = (data) => {
|
||||
if (!isCloud) {
|
||||
toast("Function only supported on cloud")
|
||||
return
|
||||
}
|
||||
|
||||
if (data.public !== true) {
|
||||
toast("Workflow is not public. Can't unpublish");
|
||||
return
|
||||
}
|
||||
|
||||
// This ALWAYS talks to Shuffle cloud
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
const url = `${globalUrl}/api/v1/workflows/${data.id}/unpublish`;
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflow publish :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Unpublishing: "+responseJson.reason)
|
||||
}
|
||||
|
||||
if (responseJson.success === true) {
|
||||
workflow.public = false
|
||||
setWorkflow(workflow)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast("Failed publishing: is the workflow valid? Remember to save the workflow first.")
|
||||
console.log(error.toString())
|
||||
})
|
||||
}
|
||||
|
||||
// This can execute a workflow with firestore. Used for test, as datastore is old and stuff
|
||||
// Too much work to move everything over alone, so won't touch it for now
|
||||
//<Button style={{borderRadius: "0px"}} color="primary" variant="contained" onClick={() => {
|
||||
@@ -14538,8 +14700,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
disabled
|
||||
variant="outlined"
|
||||
disabled={workflow.public !== true}
|
||||
fullWidth
|
||||
style={{ marginTop: 15, }}
|
||||
onClick={() => {
|
||||
@@ -14547,6 +14709,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
// workflow.public = false
|
||||
// setWorkflow(workflow)
|
||||
// setUpdate(Math.random());
|
||||
toast("Unpublishing this workflow from the search engine. It will remain public until it is deleted, as it is a copy of the original workflow.")
|
||||
unPublishWorkflow(workflow)
|
||||
}}
|
||||
>
|
||||
Unpublish Workflow
|
||||
@@ -15112,6 +15276,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip title="Refresh runs (Ctrl + ;)" arrow>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
variant="outlined"
|
||||
@@ -15124,6 +15289,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
<CachedIcon style={{ marginRight: 10 }} />
|
||||
Refresh Runs
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Divider
|
||||
style={{
|
||||
backgroundColor: "rgba(255,255,255,0.5)",
|
||||
@@ -15351,48 +15517,51 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ padding: isMobile ? "0px 10px 25px 10px" : "25px 15px 25px 15px", maxWidth: isMobile ? "100%" : 400, overflowX: "hidden" }}>
|
||||
<Breadcrumbs
|
||||
aria-label="breadcrumb"
|
||||
separator="›"
|
||||
style={{ color: "white", fontSize: 16 }}
|
||||
>
|
||||
<span
|
||||
style={{ color: "rgba(255,255,255,0.5)", display: "flex" }}
|
||||
onClick={() => {
|
||||
setExecutionRunning(false);
|
||||
stop();
|
||||
getWorkflowExecution(props.match.params.key, "");
|
||||
setExecutionModalView(0);
|
||||
setLastExecution(executionData.execution_id);
|
||||
}}
|
||||
|
||||
<Breadcrumbs
|
||||
aria-label="breadcrumb"
|
||||
separator="›"
|
||||
style={{ color: "white", fontSize: 16 }}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
onClick={() => {
|
||||
setExecutionRunning(false);
|
||||
stop()
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon style={{ color: "rgba(255,255,255,0.5)" }} />
|
||||
</IconButton>
|
||||
<h2
|
||||
style={{ color: "rgba(255,255,255,0.5)", cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
const newitem = removeParam("execution_id", cursearch);
|
||||
navigate(curpath + newitem)
|
||||
setExecutionRunning(false);
|
||||
stop()
|
||||
}}
|
||||
>
|
||||
See more runs
|
||||
</h2>
|
||||
</span>
|
||||
</Breadcrumbs>
|
||||
<span
|
||||
style={{ color: "rgba(255,255,255,0.5)", display: "flex" }}
|
||||
onClick={() => {
|
||||
setExecutionRunning(false);
|
||||
stop();
|
||||
getWorkflowExecution(props.match.params.key, "");
|
||||
setExecutionModalView(0);
|
||||
setLastExecution(executionData.execution_id);
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
onClick={() => {
|
||||
setExecutionRunning(false);
|
||||
stop()
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon style={{ color: "rgba(255,255,255,0.5)" }} />
|
||||
</IconButton>
|
||||
<Tooltip title="See more runs (Ctrl + Shift)" arrow>
|
||||
<h2
|
||||
style={{ color: "rgba(255,255,255,0.5)", cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
const newitem = removeParam("execution_id", cursearch);
|
||||
navigate(curpath + newitem)
|
||||
setExecutionRunning(false);
|
||||
stop()
|
||||
}}
|
||||
>
|
||||
See more runs
|
||||
</h2>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</Breadcrumbs>
|
||||
<Divider
|
||||
style={{
|
||||
backgroundColor: "rgba(255,255,255,0.6)",
|
||||
@@ -15456,15 +15625,19 @@ const AngularWorkflow = (defaultprops) => {
|
||||
color="primary"
|
||||
title="Explore logs for the workflow"
|
||||
placement="top"
|
||||
style={{ zIndex: 50000 }}
|
||||
style={{ zIndex: 50000, }}
|
||||
>
|
||||
<span style={{}}>
|
||||
<Button
|
||||
color="primary"
|
||||
style={{ float: "right", marginTop: 20, marginLeft: 10 }}
|
||||
disabled={!userdata.support}
|
||||
disabled={userdata.region_url !== "https://shuffler.io"}
|
||||
onClick={() => {
|
||||
window.open(`/api/v1/workflows/search/${executionData.execution_id}`, "_blank")
|
||||
toast("Opening logs in a new tab")
|
||||
|
||||
setTimeout(() => {
|
||||
window.open(`/api/v1/workflows/search/${executionData.execution_id}`, "_blank")
|
||||
}, 250)
|
||||
}}
|
||||
>
|
||||
<InsightsIcon color="secondary" />
|
||||
@@ -17855,42 +18028,45 @@ const AngularWorkflow = (defaultprops) => {
|
||||
return null
|
||||
}
|
||||
|
||||
var newrevision = JSON.parse(JSON.stringify(revision))
|
||||
|
||||
// Make unix timestamp into ISO timestamp in the format July 27th, 3:05 AM
|
||||
// Format: July 27th, 3:05 AM
|
||||
console.log("Edited time: ", revision.edited)
|
||||
//console.log("Edited time: ", revision.edited)
|
||||
// Convert 1692128391 to valid timestamp
|
||||
const validTimestamp = revision.edited.toString().length === 10 ? revision.edited * 1000 : revision.edited
|
||||
const validTimestamp = newrevision.edited.toString().length === 10 ? newrevision.edited * 1000 : newrevision.edited
|
||||
const translatedDate = new Date(validTimestamp).toLocaleString('en-US', { month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
||||
|
||||
var workflowStatus = revision.status !== undefined && revision.status !== null && revision.status !== "" ? revision.status : "test"
|
||||
if (revision.name !== undefined && revision.name !== null && revision.name !== "") {
|
||||
if (revision.name.toLowerCase().includes("test")) {
|
||||
var workflowStatus = newrevision.status !== undefined && newrevision.status !== null && newrevision.status !== "" ? newrevision.status : "test"
|
||||
if (newrevision.name !== undefined && newrevision.name !== null && newrevision.name !== "") {
|
||||
if (newrevision.name.toLowerCase().includes("test")) {
|
||||
workflowStatus = "test"
|
||||
}
|
||||
|
||||
if (revision.name.toLowerCase().includes("dev") || revision.name.toLowerCase().includes("staging") || revision.name.toLowerCase().includes("rollback")) {
|
||||
if (newrevision.name.toLowerCase().includes("dev") || newrevision.name.toLowerCase().includes("staging") || newrevision.name.toLowerCase().includes("rollback")) {
|
||||
workflowStatus = "dev"
|
||||
}
|
||||
|
||||
if (revision.name.toLowerCase().includes("prod") || revision.name.toLowerCase().includes("main")) {
|
||||
if (newrevision.name.toLowerCase().includes("prod") || newrevision.name.toLowerCase().includes("main")) {
|
||||
workflowStatus = "prod"
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper
|
||||
style={{padding: "15px 15px 15px 25px", minHeight: 105, maxHeight: 105, cursor: "pointer", backgroundColor: revision.edited === selectedRevision.edited ? "rgba(255,255,255,0.3)" : theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.3)", marginBottom: 10,
|
||||
style={{padding: "15px 15px 15px 25px", minHeight: 105, maxHeight: 105, cursor: "pointer", backgroundColor: newrevision.edited === selectedRevision.edited ? "rgba(255,255,255,0.3)" : theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.3)", marginBottom: 10,
|
||||
}} onClick={(e) => {
|
||||
if (revision.edited === selectedRevision.edited) {
|
||||
if (newrevision.edited === selectedRevision.edited) {
|
||||
console.log("Same revision! No setting.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Should render if it's not the same as workflow.edited
|
||||
console.log("Clicked revision: ", revision)
|
||||
console.log("Clicked revision: ", newrevision)
|
||||
setLastSaved(false)
|
||||
setSelectedRevision(revision)
|
||||
setWorkflow(revision)
|
||||
setSelectedRevision(newrevision)
|
||||
setWorkflow(newrevision)
|
||||
setSelectedAction({});
|
||||
setSelectedApp({})
|
||||
|
||||
@@ -17910,7 +18086,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
cy.removeListener("cxttap");
|
||||
}
|
||||
|
||||
setupGraph(revision)
|
||||
setupGraph(newrevision)
|
||||
|
||||
// Re-adding cytoscape triggers
|
||||
if (cy !== undefined && cy !== null) {
|
||||
@@ -17936,8 +18112,20 @@ const AngularWorkflow = (defaultprops) => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Need to run through graph setup with this one
|
||||
}}>
|
||||
<div style={{display: "flex", }}>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import { makeStyles, } from "@mui/styles";
|
||||
// nodejs library that concatenates classes
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
//import { useAlert
|
||||
import React, { useState, useEffect } from "react"
|
||||
import { useInterval } from "react-powerhooks"
|
||||
import AppFramework from "../components/AppFramework.jsx"
|
||||
import { makeStyles, } from "@mui/styles"
|
||||
import classNames from "classnames"
|
||||
import theme from '../theme.jsx'
|
||||
import { useNavigate, Link, useParams } from "react-router-dom"
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { parsedDatatypeImages } from "../components/AppFramework.jsx"
|
||||
import { findSpecificApp } from "../components/AppFramework.jsx"
|
||||
@@ -96,7 +91,8 @@ const useStyles = makeStyles({
|
||||
|
||||
|
||||
const UsecaseListComponent = (props) => {
|
||||
const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, } = props
|
||||
const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, getFramework, setFrameworkData, } = props
|
||||
|
||||
|
||||
const [expandedIndex, setExpandedIndex] = useState(-1);
|
||||
const [expandedItem, setExpandedItem] = useState(-1);
|
||||
@@ -114,11 +110,13 @@ const UsecaseListComponent = (props) => {
|
||||
const [firstLoad, setFirstLoad] = useState(true)
|
||||
const [apps, setApps] = useState([])
|
||||
|
||||
|
||||
const classes = useStyles();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [mitreTags, setMitreTags] = useState([]);
|
||||
|
||||
|
||||
const parseUsecase = (subcase) => {
|
||||
const srcdata = findSpecificApp(frameworkData, subcase.type)
|
||||
const dstdata = findSpecificApp(frameworkData, subcase.last)
|
||||
@@ -137,13 +135,11 @@ const UsecaseListComponent = (props) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("In frameworkData useEffect: frameworkData: ", frameworkData)
|
||||
//console.log("Frameworkdata changed. Use to set inputUsecase: ", frameworkData, prevSubcase)
|
||||
if (frameworkData === undefined || prevSubcase === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("PAST!")
|
||||
|
||||
var parsedUsecase = inputUsecase
|
||||
const subcase = parseUsecase(prevSubcase)
|
||||
|
||||
@@ -917,6 +913,9 @@ const UsecaseListComponent = (props) => {
|
||||
description={inputUsecase.description}
|
||||
|
||||
apps={apps}
|
||||
getAppFramework={getFramework}
|
||||
//appSetupDone={appSetupDone}
|
||||
//setAppSetupDone={setAppSetupDone}
|
||||
/>
|
||||
: null}
|
||||
</span>
|
||||
@@ -990,13 +989,15 @@ const UsecaseListComponent = (props) => {
|
||||
}}>
|
||||
<AppFramework
|
||||
inputUsecase={inputUsecase}
|
||||
frameworkData={frameworkData}
|
||||
selectedOption={"Draw"}
|
||||
showOptions={false}
|
||||
isLoaded={true}
|
||||
isLoggedIn={true}
|
||||
globalUrl={globalUrl}
|
||||
size={0.6}
|
||||
|
||||
frameworkData={frameworkData}
|
||||
setFrameworkData={setFrameworkData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1713,12 +1714,15 @@ const Dashboard = (props) => {
|
||||
<UsecaseListComponent
|
||||
userdata={userdata}
|
||||
isLoggedIn={isLoggedIn}
|
||||
frameworkData={frameworkData}
|
||||
keys={selectedUsecases}
|
||||
isCloud={isCloud}
|
||||
globalUrl={globalUrl}
|
||||
workflows={workflows}
|
||||
setWorkflows={setWorkflows}
|
||||
|
||||
frameworkData={frameworkData}
|
||||
setFrameworkData={setFrameworkData}
|
||||
getFramework={getFramework}
|
||||
/>
|
||||
|
||||
{treeKeys.length > 0 ?
|
||||
|
||||
@@ -13,6 +13,11 @@ import {
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
|
||||
import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
|
||||
const Settings = (props) => {
|
||||
const { globalUrl, isLoaded, userdata, setUserData } = props;
|
||||
//const alert = useAlert();
|
||||
@@ -44,6 +49,17 @@ const Settings = (props) => {
|
||||
|
||||
const [userSettings, setUserSettings] = useState({});
|
||||
|
||||
const [showApiKey, setShowApiKey] = useState(false);
|
||||
const [apiKeyCopied, setApiKeyCopied] = useState(false);
|
||||
|
||||
const handleCopyApiKey = () => {
|
||||
navigator.clipboard.writeText(userSettings.apikey);
|
||||
setApiKeyCopied(true);
|
||||
setTimeout(() => {
|
||||
setApiKeyCopied(false);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/*
|
||||
const [userdata.eth_info, setEthInfo] = useState(userdata.eth_info !== undefined && userdata.eth_info.account !== undefined && userdata.eth_info.account.length > 0 ? userdata.eth_info : {
|
||||
"account": "",
|
||||
@@ -467,7 +483,7 @@ const Settings = (props) => {
|
||||
>
|
||||
What is the API key used for?
|
||||
</a>
|
||||
<TextField
|
||||
{/* <TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor, flex: "1" }}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -484,7 +500,43 @@ const Settings = (props) => {
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
/>
|
||||
/> */}
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor, flex: "1" }}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: "50px",
|
||||
color: "white",
|
||||
},
|
||||
endAdornment: (
|
||||
<>
|
||||
<Tooltip title={showApiKey ? "Hide API Key" : "Show API Key"}>
|
||||
<IconButton
|
||||
onClick={() => setShowApiKey(!showApiKey)}
|
||||
>
|
||||
{showApiKey ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={apiKeyCopied ? "API Key copied!" : "Copy API Key"}>
|
||||
<IconButton
|
||||
onClick={handleCopyApiKey}
|
||||
>
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
color="primary"
|
||||
value={showApiKey ? userSettings.apikey : '*'.repeat(36)} // Show API key if showApiKey is true, else show asterisks
|
||||
required
|
||||
disabled
|
||||
fullWidth
|
||||
placeholder="APIKEY"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
/>
|
||||
<Button
|
||||
style={{ width: "100%", height: "40px", marginTop: "10px" }}
|
||||
variant="outlined"
|
||||
@@ -676,8 +728,13 @@ const Settings = (props) => {
|
||||
Submit password change
|
||||
</Button>
|
||||
<h3>{passwordFormMessage}</h3>
|
||||
<Divider style={{ marginTop: "40px" }} />
|
||||
<h2>Creator Incentive Program</h2>
|
||||
{isCloud && (
|
||||
<>
|
||||
<Divider style={{ marginTop: "40px" }} />
|
||||
<h2>Creator Incentive Program</h2>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div style={{ display: runFlex ? "flex" : "", width: "100%" }}>
|
||||
<div>
|
||||
{isCloud ?
|
||||
|
||||
@@ -551,9 +551,7 @@ const Workflows = (props) => {
|
||||
|
||||
const [field1, setField1] = React.useState("");
|
||||
const [field2, setField2] = React.useState("");
|
||||
const [downloadUrl, setDownloadUrl] = React.useState(
|
||||
"https://github.com/frikky/shuffle-workflows"
|
||||
);
|
||||
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows")
|
||||
const [downloadBranch, setDownloadBranch] = React.useState("master");
|
||||
const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] =
|
||||
React.useState(false);
|
||||
@@ -924,7 +922,7 @@ const Workflows = (props) => {
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}? <div />
|
||||
Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId)?.name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}? <div />
|
||||
|
||||
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
|
||||
</div>
|
||||
@@ -3630,7 +3628,7 @@ const Workflows = (props) => {
|
||||
const handleGithubValidation = () => {
|
||||
importWorkflowsFromUrl(downloadUrl);
|
||||
setLoadWorkflowsModalOpen(false);
|
||||
};
|
||||
}
|
||||
|
||||
const workflowDownloadModalOpen = loadWorkflowsModalOpen ? (
|
||||
<Dialog
|
||||
@@ -3677,11 +3675,11 @@ const Workflows = (props) => {
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadUrl(e.target.value)}
|
||||
placeholder="https://github.com/frikky/shuffle-apps"
|
||||
placeholder="https://github.com/shuffle/workflows"
|
||||
fullWidth
|
||||
/>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Branch (default value is "master"):
|
||||
Branch (default value is "main"):
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
|
||||
Reference in New Issue
Block a user