From 99e689b7bde1aa7b119b48df1de21213ff5f5c54 Mon Sep 17 00:00:00 2001 From: Maciej Wereski Date: Fri, 22 Dec 2017 10:18:29 +0100 Subject: [PATCH] Add custom Boruta HTTP headers This patch introduces two custom Boruta headers to replies for some REST API functions: * Boruta-Request-Status - contains current status of request. It is convenient to request HEAD on GetRequestInfo path to check only status of given request. * Boruta-Request-Count - contains number of requests returned for Request List/Filter operation. * Boruta-Worker-Status - contains current status of worker. * Boruta-Worker-Count - conttains number of workers returned for Worker List/Filter operation. Change-Id: I871c63058b2690bac50046f0dc500c795843b958 Signed-off-by: Maciej Wereski --- http/server/api/v1/api.go | 20 +++++++++++++++----- http/server/api/v1/api_test.go | 10 +++++++++- http/server/api/v1/handlers_test.go | 31 +++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/http/server/api/v1/api.go b/http/server/api/v1/api.go index bc897e1..0fa8dd5 100644 --- a/http/server/api/v1/api.go +++ b/http/server/api/v1/api.go @@ -71,17 +71,27 @@ func routerSetHandler(grp *httptreemux.Group, path string, fn reqHandler, return func(w http.ResponseWriter, r *http.Request, ps map[string]string) { status := status - rdata := handle(r, ps) - if data, isErr := rdata.(*util.ServerError); isErr && - data != nil { - status = data.Status + data := handle(r, ps) + switch data := data.(type) { + case *util.ServerError: + if data != nil { + status = data.Status + } + case ReqInfo: + w.Header().Add("Boruta-Request-State", string(data.State)) + case []ReqInfo: + w.Header().Add("Boruta-Request-Count", strconv.Itoa(len(data))) + case WorkerInfo: + w.Header().Add("Boruta-Worker-State", string(data.State)) + case []WorkerInfo: + w.Header().Add("Boruta-Worker-Count", strconv.Itoa(len(data))) } if status != http.StatusNoContent { w.Header().Set("Content-Type", "application/json") } w.WriteHeader(status) if status != http.StatusNoContent { - w.Write(jsonMustMarshal(rdata)) + w.Write(jsonMustMarshal(data)) } } } diff --git a/http/server/api/v1/api_test.go b/http/server/api/v1/api_test.go index e53dbd6..e8cf5b2 100644 --- a/http/server/api/v1/api_test.go +++ b/http/server/api/v1/api_test.go @@ -65,6 +65,7 @@ type requestTest struct { json string contentType string status int + header http.Header } type allMocks struct { @@ -213,10 +214,17 @@ func runTests(assert *assert.Assertions, r *httptreemux.TreeMux, tests []request if update { continue } + // check Boruta HTTP headers + if test.header != nil && len(test.header) > 0 { + for k, v := range test.header { + assert.Contains(resp.Header, k, tcaseErrStr+" (header)") + assert.Equal(v, resp.Header[k], tcaseErrStr+" (header)") + } + } // check result JSON expected, err := ioutil.ReadFile(tdata) assert.Nil(err, tcaseErrStr) - assert.JSONEq(string(expected), string(body), tcaseErrStr) + assert.JSONEq(string(expected), string(body), tcaseErrStr+" (JSON)") } } } diff --git a/http/server/api/v1/handlers_test.go b/http/server/api/v1/handlers_test.go index 29a2d4e..e5746c6 100644 --- a/http/server/api/v1/handlers_test.go +++ b/http/server/api/v1/handlers_test.go @@ -194,6 +194,8 @@ func TestGetRequestInfoHandler(t *testing.T) { var req ReqInfo err := json.Unmarshal([]byte(validReqJSON), &req) assert.Nil(err) + header := make(http.Header) + header.Set("Boruta-Request-State", "WAITING") notFoundTest := testFromTempl(notFoundTestTempl, prefix, path+"2", methods...) invalidIDTest := testFromTempl(invalidIDTestTempl, prefix, path+invalidID, methods...) @@ -209,6 +211,7 @@ func TestGetRequestInfoHandler(t *testing.T) { json: ``, contentType: contentTypeJSON, status: http.StatusOK, + header: header, }, // Try to get request information of request that doesn't exist. notFoundTest, @@ -245,13 +248,19 @@ func TestListRequestsHandler(t *testing.T) { validFilter := util.NewRequestFilter("WAIT", "") m.rq.EXPECT().ListRequests(validFilter).Return(reqs[:2], nil) + validHeader := make(http.Header) + validHeader.Set("Boruta-Request-Count", "2") emptyFilter := util.NewRequestFilter("", "") m.rq.EXPECT().ListRequests(emptyFilter).Return(reqs, nil).Times(2) m.rq.EXPECT().ListRequests(nil).Return(reqs, nil).Times(3) + allHeader := make(http.Header) + allHeader.Set("Boruta-Request-Count", "4") missingFilter := util.NewRequestFilter("INVALID", "") m.rq.EXPECT().ListRequests(missingFilter).Return([]ReqInfo{}, nil) + missingHeader := make(http.Header) + missingHeader.Set("Boruta-Request-Count", "0") // Currently ListRequests doesn't return any error hence the meaningless values. badFilter := util.NewRequestFilter("FAIL", "-1") @@ -266,6 +275,7 @@ func TestListRequestsHandler(t *testing.T) { json: string(jsonMustMarshal(validFilter)), contentType: contentTypeJSON, status: http.StatusOK, + header: validHeader, }, // List all requests. { @@ -275,6 +285,7 @@ func TestListRequestsHandler(t *testing.T) { json: ``, contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Empty body - list all requests. { @@ -284,6 +295,7 @@ func TestListRequestsHandler(t *testing.T) { json: "", contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Nil filter - list all requests (same as emptyFilter). { @@ -293,6 +305,7 @@ func TestListRequestsHandler(t *testing.T) { json: string(jsonMustMarshal(nil)), contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Empty filter - list all requests. { @@ -302,6 +315,7 @@ func TestListRequestsHandler(t *testing.T) { json: string(jsonMustMarshal(emptyFilter)), contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // No matches { @@ -311,6 +325,7 @@ func TestListRequestsHandler(t *testing.T) { json: string(jsonMustMarshal(missingFilter)), contentType: contentTypeJSON, status: http.StatusOK, + header: missingHeader, }, // Error { @@ -425,14 +440,20 @@ func TestListWorkersHandler(t *testing.T) { Capabilities: map[string]string{"architecture": "AArch64"}, } m.wm.EXPECT().ListWorkers(validFilter.Groups, validFilter.Capabilities).Return(workers[:2], nil) + validHeader := make(http.Header) + validHeader.Set("Boruta-Worker-Count", "2") m.wm.EXPECT().ListWorkers(nil, nil).Return(workers, nil).MinTimes(1) m.wm.EXPECT().ListWorkers(Groups{}, nil).Return(workers, nil) m.wm.EXPECT().ListWorkers(nil, make(Capabilities)).Return(workers, nil) + allHeader := make(http.Header) + allHeader.Set("Boruta-Worker-Count", "4") missingFilter := util.WorkersFilter{ Groups: Groups{"Fern Flower"}, } + missingHeader := make(http.Header) + missingHeader.Set("Boruta-Worker-Count", "0") m.wm.EXPECT().ListWorkers(missingFilter.Groups, missingFilter.Capabilities).Return([]WorkerInfo{}, nil) // Currently ListWorkers doesn't return any error hence the meaningless values. @@ -450,6 +471,7 @@ func TestListWorkersHandler(t *testing.T) { json: string(jsonMustMarshal(validFilter)), contentType: contentTypeJSON, status: http.StatusOK, + header: validHeader, }, // List all requests. { @@ -459,6 +481,7 @@ func TestListWorkersHandler(t *testing.T) { json: ``, contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Empty body - list all requests. { @@ -468,6 +491,7 @@ func TestListWorkersHandler(t *testing.T) { json: ``, contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Empty filter (all nil) - list all requests. { @@ -477,6 +501,7 @@ func TestListWorkersHandler(t *testing.T) { json: string(jsonMustMarshal(util.WorkersFilter{nil, nil})), contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Empty filter (nil groups) - list all requests. { @@ -486,6 +511,7 @@ func TestListWorkersHandler(t *testing.T) { json: string(jsonMustMarshal(util.WorkersFilter{nil, make(Capabilities)})), contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // Empty filter (nil caps) - list all requests. { @@ -495,6 +521,7 @@ func TestListWorkersHandler(t *testing.T) { json: string(jsonMustMarshal(util.WorkersFilter{Groups{}, nil})), contentType: contentTypeJSON, status: http.StatusOK, + header: allHeader, }, // No matches. { @@ -504,6 +531,7 @@ func TestListWorkersHandler(t *testing.T) { json: string(jsonMustMarshal(missingFilter)), contentType: contentTypeJSON, status: http.StatusOK, + header: missingHeader, }, // Error. { @@ -533,6 +561,8 @@ func TestGetWorkerInfoHandler(t *testing.T) { missingErr := NotFoundError("Worker") m.wm.EXPECT().GetWorkerInfo(WorkerUUID(validUUID)).Return(worker, nil).Times(2) m.wm.EXPECT().GetWorkerInfo(WorkerUUID(missingUUID)).Return(WorkerInfo{}, missingErr).Times(2) + header := make(http.Header) + header.Set("Boruta-Worker-State", "IDLE") tests := []requestTest{ { @@ -542,6 +572,7 @@ func TestGetWorkerInfoHandler(t *testing.T) { json: ``, contentType: contentTypeJSON, status: http.StatusOK, + header: header, }, { name: prefix + "bad-uuid", -- 2.7.4