HTTP API: Implement closing requests
authorMaciej Wereski <m.wereski@partner.samsung.com>
Wed, 27 Sep 2017 10:49:13 +0000 (12:49 +0200)
committerMaciej Wereski <m.wereski@partner.samsung.com>
Tue, 5 Jun 2018 09:26:40 +0000 (11:26 +0200)
Change-Id: Iae3aec7a4a4d3cf34e4b3b01b9121efcece94436
Signed-off-by: Maciej Wereski <m.wereski@partner.samsung.com>
server/api/v1/api.go
server/api/v1/api_test.go
server/api/v1/error.go
server/api/v1/handlers.go
server/api/v1/handlers_test.go
server/api/v1/testdata/close-req-POST.json [deleted file]
server/api/v1/testdata/close-req-bad-id-POST.json [new file with mode: 0644]
server/api/v1/testdata/close-req-missing-POST.json [new file with mode: 0644]
server/api/v1/testdata/close-req-valid-POST.json [new file with mode: 0644]

index c923096..9022419 100644 (file)
@@ -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
+}
index 32ca17d..f532063 100644 (file)
@@ -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)
+}
index 31ae7ba..a5867d1 100644 (file)
@@ -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:
index 494bdd8..40702bb 100644 (file)
@@ -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
index 9aa92d1..0371651 100644 (file)
@@ -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 (file)
index 2938a2a..0000000
+++ /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 (file)
index 0000000..9a1b539
--- /dev/null
@@ -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 (file)
index 0000000..bb60840
--- /dev/null
@@ -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 (file)
index 0000000..e69de29