diff --git a/backend/go-app/main.go b/backend/go-app/main.go index f0a3a77d..3b413141 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5743,15 +5743,16 @@ func init() { r.HandleFunc("/api/v1/streams", handleWorkflowQueue).Methods("POST") r.HandleFunc("/api/v1/streams/results", handleGetStreamResults).Methods("POST", "OPTIONS") + // App specific r.HandleFunc("/api/v1/apps/get_existing", loadSpecificApps).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/{appId}", deleteWorkflowApp).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/apps/{appId}/config", getWorkflowAppConfig).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps", getWorkflowApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS") + r.HandleFunc("/api/v1/apps/search", getSpecificApps).Methods("POST", "OPTIONS") - // Legacy things + // Legacy app things r.HandleFunc("/api/v1/workflows/apps/validate", validateAppInput).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/workflows/apps", getWorkflowApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/workflows/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index ed5c52b4..9c039655 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -2917,6 +2917,51 @@ func handleGetfile(resp http.ResponseWriter, request *http.Request) ([]byte, err return buf.Bytes(), nil } +func getSpecificApps(resp http.ResponseWriter, request *http.Request) { + cors := handleCors(resp, request) + if cors { + return + } + + // Just need to be logged in + // FIXME - should have some permissions? + _, err := handleApiAuthentication(resp, request) + if err != nil { + log.Printf("Api authentication failed in set new app: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("Error with body read: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + type tmpStruct struct { + Search string `json:"search"` + } + + var tmpBody tmpStruct + err = json.Unmarshal(body, &tmpBody) + if err != nil { + log.Printf("Error with unmarshal tmpBody: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // FIXME - continue the search here with github repos etc. + // Caching might be smart :D + log.Printf("Body: %s", string(body)) + + resp.WriteHeader(200) + resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) +} + func validateAppInput(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { diff --git a/frontend/src/AngularWorkflow.js b/frontend/src/AngularWorkflow.js index ec848df6..869182cb 100644 --- a/frontend/src/AngularWorkflow.js +++ b/frontend/src/AngularWorkflow.js @@ -90,8 +90,8 @@ const AngularWorkflow = (props) => { const [cystyle, ] = useState(cytoscapestyle) const [cy, setCy] = React.useState() + const [appSearch, setAppSearch] = React.useState("") const [currentView, setCurrentView] = React.useState("apps") - const [, setAppSearchValue] = React.useState("") const [triggerAuthentication, setTriggerAuthentication] = React.useState({}) const [triggerFolders, setTriggerFolders] = React.useState([]) @@ -1385,10 +1385,6 @@ const AngularWorkflow = (props) => { }); } - const setAppSearch = (event) => { - setFilteredApps(apps.filter(app => app.name.includes(event.target.value))) - } - const appViewStyle = { marginLeft: "5px", marginRight: "5px", @@ -1568,6 +1564,7 @@ const AngularWorkflow = (props) => { } const HandleLeftView = () => { + // Defaults to apps. var thisview = if (currentView === "triggers") { thisview = @@ -1577,14 +1574,6 @@ const AngularWorkflow = (props) => { return(
-
{ - setLeftViewOpen(false) - setLeftBarSize(50) - }}> - - - -
{thisview} @@ -1981,11 +1970,36 @@ const AngularWorkflow = (props) => { overflowX: "hidden", } + const runAppSearch = (event) => { + setAppSearch(event.target.value) + setFilteredApps(apps.filter(app => app.name.toLowerCase().includes(event.target.value.trim().toLowerCase()))) + } + const AppView = () => { return(
+ {/* + { + runAppSearch(event) + }} + /> + */} {filteredApps.map(app=> { // FIXME - add label to apps, as this might be slow with A LOT of apps var newAppname = app.name diff --git a/frontend/src/Apps.js b/frontend/src/Apps.js index aeb09d03..9c5b6c2b 100644 --- a/frontend/src/Apps.js +++ b/frontend/src/Apps.js @@ -38,6 +38,7 @@ const Apps = (props) => { const [filteredApps, setFilteredApps] = React.useState([]) const [validation, setValidation] = React.useState(false) const [isLoading, setIsLoading] = React.useState(false) + const [appSearchLoading, setAppSearchLoading] = React.useState(false) const [openApi, setOpenApi] = React.useState("") const [openApiData, setOpenApiData] = React.useState("") @@ -109,7 +110,7 @@ const Apps = (props) => { if (responseJson.length > 0) { setSelectedApp(responseJson[0]) } - }) + }) .catch(error => { alert.error(error.toString()) }); @@ -254,33 +255,6 @@ const Apps = (props) => { marginBottom: 10, } - //const handleFile = (event) =>{ - // const formData = new FormData(); - // formData.append('file', event.target.files[0]); - - // fetch(globalUrl+"/api/v1/workflows/apps/validate", { - // method: 'POST', - // headers: { - // 'Accept': 'application/json', - // }, - // body: formData, - // credentials: "include", - // }) - // .then((response) => { - // if (response.status !== 200) { - // console.log("Status not 200 for apps :O!") - // return - // } - // return response.json() - // }) - // .then((responseJson) => { - // console.log(responseJson) - // }) - // .catch(error => { - // alert.error(error.toString()) - // }); - //} - const UploadView = () => { //var imageline = selectedApp.large_image === undefined || selectedApp.large_image.length === 0 ? // @@ -386,7 +360,13 @@ const Apps = (props) => { const handleSearchChange = (event) => { const searchfield = event.target.value.toLowerCase() const newapps = apps.filter(data => data.name.toLowerCase().includes(searchfield) || data.description.toLowerCase().includes(searchfield)) - setFilteredApps(newapps) + + if (newapps.length === 0 && !appSearchLoading) { + setAppSearchLoading(true) + runAppSearch(searchfield) + } else { + setFilteredApps(newapps) + } } const appView = isLoggedIn ? @@ -461,6 +441,12 @@ const Apps = (props) => {

Try a broader search term. E.g. "http" or "TheHive"

+
+ + {appSearchLoading ? + + : null + } : @@ -577,6 +563,29 @@ const Apps = (props) => { }); } + const runAppSearch = (searchterm) => { + const data = {"search": searchterm} + + fetch(globalUrl+"/api/v1/apps/search", { + method: 'POST', + headers: { + 'Accept': 'application/json', + }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((response) => { + setAppSearchLoading(false) + return response.json() + }) + .then((responseJson) => { + console.log(responseJson) + }) + .catch(error => { + alert.error(error.toString()) + }); + } + const validateRemote = () => { setValidation(true)