diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index 34d0d2b..a05a73c 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -13,7 +13,6 @@ import ( "io" "net" "net/http" - "net/url" "os" "os/signal" "runtime" @@ -297,66 +296,7 @@ func main() { // Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project. secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) { - if search := r.URL.Query().Get("searchId"); search != "" { - splitSearch := strings.Split(search, ":") - - if (len(splitSearch) == 2) { - switch strings.Trim(splitSearch[0], " ") { - case "jobId": - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery - return - case "jobName": - http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery - return - case "projectId": - project, _ := api.JobRepository.FindProject(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: projectId - if project != "" { - http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(project), http.StatusTemporaryRedirect) - return - } else { - http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table - } - case "username": - username, _ := api.JobRepository.FindUser(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: username - if username != "" { - http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) - return - } else { - http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table - } - default: - http.Error(rw, "'searchId' type parameter unknown", http.StatusBadRequest) - } - - } else if (len(splitSearch) == 1) { - jobname, username, project, err := api.JobRepository.FindJobnameOrUserOrProject(r.Context(), strings.Trim(search, " ")) // Determine Access within - - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - - if username != "" { - http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) // User: Redirect to user page - return - } else if (project != "") { - http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // projectId (equal) - return - } else if (jobname != "") { - http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // JobName (contains) - return - } else { - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // No Result: Probably jobId - return - } - - } else { - http.Error(rw, "'searchId' query parameter malformed", http.StatusBadRequest) - } - - } else { - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) - } + routerConfig.HandleSearchBar(rw, r, api) }) // Mount all /monitoring/... and /api/... routes. diff --git a/docs/searchbar.md b/docs/searchbar.md new file mode 100644 index 0000000..77d5f75 --- /dev/null +++ b/docs/searchbar.md @@ -0,0 +1,24 @@ +## Docs for ClusterCockpit Searchbar + +### Usage + +* Searchtags are implemented as `type:` search-string + * Types `jobId, jobName, projectId, username` for roles `admin` and `support` + * Types `jobId, jobName` for role `user` + * Examples: + * `jobName:myJob12` + * `jobId:123456` + * `username:abcd100` +* If no searchTag used: Best guess search with the following hierarchy + * `jobId -> username -> projectId -> jobName` +* Simple HTML Error if ... + * Best guess search fails -> 'Not Found' + * Query `type` is unknown + * More than two colons in string -> 'malformed' +* Spaces trimmed (both for searchTag and queryString) + * ` job12` == `job12` + * `projectID : abcd ` == `projectId:abcd` +* jobId-Query now redirects to table + * Allows multiple jobs from different systems, but with identical job-id to be found +* jobName-Query works with a part of the jobName-String (e.g. jobName:myjob for jobName myjob_cluster1) + * JobName GQL Query is resolved as matching the query as a part of the whole metaData-JSON in the SQL DB. diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index ccceb58..ff3be0b 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -13,6 +13,7 @@ import ( "time" "github.com/ClusterCockpit/cc-backend/internal/auth" + "github.com/ClusterCockpit/cc-backend/internal/api" "github.com/ClusterCockpit/cc-backend/internal/graph" "github.com/ClusterCockpit/cc-backend/internal/graph/model" "github.com/ClusterCockpit/cc-backend/internal/repository" @@ -297,3 +298,66 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri }) } } + +func HandleSearchBar(rw http.ResponseWriter, r *http.Request, api *api.RestApi) { + if search := r.URL.Query().Get("searchId"); search != "" { + splitSearch := strings.Split(search, ":") + + if (len(splitSearch) == 2) { + switch strings.Trim(splitSearch[0], " ") { + case "jobId": + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + return + case "jobName": + http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + return + case "projectId": + project, _ := api.JobRepository.FindProject(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: projectId + if project != "" { + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(project), http.StatusTemporaryRedirect) + return + } else { + http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table + } + case "username": + username, _ := api.JobRepository.FindUser(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: username + if username != "" { + http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) + return + } else { + http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table + } + default: + http.Error(rw, "'searchId' type parameter unknown", http.StatusBadRequest) + } + + } else if (len(splitSearch) == 1) { + jobname, username, project, err := api.JobRepository.FindJobnameOrUserOrProject(r.Context(), strings.Trim(search, " ")) // Determine Access within + + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + if username != "" { + http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) // User: Redirect to user page + return + } else if (project != "") { + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // projectId (equal) + return + } else if (jobname != "") { + http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // JobName (contains) + return + } else { + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // No Result: Probably jobId + return + } + + } else { + http.Error(rw, "'searchId' query parameter malformed", http.StatusBadRequest) + } + + } else { + http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) + } +}