From d60204947d888cff81b18c37dcf93a73598fe3d4 Mon Sep 17 00:00:00 2001 From: Maciej Wereski Date: Wed, 27 Sep 2017 12:49:13 +0200 Subject: [PATCH] HTTP API: Implement closing requests Change-Id: Iae3aec7a4a4d3cf34e4b3b01b9121efcece94436 Signed-off-by: Maciej Wereski --- server/api/v1/api.go | 6 ++++ server/api/v1/api_test.go | 42 ++++++++++++++++++++++ server/api/v1/error.go | 15 ++++++++ server/api/v1/handlers.go | 9 ++++- server/api/v1/handlers_test.go | 23 +++++++++--- server/api/v1/testdata/close-req-POST.json | 1 - server/api/v1/testdata/close-req-bad-id-POST.json | 1 + server/api/v1/testdata/close-req-missing-POST.json | 1 + server/api/v1/testdata/close-req-valid-POST.json | 0 9 files changed, 92 insertions(+), 6 deletions(-) delete mode 100644 server/api/v1/testdata/close-req-POST.json create mode 100644 server/api/v1/testdata/close-req-bad-id-POST.json create mode 100644 server/api/v1/testdata/close-req-missing-POST.json create mode 100644 server/api/v1/testdata/close-req-valid-POST.json diff --git a/server/api/v1/api.go b/server/api/v1/api.go index c923096..9022419 100644 --- a/server/api/v1/api.go +++ b/server/api/v1/api.go @@ -23,6 +23,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" . "git.tizen.org/tools/boruta" "github.com/dimfeld/httptreemux" @@ -146,3 +147,8 @@ func NewAPI(router *httptreemux.TreeMux, requestsAPI Requests) (api *API) { return } + +func parseReqID(id string) (ReqID, error) { + reqid, err := strconv.ParseUint(id, 10, 64) + return ReqID(reqid), err +} diff --git a/server/api/v1/api_test.go b/server/api/v1/api_test.go index 32ca17d..f532063 100644 --- a/server/api/v1/api_test.go +++ b/server/api/v1/api_test.go @@ -28,6 +28,7 @@ import ( "strings" "testing" + . "git.tizen.org/tools/boruta" "git.tizen.org/tools/boruta/mocks" "github.com/dimfeld/httptreemux" "github.com/golang/mock/gomock" @@ -36,6 +37,7 @@ import ( const ( contentTypeJSON = "application/json" + invalidID = "test" validReqJSON = `{ "ID":1, "State":"WAITING", @@ -68,6 +70,8 @@ type allMocks struct { wm *mocks.MockWorkers } +// TestTempl variables shouldn't be used directly, but rather as an input for +// testFromTempl() function. var ( // malformedJSONTestTempl may be used by functions that need to check // for malformed JSON to initialize test. Every test must set path, name @@ -80,6 +84,30 @@ var ( contentType: contentTypeJSON, status: http.StatusBadRequest, } + + // invalidIDTestTempl may be used by functions that need to check for + // cases where malformed ID is given in a URL to initialize test. + // Every test must set path, name and method appropriately. + invalidIDTestTempl = &requestTest{ + name: "bad-id", + path: "", + methods: []string{}, + json: ``, + contentType: contentTypeJSON, + status: http.StatusBadRequest, + } + + // notFoundTestTempl may be used by functions that need to check for + // not existing requests to initialize test. Every test must set path, + // name and method appropriately. + notFoundTestTempl = &requestTest{ + name: "missing", + path: "", + methods: []string{}, + json: ``, + contentType: contentTypeJSON, + status: http.StatusNotFound, + } ) func TestMain(m *testing.M) { @@ -201,11 +229,16 @@ func TestNewServerError(t *testing.T) { Err: "invalid request: more details", Status: http.StatusBadRequest, } + notFound := &serverError{ + Err: NotFoundError("Fern Flower").Error(), + Status: http.StatusNotFound, + } assert.Equal(badRequest, newServerError(errors.New("foo"))) assert.Equal(missingBody, newServerError(io.EOF)) assert.Equal(notImplemented, newServerError(ErrNotImplemented)) assert.Equal(internalErr, newServerError(ErrInternalServerError)) assert.Equal(customErr, newServerError(ErrBadRequest, "more details")) + assert.Equal(notFound, newServerError(NotFoundError("Fern Flower"))) assert.Nil(newServerError(nil)) } @@ -270,3 +303,12 @@ func TestPanicHandler(t *testing.T) { assert.Equal(expected, body, tcaseErrStr) } } + +func TestParseReqID(t *testing.T) { + assert := assert.New(t) + reqid, err := parseReqID("1") + assert.Nil(err) + assert.Equal(ReqID(1), reqid) + _, err = parseReqID(invalidID) + assert.NotNil(err) +} diff --git a/server/api/v1/error.go b/server/api/v1/error.go index 31ae7ba..a5867d1 100644 --- a/server/api/v1/error.go +++ b/server/api/v1/error.go @@ -23,6 +23,8 @@ import ( "errors" "io" "net/http" + + . "git.tizen.org/tools/boruta" ) // serverError represents error that occured while creating response. @@ -42,8 +44,17 @@ var ( ErrInternalServerError = errors.New("internal server error") // ErrBadRequest is returned when User request is invalid. ErrBadRequest = errors.New("invalid request") + // ErrBadID is returned when User provided ID which can't be parsed into + // uint. + ErrBadID = errors.New("ID provided in URL isn't valid") ) +// isNotFoundError returns true if passed error is of type NotFoundError. +func isNotFoundError(err error) bool { + _, ok := err.(NotFoundError) + return ok +} + // newServerError provides pointer to initialized serverError. func newServerError(err error, details ...string) (ret *serverError) { if err == nil { @@ -56,6 +67,10 @@ func newServerError(err error, details ...string) (ret *serverError) { if len(details) > 0 { ret.Err += ": " + details[0] } + if isNotFoundError(err) { + ret.Status = http.StatusNotFound + return + } switch err { case ErrNotImplemented: diff --git a/server/api/v1/handlers.go b/server/api/v1/handlers.go index 494bdd8..40702bb 100644 --- a/server/api/v1/handlers.go +++ b/server/api/v1/handlers.go @@ -48,7 +48,14 @@ func (api *API) newRequestHandler(r *http.Request, ps map[string]string) respons // closeRequestHandler parses HTTP request for closing existing Boruta request // and calls CloseRequest(). func (api *API) closeRequestHandler(r *http.Request, ps map[string]string) responseData { - return newServerError(ErrNotImplemented, "close request") + defer r.Body.Close() + + reqid, err := parseReqID(ps["id"]) + if err != nil { + return newServerError(ErrBadID) + } + + return newServerError(api.reqs.CloseRequest(reqid)) } // updateRequestHandler parses HTTP request for modification of existing Boruta diff --git a/server/api/v1/handlers_test.go b/server/api/v1/handlers_test.go index 9aa92d1..0371651 100644 --- a/server/api/v1/handlers_test.go +++ b/server/api/v1/handlers_test.go @@ -17,6 +17,7 @@ package v1 import ( "encoding/json" + "fmt" "net/http" "testing" @@ -72,15 +73,29 @@ func TestCloseRequestHandler(t *testing.T) { assert, m, api := initTest(t) defer m.finish() + methods := []string{http.MethodPost} + pathfmt := "/api/v1/reqs/%s/close" + prefix := "close-req-" + + invalidIDTest := testFromTempl(invalidIDTestTempl, prefix, fmt.Sprintf(pathfmt, invalidID), methods...) + notFoundTest := testFromTempl(notFoundTestTempl, prefix, fmt.Sprintf(pathfmt, "2"), methods...) + m.rq.EXPECT().CloseRequest(ReqID(1)).Return(nil) + m.rq.EXPECT().CloseRequest(ReqID(2)).Return(NotFoundError("Request")) + tests := []requestTest{ + // Close valid request in state WAIT (cancel). { - name: "close-req", - path: "/api/v1/reqs/8/close", - methods: []string{http.MethodPost}, + name: prefix + "valid", + path: fmt.Sprintf(pathfmt, "1"), + methods: methods, json: ``, contentType: contentTypeJSON, - status: http.StatusNotImplemented, + status: http.StatusNoContent, }, + // Try to close request with invalid ID. + invalidIDTest, + // Try to close request which doesn't exist. + notFoundTest, } runTests(assert, api, tests) diff --git a/server/api/v1/testdata/close-req-POST.json b/server/api/v1/testdata/close-req-POST.json deleted file mode 100644 index 2938a2a..0000000 --- a/server/api/v1/testdata/close-req-POST.json +++ /dev/null @@ -1 +0,0 @@ -{"error":"not implemented yet: close request"} \ No newline at end of file diff --git a/server/api/v1/testdata/close-req-bad-id-POST.json b/server/api/v1/testdata/close-req-bad-id-POST.json new file mode 100644 index 0000000..9a1b539 --- /dev/null +++ b/server/api/v1/testdata/close-req-bad-id-POST.json @@ -0,0 +1 @@ +{"error":"invalid request: ID provided in URL isn't valid"} \ No newline at end of file diff --git a/server/api/v1/testdata/close-req-missing-POST.json b/server/api/v1/testdata/close-req-missing-POST.json new file mode 100644 index 0000000..bb60840 --- /dev/null +++ b/server/api/v1/testdata/close-req-missing-POST.json @@ -0,0 +1 @@ +{"error":"Request not found"} \ No newline at end of file diff --git a/server/api/v1/testdata/close-req-valid-POST.json b/server/api/v1/testdata/close-req-valid-POST.json new file mode 100644 index 0000000..e69de29 -- 2.7.4