Merge branch 'Shuffle:1.4.0' into 1.4.0

This commit is contained in:
Hari Krishna
2024-02-23 02:01:56 -08:00
committed by GitHub
22 changed files with 1051 additions and 408 deletions
+1 -3
View File
@@ -1212,7 +1212,7 @@ class AppBase:
return results return results
# Downloads all files from a namespace # Downloads all files from a namespace
# Currently only working on local version of Shuffle # Currently only working on local version of Shuffle (2023)
def get_file_category_ids(self, category): def get_file_category_ids(self, category):
org_id = self.full_execution["workflow"]["execution_org"]["id"] org_id = self.full_execution["workflow"]["execution_org"]["id"]
@@ -1914,7 +1914,6 @@ class AppBase:
c_parentheses = parse_nested_param(parse_string, 0)[0] c_parentheses = parse_nested_param(parse_string, 0)[0]
match_string = re.escape(c_parentheses) match_string = re.escape(c_parentheses)
custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string) custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string)
print("[DEBUG] In ELSE: %s" % custom_casting)
# check if a wrapper was found # check if a wrapper was found
if len(custom_casting) != 0: if len(custom_casting) != 0:
inner_result = parse_type(c_parentheses, custom_casting[0]) inner_result = parse_type(c_parentheses, custom_casting[0])
@@ -2093,7 +2092,6 @@ class AppBase:
except IndexError: except IndexError:
newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:])
else: else:
#print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem))
if isinstance(firstitem, str): if isinstance(firstitem, str):
if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end": if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end":
firstitem = len(basejson)-1 firstitem = len(basejson)-1
+3 -3
View File
@@ -1,6 +1,6 @@
module shuffle-shared module shuffle-shared
replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared //replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
go 1.19 go 1.19
@@ -18,7 +18,7 @@ require (
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/h2non/filetype v1.1.3 github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.5.65 github.com/shuffle/shuffle-shared v0.5.78
golang.org/x/crypto v0.16.0 golang.org/x/crypto v0.16.0
google.golang.org/api v0.125.0 google.golang.org/api v0.125.0
google.golang.org/grpc v1.55.0 google.golang.org/grpc v1.55.0
@@ -52,7 +52,7 @@ require (
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/frikky/schemaless v0.0.5 // indirect github.com/frikky/schemaless v0.0.6 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
+4 -2
View File
@@ -176,8 +176,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
github.com/frikky/schemaless v0.0.5 h1:ptQ0FpQpm/+e7HCYt7Wmc+vcMSelTYCRpKqtLVN2SOY= github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg=
github.com/frikky/schemaless v0.0.5/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw=
@@ -455,6 +455,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE=
github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+1
View File
@@ -4953,6 +4953,7 @@ func initHandlers() {
// PS: For cloud, this has to use cloud storage. // PS: For cloud, this has to use cloud storage.
// https://developer.box.com/reference/get-files-id-content/ // https://developer.box.com/reference/get-files-id-content/
// 1. Creating the "get file" option. Make it possible to run this in the frontend. // 1. Creating the "get file" option. Make it possible to run this in the frontend.
r.HandleFunc("/api/v1/files/download_remote", shuffle.HandleDownloadRemoteFiles).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS")
+3 -1
View File
@@ -552,7 +552,9 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
ctx := context.Background() ctx := context.Background()
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
if err != nil { if err != nil {
log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err) if len(actionResult.ExecutionId) > 0 {
log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err)
}
resp.WriteHeader(401) resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`)))
return return
+141 -122
View File
@@ -40,12 +40,12 @@ cytoscape.use(edgehandles);
export const findSpecificApp = (framework, inputcategory) => { export const findSpecificApp = (framework, inputcategory) => {
// Get the frameworkinfo for the org and fill in // Get the frameworkinfo for the org and fill in
if (framework === undefined || framework === null) { if (framework === undefined || framework === null) {
console.log("findSpecificApp: framework is null") //console.log("findSpecificApp: framework is null")
return null return null
} }
if (inputcategory === undefined || inputcategory === null) { if (inputcategory === undefined || inputcategory === null) {
console.log("findSpecificApp: category is null") //console.log("findSpecificApp: category is null")
return null return null
} }
@@ -118,8 +118,8 @@ export const findSpecificApp = (framework, inputcategory) => {
} }
return { return {
name: "EDR :default", name: "IAM :default",
large_image: parsedDatatypeImages["EDR & AV"], large_image: parsedDatatypeImages["IAM"],
count: 0, count: 0,
description: "", description: "",
id: "", id: "",
@@ -654,7 +654,7 @@ const AppFramework = (props) => {
const handleLoadNextSuggestion = (frameworkData) => { 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", { //fetch(globalUrl + "/api/v1/workflows/usecases", {
//credentials: "include", //credentials: "include",
//cors: "no-cors", //cors: "no-cors",
@@ -720,7 +720,7 @@ const AppFramework = (props) => {
} }
const showRecommendations = (changed, frameworkData) => { const showRecommendations = (changed, frameworkData) => {
console.log("Inside recommendation loader") //console.log("Inside recommendation loader")
setChangedApp(changed) setChangedApp(changed)
handleLoadNextSuggestion(frameworkData) handleLoadNextSuggestion(frameworkData)
@@ -761,7 +761,7 @@ const AppFramework = (props) => {
//console.log("APptype, changed, framework: ", apptype.toLowerCase(), alternativeChanged.toLowerCase(), changed.toLowerCase(), frameworkData) //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())) { if (changed.toLowerCase() === apptype.toLowerCase() || changed.toLowerCase().includes(apptype.toLowerCase()) || alternativeChanged.toLowerCase() === apptype.toLowerCase() || alternativeChanged.toLowerCase().includes(apptype.toLowerCase())) {
potential = true 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) { if (frameworkData[apptype] !== undefined && frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) {
usecase.items[itemtype].app = frameworkData[apptype] usecase.items[itemtype].app = frameworkData[apptype]
@@ -819,10 +819,10 @@ const AppFramework = (props) => {
continue continue
} }
} else { } 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) { if (matches.length === usecase.items.length) {
usecase.color = "#c51152" usecase.color = "#c51152"
usecase.type = usecaseTypes[key].name usecase.type = usecaseTypes[key].name
@@ -833,8 +833,8 @@ const AppFramework = (props) => {
} }
// FIXME: Check if a usecase has already been handled // FIXME: Check if a usecase has already been handled
console.log("") //console.log("")
console.log("GOT USECASES: ", showusecases) //console.log("GOT USECASES: ", showusecases)
// FIXME: Just showing one usecase at a time for now // FIXME: Just showing one usecase at a time for now
if (showusecases.length > 0) { if (showusecases.length > 0) {
@@ -894,38 +894,37 @@ const AppFramework = (props) => {
parsedUsecase.process.push(edge.data) parsedUsecase.process.push(edge.data)
} }
fetch(globalUrl + "/api/v1/workflows/usecases", { fetch(globalUrl + "/api/v1/workflows/usecases", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
body: JSON.stringify(parsedUsecase), body: JSON.stringify(parsedUsecase),
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for framework!"); console.log("Status not 200 for framework!");
} }
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) { if (responseJson.success === false) {
if (responseJson.reason !== undefined) { if (responseJson.reason !== undefined) {
toast("Failed updating: " + responseJson.reason) toast("Failed updating: " + responseJson.reason)
} else {
toast("Failed to update framework for your org.")
}
} else { } else {
toast("Updated usecase.") toast("Failed to update framework for your org.")
} }
}) } else {
.catch((error) => { toast("Updated usecase.")
toast(error.toString()); }
//setFrameworkLoaded(true) })
}) .catch((error) => {
} toast(error.toString());
})
}
const activateApp = (appid) => { const activateApp = (appid) => {
fetch(globalUrl+"/api/v1/apps/"+appid+"/activate", { fetch(globalUrl+"/api/v1/apps/"+appid+"/activate", {
@@ -957,45 +956,54 @@ const AppFramework = (props) => {
} }
const setFrameworkItem = (data) => { const setFrameworkItem = (data) => {
console.log("Setting framework item: ", data, isCloud)
if (!isCloud) { if (!isCloud) {
activateApp(data.id) activateApp(data.id)
} }
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for framework!"); console.log("Status not 200 for framework!");
} }
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) { if (responseJson.success === false) {
if (responseJson.reason !== undefined) { if (responseJson.reason !== undefined) {
toast("Failed updating: " + responseJson.reason) toast("Failed getting appframework: " + responseJson.reason)
} else { } else {
toast("Failed to update framework for your org.") 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) setDiscoveryData({})
//setFrameworkData(responseJson) if (setFrameworkData !== undefined) {
}) setFrameworkData(frameworkData)
.catch((error) => { }
toast(error.toString()); }
//setFrameworkLoaded(true) })
}) .catch((error) => {
} toast(error.toString());
//setFrameworkLoaded(true)
})
}
useEffect(() => { useEffect(() => {
if (!window.location.pathname.includes("usecases")) { if (!window.location.pathname.includes("usecases")) {
@@ -1058,7 +1066,6 @@ const AppFramework = (props) => {
frameworkData[keys[key]] = submitValue frameworkData[keys[key]] = submitValue
} }
console.log("Frameworkdata: ", frameworkData)
setFrameworkData(frameworkData) setFrameworkData(frameworkData)
if (discoveryData.large_image !== undefined && discoveryData.large_image !== null && discoveryData.large_image.includes("storage.googleapis.com")) { 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 isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const imgSize = 50; const imgSize = 50;
var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData
@@ -1263,7 +1268,7 @@ const AppFramework = (props) => {
var parsedStyle2 = { var parsedStyle2 = {
"ghost": "yes", "ghost": "yes",
"border-width": "7px", "border-width": "7px",
} }
/* /*
@@ -1399,7 +1404,6 @@ const AppFramework = (props) => {
const onNodeSelect = (event) => { const onNodeSelect = (event) => {
var data = event.target.data(); var data = event.target.data();
console.log("Node: ", data)
if (data.id === "SHUFFLE") { if (data.id === "SHUFFLE") {
event.target.unselect() event.target.unselect()
return 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 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 fontSize = `${12*scale}px`
const defaultSize = `${85*scale}px` const defaultSize = `${85*scale}px`
@@ -1484,11 +1497,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Cases.name === undefined ? "" : parsedFrameworkData.Cases.name, name: parsedFrameworkData.Cases.name === undefined ? "" : parsedFrameworkData.Cases.name,
description: parsedFrameworkData.Cases.description === undefined ? "" : parsedFrameworkData.Cases.description, description: parsedFrameworkData.Cases.description === undefined ? "" : parsedFrameworkData.Cases.description,
app_id: parsedFrameworkData.Cases.id === undefined ? "" : parsedFrameworkData.Cases.id, app_id: parsedFrameworkData.Cases.id === undefined ? "" : parsedFrameworkData.Cases.id,
text_margin_y: parsedFrameworkData.Cases.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: casescheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.Cases.large_image === undefined ? `${32*scale}px` : "0px", margin_x: casescheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.Cases.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: casescheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize, width: casescheck ? iconSize : defaultSize,
height: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize, height: casescheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image, large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image,
label: securityFramework[0].text.toUpperCase(), label: securityFramework[0].text.toUpperCase(),
@@ -1501,6 +1514,7 @@ const AppFramework = (props) => {
} }
}, },
{ {
group: "nodes", group: "nodes",
data: { data: {
font_size: fontSize, font_size: fontSize,
@@ -1512,11 +1526,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.IAM.name === undefined ? "" : parsedFrameworkData.IAM.name, name: parsedFrameworkData.IAM.name === undefined ? "" : parsedFrameworkData.IAM.name,
description: parsedFrameworkData.IAM.description === undefined ? "" : parsedFrameworkData.IAM.description, description: parsedFrameworkData.IAM.description === undefined ? "" : parsedFrameworkData.IAM.description,
app_id: parsedFrameworkData.IAM.id === undefined ? "" : parsedFrameworkData.IAM.id, app_id: parsedFrameworkData.IAM.id === undefined ? "" : parsedFrameworkData.IAM.id,
text_margin_y: parsedFrameworkData.IAM.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: iamcheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.IAM.large_image === undefined ? `${32*scale}px` : "0px", margin_x: iamcheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.IAM.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: iamcheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize, width: iamcheck ? iconSize : defaultSize,
height: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize, height: iamcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image, large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image,
label: securityFramework[3].text.toUpperCase(), label: securityFramework[3].text.toUpperCase(),
@@ -1540,11 +1554,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Assets.name === undefined ? "" : parsedFrameworkData.Assets.name, name: parsedFrameworkData.Assets.name === undefined ? "" : parsedFrameworkData.Assets.name,
description: parsedFrameworkData.Assets.description === undefined ? "" : parsedFrameworkData.Assets.description, description: parsedFrameworkData.Assets.description === undefined ? "" : parsedFrameworkData.Assets.description,
app_id: parsedFrameworkData.Assets.id === undefined ? "" : parsedFrameworkData.Assets.id, app_id: parsedFrameworkData.Assets.id === undefined ? "" : parsedFrameworkData.Assets.id,
text_margin_y: parsedFrameworkData.Assets.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: assetscheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.Assets.large_image === undefined ? `${32*scale}px` : "0px", margin_x: assetscheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.Assets.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: assetscheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize, width: assetscheck ? iconSize : defaultSize,
height: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize, height: assetscheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image, large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image,
label: securityFramework[2].text.toUpperCase(), label: securityFramework[2].text.toUpperCase(),
@@ -1568,11 +1582,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Intel.name === undefined ? "" : parsedFrameworkData.Intel.name, name: parsedFrameworkData.Intel.name === undefined ? "" : parsedFrameworkData.Intel.name,
description: parsedFrameworkData.Intel.description === undefined ? "" : parsedFrameworkData.Intel.description, description: parsedFrameworkData.Intel.description === undefined ? "" : parsedFrameworkData.Intel.description,
app_id: parsedFrameworkData.Intel.id === undefined ? "" : parsedFrameworkData.Intel.id, app_id: parsedFrameworkData.Intel.id === undefined ? "" : parsedFrameworkData.Intel.id,
text_margin_y: parsedFrameworkData.Intel.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: intelcheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.Intel.large_image === undefined ? `${32*scale}px` : "0px", margin_x: intelcheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.Intel.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: intelcheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize, width: intelcheck ? iconSize : defaultSize,
height: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize, height: intelcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image, large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image,
label: securityFramework[4].text.toUpperCase(), label: securityFramework[4].text.toUpperCase(),
@@ -1596,11 +1610,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Comms.name === undefined ? "" : parsedFrameworkData.Comms.name, name: parsedFrameworkData.Comms.name === undefined ? "" : parsedFrameworkData.Comms.name,
description: parsedFrameworkData.Comms.description === undefined ? "" : parsedFrameworkData.Comms.description, description: parsedFrameworkData.Comms.description === undefined ? "" : parsedFrameworkData.Comms.description,
app_id: parsedFrameworkData.Comms.id === undefined ? "" : parsedFrameworkData.Comms.id, app_id: parsedFrameworkData.Comms.id === undefined ? "" : parsedFrameworkData.Comms.id,
text_margin_y: parsedFrameworkData.Comms.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: commscheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.Comms.large_image === undefined ? `${32*scale}px` : "0px", margin_x: commscheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.Comms.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: commscheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize, width: commscheck ? iconSize : defaultSize,
height: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize, height: commscheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image, large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image,
label: securityFramework[5].text.toUpperCase(), label: securityFramework[5].text.toUpperCase(),
@@ -1624,11 +1638,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData["EDR & AV"].name === undefined ? "" : parsedFrameworkData["EDR & AV"].name, name: parsedFrameworkData["EDR & AV"].name === undefined ? "" : parsedFrameworkData["EDR & AV"].name,
description: parsedFrameworkData["EDR & AV"].description === undefined ? "" : parsedFrameworkData["EDR & AV"].description, description: parsedFrameworkData["EDR & AV"].description === undefined ? "" : parsedFrameworkData["EDR & AV"].description,
app_id: parsedFrameworkData["EDR & AV"].id === undefined ? "" : parsedFrameworkData["EDR & AV"].id, app_id: parsedFrameworkData["EDR & AV"].id === undefined ? "" : parsedFrameworkData["EDR & AV"].id,
text_margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: edrcheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${32*scale}px` : "0px", margin_x: edrcheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${19*scale}px` : `0px`, margin_y: edrcheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize, width: edrcheck ? iconSize : defaultSize,
height: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize, height: edrcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image, large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image,
label: securityFramework[7].text.toUpperCase(), label: securityFramework[7].text.toUpperCase(),
@@ -1652,11 +1666,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Network.name === undefined ? "" : parsedFrameworkData.Network.name, name: parsedFrameworkData.Network.name === undefined ? "" : parsedFrameworkData.Network.name,
description: parsedFrameworkData.Network.description === undefined ? "" : parsedFrameworkData.Network.description, description: parsedFrameworkData.Network.description === undefined ? "" : parsedFrameworkData.Network.description,
app_id: parsedFrameworkData.Network.id === undefined ? "" : parsedFrameworkData.Network.id, app_id: parsedFrameworkData.Network.id === undefined ? "" : parsedFrameworkData.Network.id,
text_margin_y: parsedFrameworkData.Network.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: networkcheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.Network.large_image === undefined ? `${32*scale}px` : "0px", margin_x: networkcheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.Network.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: networkcheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize, width: networkcheck ? iconSize : defaultSize,
height: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize, height: networkcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image, large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image,
label: securityFramework[6].text.toUpperCase(), label: securityFramework[6].text.toUpperCase(),
id: securityFramework[6].text.toUpperCase(), id: securityFramework[6].text.toUpperCase(),
@@ -1679,11 +1693,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.SIEM.name === undefined ? "" : parsedFrameworkData.SIEM.name, name: parsedFrameworkData.SIEM.name === undefined ? "" : parsedFrameworkData.SIEM.name,
description: parsedFrameworkData.SIEM.description === undefined ? "" : parsedFrameworkData.SIEM.description, description: parsedFrameworkData.SIEM.description === undefined ? "" : parsedFrameworkData.SIEM.description,
app_id: parsedFrameworkData.SIEM.id === undefined ? "" : parsedFrameworkData.SIEM.id, app_id: parsedFrameworkData.SIEM.id === undefined ? "" : parsedFrameworkData.SIEM.id,
text_margin_y: parsedFrameworkData.SIEM.large_image === undefined ? textMarginDefault : textMarginImage, text_margin_y: siemcheck ? textMarginDefault : textMarginImage,
margin_x: parsedFrameworkData.SIEM.large_image === undefined ? `${32*scale}px` : "0px", margin_x: siemcheck ? `${32*scale}px` : "0px",
margin_y: parsedFrameworkData.SIEM.large_image === undefined ? `${19*scale}px` : `0px`, margin_y: siemcheck ? `${19*scale}px` : `0px`,
width: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize, width: siemcheck ? iconSize : defaultSize,
height: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize, height: siemcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image, large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image,
label: securityFramework[1].text.toUpperCase(), label: securityFramework[1].text.toUpperCase(),
id: 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 const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
return ( return (
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}> <div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
{/*
<div style={{position: "absolute"}}> <div style={{position: "absolute"}}>
<SuggestedWorkflows <SuggestedWorkflows
@@ -1999,9 +2014,11 @@ const AppFramework = (props) => {
inputSearch={changedApp} inputSearch={changedApp}
apps={apps} apps={apps}
/> />
</div>
{injectedApps.map((apps, appindex) => { </div>
*/}
{/*injectedApps.map((apps, appindex) => {
var categoryTop = 100 var categoryTop = 100
var categoryLeft = 100 var categoryLeft = 100
@@ -2028,8 +2045,11 @@ const AppFramework = (props) => {
return null return null
} }
const scale = 0.9
const offsetTop = -70
const offsetLeft = 0
return ( 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) => { {apps.map((app, appIndex) => {
return ( return (
<Chip <Chip
@@ -2059,7 +2079,6 @@ const AppFramework = (props) => {
} }
if (setFrameworkData !== undefined) { if (setFrameworkData !== undefined) {
console.log("Setting frameworkdata")
// Find discoveryData.id // Find discoveryData.id
var keys = [] var keys = []
for (const [key, value] of Object.entries(frameworkData)) { for (const [key, value] of Object.entries(frameworkData)) {
@@ -2088,7 +2107,7 @@ const AppFramework = (props) => {
})} })}
</div> </div>
) )
})} })*/}
{showOptions === false ? null : {showOptions === false ? null :
<div style={{textAlign: "center",}}> <div style={{textAlign: "center",}}>
+8 -4
View File
@@ -37,7 +37,7 @@ import {
} from '@mui/material'; } from '@mui/material';
const AppSearchButtons = (props) => { 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() const ref = useRef()
let navigate = useNavigate(); let navigate = useNavigate();
@@ -48,8 +48,6 @@ const AppSearchButtons = (props) => {
useEffect(() => { useEffect(() => {
if (newSelectedApp !== undefined && setMissing != undefined) { if (newSelectedApp !== undefined && setMissing != undefined) {
console.log("AppSearchButtons: setMissing is defined!")
const submitAppFramework = { const submitAppFramework = {
"description": newSelectedApp.description, "description": newSelectedApp.description,
"id": newSelectedApp.objectID, "id": newSelectedApp.objectID,
@@ -60,11 +58,17 @@ const AppSearchButtons = (props) => {
setFrameworkItem(submitAppFramework) setFrameworkItem(submitAppFramework)
setMissing(newSelectedApp) setMissing(newSelectedApp)
if (getAppFramework !== undefined) {
setTimeout(() => {
getAppFramework()
}, 1000)
}
} }
}, [newSelectedApp]) }, [newSelectedApp])
if (appType === undefined) { if (appType === undefined) {
console.log("Apptype is required in AppSearchButtons") //console.log("Apptype is required in AppSearchButtons")
return null; return null;
} }
+23 -6
View File
@@ -77,10 +77,7 @@ const ConfigureWorkflow = (props) => {
const [checkStarted, setCheckStarted] = React.useState(false); const [checkStarted, setCheckStarted] = React.useState(false);
useEffect(() => { useEffect(() => {
console.log("Required actions: ", requiredActions)
if (requiredActions.length === 0) { if (requiredActions.length === 0) {
console.log("No more actions? Set parent to done?")
if (setConfigurationFinished !== undefined) { if (setConfigurationFinished !== undefined) {
setConfigurationFinished(true) setConfigurationFinished(true)
} }
@@ -705,9 +702,9 @@ const ConfigureWorkflow = (props) => {
}) })
.then((responseJson) => { .then((responseJson) => {
if (!responseJson.success) { if (!responseJson.success) {
toast("Failed to set app auth: " + responseJson.reason); toast("Failed to set app authentication: " + responseJson.reason);
} else { } else {
toast("App auth set for app " + app.name.replace("_", " ")); toast("App authentication set for app " + app.name.replace("_", " "));
setFinalized(true) setFinalized(true)
setOpened(false) setOpened(false)
} }
@@ -731,6 +728,7 @@ const ConfigureWorkflow = (props) => {
parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " "); parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
console.log("AUTH Action: ", action)
return ( return (
<ListItem <ListItem
style={{padding: 0, display: "flex", flexDirection: "column", }} style={{padding: 0, display: "flex", flexDirection: "column", }}
@@ -744,6 +742,25 @@ const ConfigureWorkflow = (props) => {
<div style={{display: "flex", }} <div style={{display: "flex", }}
onClick={() => { onClick={() => {
setOpened(!opened); 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, }}> <div style={{display: "flex", flex: 7, }}>
@@ -1318,7 +1335,7 @@ const ConfigureWorkflow = (props) => {
</Typography> </Typography>
: null} : 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) => { {requiredActions.map((data, index) => {
// AppWrapper = Default in a workflow, only shows with steps // AppWrapper = Default in a workflow, only shows with steps
+1 -3
View File
@@ -146,8 +146,6 @@ const EditWorkflow = (props) => {
const newWorkflow = isEditing === true ? false : true const newWorkflow = isEditing === true ? false : true
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === 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 upload = "";
var total_count = 0 var total_count = 0
@@ -485,7 +483,7 @@ const EditWorkflow = (props) => {
</DialogContent> </DialogContent>
<DialogActions style={{paddingRight: 100, }}> <DialogActions style={{paddingRight: 100, marginBottom: 100, }}>
<Button <Button
style={{}} style={{}}
onClick={() => { onClick={() => {
+291 -21
View File
@@ -16,6 +16,11 @@ import {
Divider, Divider,
Select, Select,
MenuItem, MenuItem,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Typography,
} from "@mui/material"; } from "@mui/material";
import { import {
@@ -30,9 +35,8 @@ import {
Add as AddIcon, Add as AddIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
//import { useAlert
import Dropzone from "../components/Dropzone.jsx"; import Dropzone from "../components/Dropzone.jsx";
import CodeEditor from "../components/ShuffleCodeEditor.jsx"; import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import theme from "../theme.jsx"; import theme from "../theme.jsx";
const Files = (props) => { const Files = (props) => {
@@ -45,6 +49,13 @@ const Files = (props) => {
const [fileContent, setFileContent] = React.useState(""); const [fileContent, setFileContent] = React.useState("");
const [openEditor, setOpenEditor] = React.useState(false); const [openEditor, setOpenEditor] = React.useState(false);
const [renderTextBox, setRenderTextBox] = 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 alert = useAlert();
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"] 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`, { fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, {
method: "PUT", method: "PUT",
headers: { headers: {
@@ -76,17 +87,34 @@ const Files = (props) => {
}, },
body:text, body:text,
credentials: "include", credentials: "include",
}).then((response) => { })
.then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Can't update file"); console.log("Can't update file");
} }
return response.json(); 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 = () => { const getFiles = (namespace) => {
fetch(globalUrl + "/api/v1/files", { 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", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -104,12 +132,23 @@ const Files = (props) => {
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.files !== undefined && responseJson.files !== null) { if (responseJson.files !== undefined && responseJson.files !== null) {
setFiles(responseJson.files); setFiles(responseJson.files);
} else { } 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([]); 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); setFileNamespaces(responseJson.namespaces);
} }
}) })
@@ -119,9 +158,214 @@ const Files = (props) => {
}; };
useEffect(() => { 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) => { const deleteFile = (file) => {
fetch(globalUrl + "/api/v1/files/" + file.id, { fetch(globalUrl + "/api/v1/files/" + file.id, {
method: "DELETE", method: "DELETE",
@@ -140,7 +384,7 @@ const Files = (props) => {
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success) { if (responseJson.success) {
toast("Successfully deleted file " + file.name); toast("Successfully deleted file")
} else if ( } else if (
responseJson.reason !== undefined && responseJson.reason !== undefined &&
responseJson.reason !== null responseJson.reason !== null
@@ -254,7 +498,7 @@ const Files = (props) => {
}); });
}; };
const handleCreateFile = (filename, file) => { const handleCreateFile = (filename, file) => {
var data = { var data = {
filename: filename, filename: filename,
org_id: selectedOrganization.id, org_id: selectedOrganization.id,
@@ -355,7 +599,7 @@ const Files = (props) => {
} }
setTimeout(() => { setTimeout(() => {
getFiles(); getFiles()
}, 2500); }, 2500);
}; };
@@ -378,7 +622,21 @@ const Files = (props) => {
}} }}
onDrop={uploadFile} 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 }}> <div style={{ marginTop: 20, marginBottom: 20 }}>
<h2 style={{ display: "inline" }}>Files</h2> <h2 style={{ display: "inline" }}>Files</h2>
<span style={{ marginLeft: 25 }}> <span style={{ marginLeft: 25 }}>
@@ -393,6 +651,9 @@ const Files = (props) => {
</a> </a>
</span> </span>
</div> </div>
<Button <Button
color="primary" color="primary"
variant="contained" variant="contained"
@@ -426,6 +687,8 @@ const Files = (props) => {
<CachedIcon /> <CachedIcon />
</Button> </Button>
{fileNamespaces !== undefined && {fileNamespaces !== undefined &&
fileNamespaces !== null && fileNamespaces !== null &&
fileNamespaces.length > 1 ? ( fileNamespaces.length > 1 ? (
@@ -442,8 +705,13 @@ const Files = (props) => {
}} }}
value={selectedNamespace} value={selectedNamespace}
onChange={(event) => { onChange={(event) => {
console.log("CHANGE NAMESPACE: ", event.target);
setSelectedNamespace(event.target.value); setSelectedNamespace(event.target.value);
if (event.target.value === "all" || event.target.value === "default") {
getFiles()
} else {
getFiles(event.target.value)
}
}} }}
> >
{fileNamespaces.map((data, index) => { {fileNamespaces.map((data, index) => {
@@ -453,7 +721,7 @@ const Files = (props) => {
value={data} value={data}
style={{ color: "white" }} style={{ color: "white" }}
> >
{data} {data.replaceAll("_", " ")}
</MenuItem> </MenuItem>
); );
})} })}
@@ -487,6 +755,8 @@ const Files = (props) => {
<AddIcon/> <AddIcon/>
</Button> </Button>
</Tooltip> } </Tooltip> }
{renderTextBox && <TextField {renderTextBox && <TextField
onKeyPress={(event)=>{ onKeyPress={(event)=>{
handleKeyDown(event); handleKeyDown(event);
@@ -504,7 +774,7 @@ const Files = (props) => {
autoFocus autoFocus
/>}</div> />}</div>
<CodeEditor <ShuffleCodeEditor
isCloud={isCloud} isCloud={isCloud}
expansionModalOpen={openEditor} expansionModalOpen={openEditor}
setExpansionModalOpen={setOpenEditor} setExpansionModalOpen={setOpenEditor}
@@ -602,7 +872,7 @@ const Files = (props) => {
/> />
<ListItemText <ListItemText
primary={ primary={
file.workflow_id === "global" ? ( file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ?
<IconButton <IconButton
disabled={file.workflow_id === "global"} disabled={file.workflow_id === "global"}
> >
@@ -615,7 +885,7 @@ const Files = (props) => {
}} }}
/> />
</IconButton> </IconButton>
) : ( : (
<Tooltip <Tooltip
title={"Go to workflow"} title={"Go to workflow"}
style={{}} style={{}}
+23 -39
View File
@@ -88,12 +88,6 @@ import {
SquareFoot as SquareFootIcon, SquareFoot as SquareFootIcon,
} from '@mui/icons-material'; } 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({ const useStyles = makeStyles({
notchedOutline: { notchedOutline: {
borderColor: "#f85a3e !important", borderColor: "#f85a3e !important",
@@ -1040,22 +1034,28 @@ const ParsedAction = (props) => {
}; };
const returnHelperText = (name, value) => { const returnHelperText = (name, value) => {
if (name === undefined || value === undefined) { if (name === undefined || value === undefined || name === null || value === null || name === "" || value === "") {
return "" //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 an external IP or hostname."
}
}
return helperText
} }
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) //console.log("AUTH: ", authenticationType)
if (authenticationType !== undefined && authenticationType !== null && authenticationType.type === "oauth2") { if (authenticationType !== undefined && authenticationType !== null && authenticationType.type === "oauth2") {
/* /*
@@ -1297,17 +1297,13 @@ const ParsedAction = (props) => {
} }
var placeholder = "Value"; var placeholder = "Value";
if ( if (data.example !== undefined && data.example !== null && data.example.length > 0) {
data.example !== undefined &&
data.example !== null &&
data.example.length > 0
) {
placeholder = data.example; placeholder = data.example;
if (data.name === "url" && data.value !== undefined && data.value !== null && data.value.length === 0) { if (data.name === "url" && data.value !== undefined && data.value !== null && data.value.length === 0) {
data.value = data.example; data.value = data.example;
} }
// In case of data.example // In case of data.example
if (data.value === undefined || data.value === null) { if (data.value === undefined || data.value === null) {
data.value = "" data.value = ""
@@ -1642,7 +1638,6 @@ const ParsedAction = (props) => {
), ),
}} }}
multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline} multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline}
helperText={returnHelperText(data.name, data.value)}
onClick={() => { onClick={() => {
/* /*
setExpansionModalOpen(false); setExpansionModalOpen(false);
@@ -1680,18 +1675,7 @@ const ParsedAction = (props) => {
//changeActionParameterCodemirror(event, count, data) //changeActionParameterCodemirror(event, count, data)
changeActionParameter(event, count, data); changeActionParameter(event, count, data);
}} }}
helperText={baseHelperText.length > 0 ? baseHelperText : helperText={returnHelperText(data.name, data.value)}
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
}
onBlur={(event) => { onBlur={(event) => {
baseHelperText = calculateHelpertext(event.target.value) baseHelperText = calculateHelpertext(event.target.value)
if (setLastSaved !== undefined) { if (setLastSaved !== undefined) {
+76 -8
View File
@@ -67,9 +67,15 @@ const Priorities = (props) => {
}) })
} }
const dismissNotification = (alert_id) => { const dismissNotification = (alert_id, disabled) => {
// Don't really care about the logout var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread`
fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { if (disabled === true) {
notificationurl += "?disabled=true"
} else if (disabled === false) {
notificationurl += "?disabled=false"
}
fetch(notificationurl , {
credentials: "include", credentials: "include",
method: "GET", method: "GET",
headers: { headers: {
@@ -85,12 +91,50 @@ const Priorities = (props) => {
}) })
.then(function (responseJson) { .then(function (responseJson) {
if (responseJson.success === true) { if (responseJson.success === true) {
const newNotifications = notifications.filter( // Mark current one as read
(data) => data.id !== alert_id var newNotifications = notifications.map((notification) => {
); if (notification.id === alert_id) {
console.log("NEW NOTIFICATIONS: ", newNotifications); 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) setNotifications(newNotifications)
} }
} else { } else {
@@ -174,6 +218,14 @@ const Priorities = (props) => {
style={{marginRight: 15, height: 25, }} style={{marginRight: 15, height: 25, }}
/> />
: null} : null}
{data.ignored === true ?
<Chip
label={"Disabled"}
variant="outlined"
color="primary"
style={{marginRight: 15, height: 25, }}
/>
: null}
{data.read === false ? {data.read === false ?
<Chip <Chip
label={"Unread"} label={"Unread"}
@@ -227,6 +279,22 @@ const Priorities = (props) => {
Dismiss Dismiss
</Button> </Button>
) : null} ) : 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> </ButtonGroup>
<Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}> <Typography variant="body2" color="textSecondary" style={{marginLeft: 20, marginTop: 20, }}>
-1
View File
@@ -32,7 +32,6 @@ const Priority = (props) => {
let newdescription = priority.description let newdescription = priority.description
const descsplit = priority.description.split("&") const descsplit = priority.description.split("&")
if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) { if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) {
console.log("descsplit: ", descsplit)
if (descsplit[1] === "") { if (descsplit[1] === "") {
const item = findSpecificApp(appFramework, descsplit[0]) const item = findSpecificApp(appFramework, descsplit[0])
+6 -28
View File
@@ -5,13 +5,13 @@ import cystyle from "../defaultCytoscapeStyle.jsx";
const surfaceColor = "#27292D"; const surfaceColor = "#27292D";
const CytoscapeWrapper = (props) => { const CytoscapeWrapper = (props) => {
const { globalUrl, inworkflow } = props; const { globalUrl, inworkflow, height, width } = props;
const [elements, setElements] = useState([]); const [elements, setElements] = useState([]);
const [workflow, setWorkflow] = useState(inworkflow); const [workflow, setWorkflow] = useState(inworkflow);
const [cy, setCy] = React.useState(); const [cy, setCy] = React.useState();
const bodyWidth = 200; const bodyWidth = height === undefined ? 1000 : width
const bodyHeight = 150; const bodyHeight = width === undefined ? 1000 : height
const setupGraph = () => { const setupGraph = () => {
const actions = workflow.actions.map((action) => { 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. // 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; return edge;
}); });
@@ -135,9 +112,10 @@ const CytoscapeWrapper = (props) => {
elements={elements} elements={elements}
minZoom={0.35} minZoom={0.35}
maxZoom={2.0} maxZoom={2.0}
zoom={1.0}
style={{ style={{
width: bodyWidth - 15, width: bodyWidth,
height: bodyHeight - 5, height: bodyHeight,
backgroundColor: surfaceColor, backgroundColor: surfaceColor,
}} }}
stylesheet={cystyle} stylesheet={cystyle}
+14 -4
View File
@@ -75,9 +75,15 @@ const RuntimeDebugger = (props) => {
setTotalCount(0) setTotalCount(0)
var starttime = ""
var endtime = ""
var count = 0 var count = 0
var starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString() try {
var endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString() 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 var maxworkflows = 5
@@ -88,6 +94,11 @@ const RuntimeDebugger = (props) => {
} }
const workflowId = workflows[key].id const workflowId = workflows[key].id
if (workflowId === undefined || workflowId === null || workflowId === "") {
continue
}
// Fetch the data for the workflow // Fetch the data for the workflow
var url = `${globalUrl}/api/v1/workflows/${workflowId}/executions/count` var url = `${globalUrl}/api/v1/workflows/${workflowId}/executions/count`
if (starttime !== "") { if (starttime !== "") {
@@ -133,7 +144,6 @@ const RuntimeDebugger = (props) => {
} }
setTimeout(() => { setTimeout(() => {
console.log("Setting total count: ", count)
setTotalCount(count) setTotalCount(count)
}, maxworkflows*300) }, maxworkflows*300)
} }
@@ -578,7 +588,7 @@ const RuntimeDebugger = (props) => {
onClick={() => { onClick={() => {
window.open(`${globalUrl}/api/v1/workflows/search/${params.row.id}`, "_blank") window.open(`${globalUrl}/api/v1/workflows/search/${params.row.id}`, "_blank")
}} }}
disabled={!userdata.support} disabled={userdata.region_url !== "https://shuffler.io"}
> >
<InsightsIcon fontSize="small" /> <InsightsIcon fontSize="small" />
</IconButton> </IconButton>
@@ -57,7 +57,6 @@ import { padding, textAlign } from '@mui/system';
import data from '../frameworkStyle.jsx'; import data from '../frameworkStyle.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import { tags as t } from '@lezer/highlight'; 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": ``}, {"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 CodeEditor = (props) => {
const { const {
globalUrl, globalUrl,
@@ -5,6 +5,7 @@ import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import AppSearchButtons from "../components/AppSearchButtons.jsx"; import AppSearchButtons from "../components/AppSearchButtons.jsx";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import RenderCytoscape from "../components/RenderCytoscape.jsx";
import { import {
Button, Button,
Typography, Typography,
@@ -29,7 +30,7 @@ import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
const WorkflowTemplatePopup = (props) => { 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 [isActive, setIsActive] = useState(false);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
@@ -39,17 +40,41 @@ const WorkflowTemplatePopup = (props) => {
const [workflow, setWorkflow] = useState({}); const [workflow, setWorkflow] = useState({});
const [showLoginButton, setShowLoginButton] = useState(false); const [showLoginButton, setShowLoginButton] = useState(false);
const [appAuthentication, setAppAuthentication] = React.useState(undefined); const [appAuthentication, setAppAuthentication] = React.useState(undefined);
const [missingSource, setMissingSource] = React.useState(undefined) const [missingSource, setMissingSource] = React.useState(undefined)
const [missingDestination, setMissingDestination] = React.useState(undefined); const [missingDestination, setMissingDestination] = React.useState(undefined);
const [configurationFinished, setConfigurationFinished] = React.useState(false); const [configurationFinished, setConfigurationFinished] = React.useState(false);
const [appSetupDone, setAppSetupDone] = React.useState(false)
useEffect(() => { const [requestSent, setRequestSent] = React.useState(false)
}, [missingSource, missingDestination])
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
let navigate = useNavigate(); 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 = { const imagestyleWrapper = {
height: 40, height: 40,
@@ -272,6 +297,9 @@ const WorkflowTemplatePopup = (props) => {
// middle:[] // middle:[]
// name: "Email analysis" // name: "Email analysis"
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
if (requestSent === true) {
return
}
if (srcapp.includes(":default") || dstapp.includes(":default")) { 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 = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge`
//const url = `https://shuffler.io/api/v1/workflows/merge`
fetch(url, { fetch(url, {
method: "POST", method: "POST",
headers: { headers: {
@@ -324,6 +351,7 @@ const WorkflowTemplatePopup = (props) => {
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
//console.log("Status not 200 for framework!"); //console.log("Status not 200 for framework!");
setRequestSent(false)
} }
setWorkflowLoading(false) setWorkflowLoading(false)
@@ -341,6 +369,7 @@ const WorkflowTemplatePopup = (props) => {
if (responseJson.success === false) { if (responseJson.success === false) {
//console.log("Error in workflow template: ", responseJson.error); //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." 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 !== "") { if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
@@ -366,10 +395,21 @@ const WorkflowTemplatePopup = (props) => {
}) })
.catch((error) => { .catch((error) => {
console.log("err in framework: ", error.toString()); console.log("err in framework: ", error.toString());
setRequestSent(false)
setWorkflowLoading(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 = () => { const isFinished = () => {
// Look for configuration fields being done in the current modal // Look for configuration fields being done in the current modal
// 1. Start by finding the modal // 1. Start by finding the modal
@@ -388,6 +428,13 @@ const WorkflowTemplatePopup = (props) => {
} }
const ModalView = () => { const ModalView = () => {
if (modalOpen === false) {
return null
}
const divHeight = 500
const divWidth = 500
return ( return (
<Drawer <Drawer
anchor={"left"} anchor={"left"}
@@ -475,9 +522,9 @@ const WorkflowTemplatePopup = (props) => {
</div> </div>
} }
{(missingSource !== undefined || missingDestination !== undefined) ? {(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ?
<Typography variant="body1" style={{marginTop: 75, marginBottom: 10, }}> <Typography variant="body1" style={{marginTop: 75, marginBottom: 10, }}>
{"Find relevevant Apps for this Usecase"} {"Find relevant Apps for this Usecase"}
</Typography> </Typography>
: null} : null}
@@ -491,6 +538,8 @@ const WorkflowTemplatePopup = (props) => {
AppImage={missingSource.image} AppImage={missingSource.image}
setMissing={setMissingSource} setMissing={setMissingSource}
getAppFramework={getAppFramework}
/> />
</div> </div>
: null} : null}
@@ -505,6 +554,8 @@ const WorkflowTemplatePopup = (props) => {
AppImage={missingDestination.image} AppImage={missingDestination.image}
setMissing={setMissingDestination} setMissing={setMissingDestination}
getAppFramework={getAppFramework}
/> />
</div> </div>
: null} : null}
@@ -521,12 +572,29 @@ const WorkflowTemplatePopup = (props) => {
setConfigurationFinished={setConfigurationFinished} 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 ? {errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
<Tooltip title="Workflow generated!" placement="top"> <Tooltip title="Click to explore the workflow" placement="top">
<span style={{display: "flex", }}> <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 }} /> */} {/*<CheckIcon color="primary" sx={{ borderRadius: 4 }} /> */}
<Typography variant="h6" style={{ marginLeft: 20, }}> <Typography variant="h5" style={{ }}>
Workflow generated! Workflow Successfully Generated!
</Typography> </Typography>
</span> </span>
</Tooltip> </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, }} /> <CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
: ""} : ""}
</div> </div>
</div> </div>
+31 -22
View File
@@ -5,8 +5,6 @@ import { makeStyles } from "@mui/styles";
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
import countries from "../components/Countries.jsx"; 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 CacheView from "../components/CacheView.jsx";
import { 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" ? {selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
<div> <div>
<Typography variant="body1" color="textSecondary" style={{ marginBottom: 0, marginTop: 10 }}> <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> </Typography>
</div> </div>
: : null }
selectedAuthentication.fields.map((data, index) => {
{selectedAuthentication.fields.map((data, index) => {
var fieldname = data.key.replaceAll("_", " ") var fieldname = data.key.replaceAll("_", " ")
if (fieldname.endsWith(" basic")) { if (fieldname.endsWith(" basic")) {
fieldname = fieldname.substring(0, fieldname.length - 6) 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) //console.log("DATA: ", data, selectedAuthentication)
return ( return (
<div key={index}> <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 // using request id or trace id
<ListItem key={index} style={{ backgroundColor: index % 2 === 0 ? "#1f2023" : "#27292d" }}> <ListItem key={index} style={{ backgroundColor: index % 2 === 0 ? "#1f2023" : "#27292d" }}>
<ListItemText <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={{ style={{
minWidth: 150, minWidth: 200,
maxWidth: 150, maxWidth: 200,
}} }}
/> />
<ListItemText <ListItemText
primary={data.status} primary={data.referrer}
style={{ style={{
minWidth: 70, minWidth: 300,
maxWidth: 70, maxWidth: 300,
}} overflow: "hidden",
/>
<ListItemText
primary={data.method}
style={{
minWidth: 150,
maxWidth: 150,
}} }}
/> />
<ListItemText <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, minWidth: 700,
maxWidth: 700, maxWidth: 700,
overflow: "hidden", overflow: "hidden",
marginLeft: 10,
}} }}
/> />
</ListItem> </ListItem>
@@ -4406,7 +4415,7 @@ If you're interested, please let me know a time that works for you, or set up a
/> />
<ListItemText <ListItemText
primary="Default" primary="Default"
style={{ minWidth: 100, maxWidth: 100 }} style={{ minWidth: 110, maxWidth: 110, }}
/> />
<ListItemText <ListItemText
primary="Actions" 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 <ListItemText
style={{ style={{
minWidth: 80, minWidth: 100,
maxWidth: 80, maxWidth: 100,
overflow: "hidden", overflow: "hidden",
}} }}
primary={environment.default ? "true" : null} 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 : ( {environment.default ? null : (
<Button <Button
variant="outlined" variant="outlined"
style={{ marginLeft: 0, marginRight: 0, }} style={{ marginLeft: 0, marginRight: 0, maxWidth: 150, }}
onClick={() => setDefaultEnvironment(environment)} onClick={() => setDefaultEnvironment(environment)}
color="primary" color="primary"
> >
+252 -64
View File
@@ -121,7 +121,7 @@ import * as edgehandles from "cytoscape-edgehandles";
import CytoscapeComponent from "react-cytoscapejs"; import CytoscapeComponent from "react-cytoscapejs";
import Draggable from "react-draggable"; import Draggable from "react-draggable";
import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; 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 { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
@@ -13366,6 +13366,67 @@ const AngularWorkflow = (defaultprops) => {
</Link> </Link>
<h2 style={{ margin: 0 }}>{workflow.name}</h2> <h2 style={{ margin: 0 }}>{workflow.name}</h2>
</Breadcrumbs> </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>
<div style={{display: "flex", marginLeft: 10, }}> <div style={{display: "flex", marginLeft: 10, }}>
{parentWorkflows.slice(0,5).map((wf, index) => { {parentWorkflows.slice(0,5).map((wf, index) => {
@@ -13744,9 +13805,64 @@ const AngularWorkflow = (defaultprops) => {
</div> </div>
: null : null
const RightsideBar = () => { const RightsideBar = () => {
const [hovered, setHovered] = useState(false) 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 ( return (
<div <div
style={{ style={{
@@ -13802,7 +13918,7 @@ const AngularWorkflow = (defaultprops) => {
</span> </span>
</Tooltip> </Tooltip>
) : ( ) : (
<Tooltip color="primary" title="Test Execution" placement="top"> <Tooltip color="primary" title="Test Execution (Ctrl + /)" placement="top">
<span> <span>
<Button <Button
disabled={ disabled={
@@ -14000,7 +14116,7 @@ const AngularWorkflow = (defaultprops) => {
</Tooltip> </Tooltip>
<Tooltip <Tooltip
color="secondary" color="secondary"
title={`Show executions (${workflowExecutions.length})`} title={`Show executions (${workflowExecutions.length}) (Ctrl + ')`}
placement="top-start" placement="top-start"
> >
<span> <span>
@@ -14069,7 +14185,7 @@ const AngularWorkflow = (defaultprops) => {
</Tooltip> </Tooltip>
<Tooltip <Tooltip
color="secondary" color="secondary"
title="Show Workflow Revision History (Beta)" title="Show Workflow Revision History (Beta) (Ctrl + ])"
placement="top" placement="top"
> >
<span> <span>
@@ -14080,8 +14196,8 @@ const AngularWorkflow = (defaultprops) => {
variant={"outlined"} variant={"outlined"}
onClick={() => { onClick={() => {
setShowWorkflowRevisions(true) setShowWorkflowRevisions(true)
setSelectedRevision(workflow) setSelectedRevision(workflow)
//setOriginalWorkflow(workflow) //setOriginalWorkflow(workflow)
}} }}
> >
<RestoreIcon /> <RestoreIcon />
@@ -14283,6 +14399,52 @@ const AngularWorkflow = (defaultprops) => {
//return null; //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 // 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 // Too much work to move everything over alone, so won't touch it for now
//<Button style={{borderRadius: "0px"}} color="primary" variant="contained" onClick={() => { //<Button style={{borderRadius: "0px"}} color="primary" variant="contained" onClick={() => {
@@ -14538,8 +14700,8 @@ const AngularWorkflow = (defaultprops) => {
</Button> </Button>
<Button <Button
color="secondary" color="secondary"
disabled
variant="outlined" variant="outlined"
disabled={workflow.public !== true}
fullWidth fullWidth
style={{ marginTop: 15, }} style={{ marginTop: 15, }}
onClick={() => { onClick={() => {
@@ -14547,6 +14709,8 @@ const AngularWorkflow = (defaultprops) => {
// workflow.public = false // workflow.public = false
// setWorkflow(workflow) // setWorkflow(workflow)
// setUpdate(Math.random()); // 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 Unpublish Workflow
@@ -15112,6 +15276,7 @@ const AngularWorkflow = (defaultprops) => {
</a> </a>
</Tooltip> </Tooltip>
</div> </div>
<Tooltip title="Refresh runs (Ctrl + ;)" arrow>
<Button <Button
style={{ borderRadius: "0px" }} style={{ borderRadius: "0px" }}
variant="outlined" variant="outlined"
@@ -15124,6 +15289,7 @@ const AngularWorkflow = (defaultprops) => {
<CachedIcon style={{ marginRight: 10 }} /> <CachedIcon style={{ marginRight: 10 }} />
Refresh Runs Refresh Runs
</Button> </Button>
</Tooltip>
<Divider <Divider
style={{ style={{
backgroundColor: "rgba(255,255,255,0.5)", backgroundColor: "rgba(255,255,255,0.5)",
@@ -15351,48 +15517,51 @@ const AngularWorkflow = (defaultprops) => {
</div> </div>
) : ( ) : (
<div style={{ padding: isMobile ? "0px 10px 25px 10px" : "25px 15px 25px 15px", maxWidth: isMobile ? "100%" : 400, overflowX: "hidden" }}> <div style={{ padding: isMobile ? "0px 10px 25px 10px" : "25px 15px 25px 15px", maxWidth: isMobile ? "100%" : 400, overflowX: "hidden" }}>
<Breadcrumbs
aria-label="breadcrumb" <Breadcrumbs
separator="" aria-label="breadcrumb"
style={{ color: "white", fontSize: 16 }} 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);
}}
> >
<IconButton <span
style={{ style={{ color: "rgba(255,255,255,0.5)", display: "flex" }}
paddingLeft: 0, onClick={() => {
marginTop: "auto", setExecutionRunning(false);
marginBottom: "auto", stop();
}} getWorkflowExecution(props.match.params.key, "");
onClick={() => { setExecutionModalView(0);
setExecutionRunning(false); setLastExecution(executionData.execution_id);
stop() }}
}} >
> <IconButton
<ArrowBackIcon style={{ color: "rgba(255,255,255,0.5)" }} /> style={{
</IconButton> paddingLeft: 0,
<h2 marginTop: "auto",
style={{ color: "rgba(255,255,255,0.5)", cursor: "pointer" }} marginBottom: "auto",
onClick={() => { }}
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; onClick={() => {
const newitem = removeParam("execution_id", cursearch); setExecutionRunning(false);
navigate(curpath + newitem) stop()
setExecutionRunning(false); }}
stop() >
}} <ArrowBackIcon style={{ color: "rgba(255,255,255,0.5)" }} />
> </IconButton>
See more runs <Tooltip title="See more runs (Ctrl + Shift)" arrow>
</h2> <h2
</span> style={{ color: "rgba(255,255,255,0.5)", cursor: "pointer" }}
</Breadcrumbs> 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 <Divider
style={{ style={{
backgroundColor: "rgba(255,255,255,0.6)", backgroundColor: "rgba(255,255,255,0.6)",
@@ -15456,15 +15625,19 @@ const AngularWorkflow = (defaultprops) => {
color="primary" color="primary"
title="Explore logs for the workflow" title="Explore logs for the workflow"
placement="top" placement="top"
style={{ zIndex: 50000 }} style={{ zIndex: 50000, }}
> >
<span style={{}}> <span style={{}}>
<Button <Button
color="primary" color="primary"
style={{ float: "right", marginTop: 20, marginLeft: 10 }} style={{ float: "right", marginTop: 20, marginLeft: 10 }}
disabled={!userdata.support} disabled={userdata.region_url !== "https://shuffler.io"}
onClick={() => { 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" /> <InsightsIcon color="secondary" />
@@ -17855,42 +18028,45 @@ const AngularWorkflow = (defaultprops) => {
return null return null
} }
var newrevision = JSON.parse(JSON.stringify(revision))
// Make unix timestamp into ISO timestamp in the format July 27th, 3:05 AM // Make unix timestamp into ISO timestamp in the format July 27th, 3:05 AM
// 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 // 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 }) 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" var workflowStatus = newrevision.status !== undefined && newrevision.status !== null && newrevision.status !== "" ? newrevision.status : "test"
if (revision.name !== undefined && revision.name !== null && revision.name !== "") { if (newrevision.name !== undefined && newrevision.name !== null && newrevision.name !== "") {
if (revision.name.toLowerCase().includes("test")) { if (newrevision.name.toLowerCase().includes("test")) {
workflowStatus = "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" 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" workflowStatus = "prod"
} }
} }
return ( return (
<Paper <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) => { }} onClick={(e) => {
if (revision.edited === selectedRevision.edited) { if (newrevision.edited === selectedRevision.edited) {
console.log("Same revision! No setting.") console.log("Same revision! No setting.")
return return
} }
// Should render if it's not the same as workflow.edited // Should render if it's not the same as workflow.edited
console.log("Clicked revision: ", revision) console.log("Clicked revision: ", newrevision)
setLastSaved(false) setLastSaved(false)
setSelectedRevision(revision) setSelectedRevision(newrevision)
setWorkflow(revision) setWorkflow(newrevision)
setSelectedAction({}); setSelectedAction({});
setSelectedApp({}) setSelectedApp({})
@@ -17910,7 +18086,7 @@ const AngularWorkflow = (defaultprops) => {
cy.removeListener("cxttap"); cy.removeListener("cxttap");
} }
setupGraph(revision) setupGraph(newrevision)
// Re-adding cytoscape triggers // Re-adding cytoscape triggers
if (cy !== undefined && cy !== null) { if (cy !== undefined && cy !== null) {
@@ -17936,8 +18112,20 @@ const AngularWorkflow = (defaultprops) => {
cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction)); cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction));
cy.on("cxttap", "node", (e) => onCtxTap(e)); 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 // Need to run through graph setup with this one
}}> }}>
<div style={{display: "flex", }}> <div style={{display: "flex", }}>
+23 -19
View File
@@ -1,16 +1,11 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react"
import { useInterval } from "react-powerhooks"; import { useInterval } from "react-powerhooks"
import AppFramework from "../components/AppFramework.jsx"; import AppFramework from "../components/AppFramework.jsx"
import { makeStyles, } from "@mui/styles"; import { makeStyles, } from "@mui/styles"
// nodejs library that concatenates classes import classNames from "classnames"
import classNames from "classnames"; import theme from '../theme.jsx'
import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"
import { useNavigate, Link, useParams } from "react-router-dom"; import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
// react plugin used to create charts
//import { Line, Bar } from "react-chartjs-2";
//import { useAlert
import { ToastContainer, toast } from "react-toastify" import { ToastContainer, toast } from "react-toastify"
import { parsedDatatypeImages } from "../components/AppFramework.jsx" import { parsedDatatypeImages } from "../components/AppFramework.jsx"
import { findSpecificApp } from "../components/AppFramework.jsx" import { findSpecificApp } from "../components/AppFramework.jsx"
@@ -96,7 +91,8 @@ const useStyles = makeStyles({
const UsecaseListComponent = (props) => { 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 [expandedIndex, setExpandedIndex] = useState(-1);
const [expandedItem, setExpandedItem] = useState(-1); const [expandedItem, setExpandedItem] = useState(-1);
@@ -114,11 +110,13 @@ const UsecaseListComponent = (props) => {
const [firstLoad, setFirstLoad] = useState(true) const [firstLoad, setFirstLoad] = useState(true)
const [apps, setApps] = useState([]) const [apps, setApps] = useState([])
const classes = useStyles(); const classes = useStyles();
let navigate = useNavigate(); let navigate = useNavigate();
const [mitreTags, setMitreTags] = useState([]); const [mitreTags, setMitreTags] = useState([]);
const parseUsecase = (subcase) => { const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type) const srcdata = findSpecificApp(frameworkData, subcase.type)
const dstdata = findSpecificApp(frameworkData, subcase.last) const dstdata = findSpecificApp(frameworkData, subcase.last)
@@ -137,13 +135,11 @@ const UsecaseListComponent = (props) => {
} }
useEffect(() => { useEffect(() => {
console.log("In frameworkData useEffect: frameworkData: ", frameworkData) //console.log("Frameworkdata changed. Use to set inputUsecase: ", frameworkData, prevSubcase)
if (frameworkData === undefined || prevSubcase === undefined) { if (frameworkData === undefined || prevSubcase === undefined) {
return return
} }
console.log("PAST!")
var parsedUsecase = inputUsecase var parsedUsecase = inputUsecase
const subcase = parseUsecase(prevSubcase) const subcase = parseUsecase(prevSubcase)
@@ -917,6 +913,9 @@ const UsecaseListComponent = (props) => {
description={inputUsecase.description} description={inputUsecase.description}
apps={apps} apps={apps}
getAppFramework={getFramework}
//appSetupDone={appSetupDone}
//setAppSetupDone={setAppSetupDone}
/> />
: null} : null}
</span> </span>
@@ -990,13 +989,15 @@ const UsecaseListComponent = (props) => {
}}> }}>
<AppFramework <AppFramework
inputUsecase={inputUsecase} inputUsecase={inputUsecase}
frameworkData={frameworkData}
selectedOption={"Draw"} selectedOption={"Draw"}
showOptions={false} showOptions={false}
isLoaded={true} isLoaded={true}
isLoggedIn={true} isLoggedIn={true}
globalUrl={globalUrl} globalUrl={globalUrl}
size={0.6} size={0.6}
frameworkData={frameworkData}
setFrameworkData={setFrameworkData}
/> />
</div> </div>
</div> </div>
@@ -1713,12 +1714,15 @@ const Dashboard = (props) => {
<UsecaseListComponent <UsecaseListComponent
userdata={userdata} userdata={userdata}
isLoggedIn={isLoggedIn} isLoggedIn={isLoggedIn}
frameworkData={frameworkData}
keys={selectedUsecases} keys={selectedUsecases}
isCloud={isCloud} isCloud={isCloud}
globalUrl={globalUrl} globalUrl={globalUrl}
workflows={workflows} workflows={workflows}
setWorkflows={setWorkflows} setWorkflows={setWorkflows}
frameworkData={frameworkData}
setFrameworkData={setFrameworkData}
getFramework={getFramework}
/> />
{treeKeys.length > 0 ? {treeKeys.length > 0 ?
+61 -4
View File
@@ -13,6 +13,11 @@ import {
//import { useAlert //import { useAlert
import { ToastContainer, toast } from "react-toastify" 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 Settings = (props) => {
const { globalUrl, isLoaded, userdata, setUserData } = props; const { globalUrl, isLoaded, userdata, setUserData } = props;
//const alert = useAlert(); //const alert = useAlert();
@@ -44,6 +49,17 @@ const Settings = (props) => {
const [userSettings, setUserSettings] = useState({}); 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 : { 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": "", "account": "",
@@ -467,7 +483,7 @@ const Settings = (props) => {
> >
What is the API key used for? What is the API key used for?
</a> </a>
<TextField {/* <TextField
style={{ backgroundColor: theme.palette.inputColor, flex: "1" }} style={{ backgroundColor: theme.palette.inputColor, flex: "1" }}
InputProps={{ InputProps={{
style: { style: {
@@ -484,7 +500,43 @@ const Settings = (props) => {
id="standard-required" id="standard-required"
margin="normal" margin="normal"
variant="outlined" 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 <Button
style={{ width: "100%", height: "40px", marginTop: "10px" }} style={{ width: "100%", height: "40px", marginTop: "10px" }}
variant="outlined" variant="outlined"
@@ -676,8 +728,13 @@ const Settings = (props) => {
Submit password change Submit password change
</Button> </Button>
<h3>{passwordFormMessage}</h3> <h3>{passwordFormMessage}</h3>
<Divider style={{ marginTop: "40px" }} /> {isCloud && (
<h2>Creator Incentive Program</h2> <>
<Divider style={{ marginTop: "40px" }} />
<h2>Creator Incentive Program</h2>
</>
)}
<div style={{ display: runFlex ? "flex" : "", width: "100%" }}> <div style={{ display: runFlex ? "flex" : "", width: "100%" }}>
<div> <div>
{isCloud ? {isCloud ?
+5 -7
View File
@@ -551,9 +551,7 @@ const Workflows = (props) => {
const [field1, setField1] = React.useState(""); const [field1, setField1] = React.useState("");
const [field2, setField2] = React.useState(""); const [field2, setField2] = React.useState("");
const [downloadUrl, setDownloadUrl] = React.useState( const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows")
"https://github.com/frikky/shuffle-workflows"
);
const [downloadBranch, setDownloadBranch] = React.useState("master"); const [downloadBranch, setDownloadBranch] = React.useState("master");
const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] =
React.useState(false); React.useState(false);
@@ -924,7 +922,7 @@ const Workflows = (props) => {
> >
<DialogTitle> <DialogTitle>
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}> <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 Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
</div> </div>
@@ -3630,7 +3628,7 @@ const Workflows = (props) => {
const handleGithubValidation = () => { const handleGithubValidation = () => {
importWorkflowsFromUrl(downloadUrl); importWorkflowsFromUrl(downloadUrl);
setLoadWorkflowsModalOpen(false); setLoadWorkflowsModalOpen(false);
}; }
const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( const workflowDownloadModalOpen = loadWorkflowsModalOpen ? (
<Dialog <Dialog
@@ -3677,11 +3675,11 @@ const Workflows = (props) => {
}, },
}} }}
onChange={(e) => setDownloadUrl(e.target.value)} onChange={(e) => setDownloadUrl(e.target.value)}
placeholder="https://github.com/frikky/shuffle-apps" placeholder="https://github.com/shuffle/workflows"
fullWidth fullWidth
/> />
<span style={{ marginTop: 10 }}> <span style={{ marginTop: 10 }}>
Branch (default value is "master"): Branch (default value is "main"):
</span> </span>
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<TextField <TextField