Fix bugs in getJobs REST Api endpoint

Relates to #94 and #95
This commit is contained in:
Jan Eitzinger 2023-02-25 07:21:54 +01:00
parent a4911e3590
commit ae5a73335d

View File

@ -50,11 +50,11 @@ import (
// @name X-Auth-Token // @name X-Auth-Token
type RestApi struct { type RestApi struct {
JobRepository *repository.JobRepository JobRepository *repository.JobRepository
Resolver *graph.Resolver Resolver *graph.Resolver
Authentication *auth.Authentication Authentication *auth.Authentication
MachineStateDir string MachineStateDir string
RepositoryMutex sync.Mutex RepositoryMutex sync.Mutex
} }
func (api *RestApi) MountRoutes(r *mux.Router) { func (api *RestApi) MountRoutes(r *mux.Router) {
@ -117,6 +117,13 @@ type DeleteJobApiRequest struct {
StartTime *int64 `json:"startTime" example:"1649723812"` // Start Time of job as epoch StartTime *int64 `json:"startTime" example:"1649723812"` // Start Time of job as epoch
} }
// GetJobsApiResponse model
type GetJobsApiResponse struct {
Jobs []*schema.JobMeta `json:"jobs"` // Array of jobs
Items int `json:"items"` // Number of jobs returned
Page int `json:"page"` // Page id returned
}
// ErrorResponse model // ErrorResponse model
type ErrorResponse struct { type ErrorResponse struct {
// Statustext of Errorcode // Statustext of Errorcode
@ -161,7 +168,7 @@ func decode(r io.Reader, val interface{}) error {
// @param items-per-page query int false "Items per page (Default: 25)" // @param items-per-page query int false "Items per page (Default: 25)"
// @param page query int false "Page Number (Default: 1)" // @param page query int false "Page Number (Default: 1)"
// @param with-metadata query bool false "Include metadata (e.g. jobScript) in response" // @param with-metadata query bool false "Include metadata (e.g. jobScript) in response"
// @success 200 {array} schema.Job "Array of matching jobs" // @success 200 {object} api.GetJobsApiResponse "Job array and page info"
// @failure 400 {object} api.ErrorResponse "Bad Request" // @failure 400 {object} api.ErrorResponse "Bad Request"
// @failure 401 {object} api.ErrorResponse "Unauthorized" // @failure 401 {object} api.ErrorResponse "Unauthorized"
// @failure 500 {object} api.ErrorResponse "Internal Server Error" // @failure 500 {object} api.ErrorResponse "Internal Server Error"
@ -184,7 +191,8 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
for _, s := range vals { for _, s := range vals {
state := schema.JobState(s) state := schema.JobState(s)
if !state.Valid() { if !state.Valid() {
http.Error(rw, "invalid query parameter value: state", http.StatusBadRequest) handleError(fmt.Errorf("invalid query parameter value: state"),
http.StatusBadRequest, rw)
return return
} }
filter.State = append(filter.State, state) filter.State = append(filter.State, state)
@ -194,17 +202,18 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
case "start-time": case "start-time":
st := strings.Split(vals[0], "-") st := strings.Split(vals[0], "-")
if len(st) != 2 { if len(st) != 2 {
http.Error(rw, "invalid query parameter value: startTime", http.StatusBadRequest) handleError(fmt.Errorf("invalid query parameter value: startTime"),
http.StatusBadRequest, rw)
return return
} }
from, err := strconv.ParseInt(st[0], 10, 64) from, err := strconv.ParseInt(st[0], 10, 64)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest) handleError(err, http.StatusBadRequest, rw)
return return
} }
to, err := strconv.ParseInt(st[1], 10, 64) to, err := strconv.ParseInt(st[1], 10, 64)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest) handleError(err, http.StatusBadRequest, rw)
return return
} }
ufrom, uto := time.Unix(from, 0), time.Unix(to, 0) ufrom, uto := time.Unix(from, 0), time.Unix(to, 0)
@ -212,28 +221,29 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
case "page": case "page":
x, err := strconv.Atoi(vals[0]) x, err := strconv.Atoi(vals[0])
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest) handleError(err, http.StatusBadRequest, rw)
return return
} }
page.Page = x page.Page = x
case "items-per-page": case "items-per-page":
x, err := strconv.Atoi(vals[0]) x, err := strconv.Atoi(vals[0])
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest) handleError(err, http.StatusBadRequest, rw)
return return
} }
page.ItemsPerPage = x page.ItemsPerPage = x
case "with-metadata": case "with-metadata":
withMetadata = true withMetadata = true
default: default:
http.Error(rw, "invalid query parameter: "+key, http.StatusBadRequest) handleError(fmt.Errorf("invalid query parameter: %s", key),
http.StatusBadRequest, rw)
return return
} }
} }
jobs, err := api.JobRepository.QueryJobs(r.Context(), []*model.JobFilter{filter}, page, order) jobs, err := api.JobRepository.QueryJobs(r.Context(), []*model.JobFilter{filter}, page, order)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) handleError(err, http.StatusInternalServerError, rw)
return return
} }
@ -241,7 +251,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
for _, job := range jobs { for _, job := range jobs {
if withMetadata { if withMetadata {
if _, err := api.JobRepository.FetchMetadata(job); err != nil { if _, err := api.JobRepository.FetchMetadata(job); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) handleError(err, http.StatusInternalServerError, rw)
return return
} }
} }
@ -254,7 +264,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
res.Tags, err = api.JobRepository.GetTags(&job.ID) res.Tags, err = api.JobRepository.GetTags(&job.ID)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) handleError(err, http.StatusInternalServerError, rw)
return return
} }
@ -262,7 +272,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
res.Statistics, err = archive.GetStatistics(job) res.Statistics, err = archive.GetStatistics(job)
if err != nil { if err != nil {
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) handleError(err, http.StatusInternalServerError, rw)
return return
} }
} }
@ -272,12 +282,18 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
} }
log.Debugf("/api/jobs: %d jobs returned", len(results)) log.Debugf("/api/jobs: %d jobs returned", len(results))
rw.Header().Add("Content-Type", "application/json")
bw := bufio.NewWriter(rw) bw := bufio.NewWriter(rw)
defer bw.Flush() defer bw.Flush()
if err := json.NewEncoder(bw).Encode(map[string]interface{}{
"jobs": results, payload := GetJobsApiResponse{
}); err != nil { Jobs: results,
http.Error(rw, err.Error(), http.StatusInternalServerError) Items: page.ItemsPerPage,
Page: page.Page,
}
if err := json.NewEncoder(bw).Encode(payload); err != nil {
handleError(err, http.StatusInternalServerError, rw)
return return
} }
} }
@ -793,7 +809,8 @@ func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) {
me := auth.GetUser(r.Context()) me := auth.GetUser(r.Context())
if !me.HasRole(auth.RoleAdmin) { if !me.HasRole(auth.RoleAdmin) {
if username != me.Username { if username != me.Username {
http.Error(rw, "Only admins are allowed to sign JWTs not for themselves", http.StatusForbidden) http.Error(rw, "Only admins are allowed to sign JWTs not for themselves",
http.StatusForbidden)
return return
} }
} }