HTTP API: Implement filtering workers
authorMaciej Wereski <m.wereski@partner.samsung.com>
Tue, 17 Oct 2017 10:53:38 +0000 (12:53 +0200)
committerMaciej Wereski <m.wereski@partner.samsung.com>
Tue, 5 Jun 2018 10:48:28 +0000 (12:48 +0200)
Change-Id: I93137f8e3c7dc66c859de671c316a443ec4aad9e
Signed-off-by: Maciej Wereski <m.wereski@partner.samsung.com>
14 files changed:
server/api/v1/api_test.go
server/api/v1/filter.go
server/api/v1/handlers.go
server/api/v1/handlers_test.go
server/api/v1/testdata/filter-workers-bad-filter-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-empty-body-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-empty-filter-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-empty2-filter-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-empty3-filter-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-malformed-json-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-nomatch-POST.json [new file with mode: 0644]
server/api/v1/testdata/filter-workers-valid-filter-POST.json [new file with mode: 0644]
server/api/v1/testdata/list-workers-all-GET.json
server/api/v1/testdata/list-workers-filter-POST.json [deleted file]

index 0883f7d..23f53f2 100644 (file)
@@ -146,14 +146,20 @@ func testFromTempl(templ *requestTest, name string, path string,
        return
 }
 
-func newWorker(uuid string, state WorkerState) WorkerInfo {
-       caps := make(Capabilities)
+func newWorker(uuid string, state WorkerState, groups Groups, caps Capabilities) (w WorkerInfo) {
+       if caps == nil {
+               caps = make(Capabilities)
+       }
        caps["UUID"] = uuid
-       return WorkerInfo{
+       w = WorkerInfo{
                WorkerUUID: WorkerUUID(uuid),
                State:      state,
                Caps:       caps,
        }
+       if len(groups) != 0 {
+               w.Groups = groups
+       }
+       return
 }
 
 func runTests(assert *assert.Assertions, api *API, tests []requestTest) {
index 3fb830a..e98c499 100644 (file)
@@ -25,6 +25,12 @@ import (
        . "git.tizen.org/tools/boruta"
 )
 
+// WorkersFilter contains Groups and Capabilities to be used to filter workers.
+type WorkersFilter struct {
+       Groups
+       Capabilities
+}
+
 // RequestFilter implements ListFilter interface. Currently it is possible to
 // filter by state and priority.
 type RequestFilter struct {
index 850c009..2b92504 100644 (file)
@@ -169,7 +169,22 @@ func (api *API) prolongAccessHandler(r *http.Request, ps map[string]string) resp
 
 // listWorkersHandler parses HTTP request for listing workers and calls ListWorkers().
 func (api *API) listWorkersHandler(r *http.Request, ps map[string]string) responseData {
-       return newServerError(ErrNotImplemented, "list workers")
+       var filter WorkersFilter
+       defer r.Body.Close()
+
+       if r.Method == http.MethodPost {
+               err := json.NewDecoder(r.Body).Decode(&filter)
+               if err != nil && err != io.EOF {
+                       return newServerError(err)
+               }
+       }
+
+       workers, err := api.workers.ListWorkers(filter.Groups, filter.Capabilities)
+       if err != nil {
+               return newServerError(err)
+       }
+
+       return workers
 }
 
 // getWorkerInfoHandler parses HTTP request for obtaining worker information and
index 6fba4eb..d5cd366 100644 (file)
@@ -402,23 +402,118 @@ func TestListWorkersHandler(t *testing.T) {
        assert, m, api := initTest(t)
        defer m.finish()
 
+       armCaps := make(Capabilities)
+       armCaps["architecture"] = "AArch64"
+       riscvCaps := make(Capabilities)
+       riscvCaps["architecture"] = "RISC-V"
+
+       workers := []WorkerInfo{
+               newWorker("0", IDLE, Groups{"Lędzianie"}, armCaps),
+               newWorker("1", FAIL, Groups{"Malinowy Chruśniak"}, armCaps),
+               newWorker("2", IDLE, Groups{"Malinowy Chruśniak", "Lędzianie"}, riscvCaps),
+               newWorker("3", FAIL, Groups{"Malinowy Chruśniak"}, riscvCaps),
+       }
+
+       methods := []string{http.MethodPost}
+       prefix := "filter-workers-"
+       filterPath := "/api/v1/workers/list"
+       malformedJSONTest := testFromTempl(malformedJSONTestTempl, prefix, filterPath, methods...)
+
+       validFilter := WorkersFilter{
+               Groups:       Groups{"Lędzianie"},
+               Capabilities: map[string]string{"architecture": "AArch64"},
+       }
+       m.wm.EXPECT().ListWorkers(validFilter.Groups, validFilter.Capabilities).Return(workers[:2], nil)
+
+       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)
+
+       missingFilter := WorkersFilter{
+               Groups: Groups{"Fern Flower"},
+       }
+       m.wm.EXPECT().ListWorkers(missingFilter.Groups, missingFilter.Capabilities).Return([]WorkerInfo{}, nil)
+
+       // Currently ListWorkers doesn't return any error hence the meaningless values.
+       badFilter := WorkersFilter{
+               Groups: Groups{"Oops"},
+       }
+       m.wm.EXPECT().ListWorkers(badFilter.Groups, badFilter.Capabilities).Return([]WorkerInfo{}, errors.New("foo bar: pizza failed"))
+
        tests := []requestTest{
+               // Valid filter - list some requests.
+               {
+                       name:        prefix + "valid-filter",
+                       path:        filterPath,
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(validFilter)),
+                       contentType: contentTypeJSON,
+                       status:      http.StatusOK,
+               },
+               // List all requests.
                {
                        name:        "list-workers-all",
                        path:        "/api/v1/workers/",
                        methods:     []string{http.MethodGet, http.MethodHead},
                        json:        ``,
                        contentType: contentTypeJSON,
-                       status:      http.StatusNotImplemented,
+                       status:      http.StatusOK,
                },
+               // Empty body - list all requests.
                {
-                       name:        "list-workers-filter",
-                       path:        "/api/v1/workers/list",
-                       methods:     []string{http.MethodPost},
+                       name:        prefix + "empty-body",
+                       path:        filterPath,
+                       methods:     methods,
                        json:        ``,
                        contentType: contentTypeJSON,
-                       status:      http.StatusNotImplemented,
+                       status:      http.StatusOK,
                },
+               // Empty filter (all nil) - list all requests.
+               {
+                       name:        prefix + "empty-filter",
+                       path:        filterPath,
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(WorkersFilter{nil, nil})),
+                       contentType: contentTypeJSON,
+                       status:      http.StatusOK,
+               },
+               // Empty filter (nil groups) - list all requests.
+               {
+                       name:        prefix + "empty2-filter",
+                       path:        filterPath,
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(WorkersFilter{nil, make(Capabilities)})),
+                       contentType: contentTypeJSON,
+                       status:      http.StatusOK,
+               },
+               // Empty filter (nil caps) - list all requests.
+               {
+                       name:        prefix + "empty3-filter",
+                       path:        filterPath,
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(WorkersFilter{Groups{}, nil})),
+                       contentType: contentTypeJSON,
+                       status:      http.StatusOK,
+               },
+               // No matches.
+               {
+                       name:        prefix + "nomatch",
+                       path:        filterPath,
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(missingFilter)),
+                       contentType: contentTypeJSON,
+                       status:      http.StatusOK,
+               },
+               // Error.
+               {
+                       name:        prefix + "bad-filter",
+                       path:        filterPath,
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(badFilter)),
+                       contentType: contentTypeJSON,
+                       status:      http.StatusBadRequest,
+               },
+               malformedJSONTest,
        }
 
        runTests(assert, api, tests)
@@ -433,7 +528,7 @@ func TestGetWorkerInfoHandler(t *testing.T) {
        methods := []string{http.MethodGet, http.MethodHead}
        notFoundTest := testFromTempl(notFoundTestTempl, prefix, path+missingUUID, methods...)
 
-       worker := newWorker(validUUID, IDLE)
+       worker := newWorker(validUUID, IDLE, Groups{}, nil)
        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)
diff --git a/server/api/v1/testdata/filter-workers-bad-filter-POST.json b/server/api/v1/testdata/filter-workers-bad-filter-POST.json
new file mode 100644 (file)
index 0000000..f6dd9fd
--- /dev/null
@@ -0,0 +1 @@
+{"error":"invalid request: foo bar: pizza failed"}
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-empty-body-POST.json b/server/api/v1/testdata/filter-workers-empty-body-POST.json
new file mode 100644 (file)
index 0000000..a7d5746
--- /dev/null
@@ -0,0 +1 @@
+[{"WorkerUUID":"0","State":"IDLE","Groups":["Lędzianie"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"1","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"2","State":"IDLE","Groups":["Malinowy Chruśniak","Lędzianie"],"Caps":{"UUID":"3","architecture":"RISC-V"}},{"WorkerUUID":"3","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"3","architecture":"RISC-V"}}]
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-empty-filter-POST.json b/server/api/v1/testdata/filter-workers-empty-filter-POST.json
new file mode 100644 (file)
index 0000000..a7d5746
--- /dev/null
@@ -0,0 +1 @@
+[{"WorkerUUID":"0","State":"IDLE","Groups":["Lędzianie"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"1","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"2","State":"IDLE","Groups":["Malinowy Chruśniak","Lędzianie"],"Caps":{"UUID":"3","architecture":"RISC-V"}},{"WorkerUUID":"3","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"3","architecture":"RISC-V"}}]
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-empty2-filter-POST.json b/server/api/v1/testdata/filter-workers-empty2-filter-POST.json
new file mode 100644 (file)
index 0000000..a7d5746
--- /dev/null
@@ -0,0 +1 @@
+[{"WorkerUUID":"0","State":"IDLE","Groups":["Lędzianie"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"1","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"2","State":"IDLE","Groups":["Malinowy Chruśniak","Lędzianie"],"Caps":{"UUID":"3","architecture":"RISC-V"}},{"WorkerUUID":"3","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"3","architecture":"RISC-V"}}]
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-empty3-filter-POST.json b/server/api/v1/testdata/filter-workers-empty3-filter-POST.json
new file mode 100644 (file)
index 0000000..a7d5746
--- /dev/null
@@ -0,0 +1 @@
+[{"WorkerUUID":"0","State":"IDLE","Groups":["Lędzianie"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"1","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"2","State":"IDLE","Groups":["Malinowy Chruśniak","Lędzianie"],"Caps":{"UUID":"3","architecture":"RISC-V"}},{"WorkerUUID":"3","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"3","architecture":"RISC-V"}}]
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-malformed-json-POST.json b/server/api/v1/testdata/filter-workers-malformed-json-POST.json
new file mode 100644 (file)
index 0000000..c59dde1
--- /dev/null
@@ -0,0 +1 @@
+{"error":"invalid request: unexpected EOF"}
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-nomatch-POST.json b/server/api/v1/testdata/filter-workers-nomatch-POST.json
new file mode 100644 (file)
index 0000000..0637a08
--- /dev/null
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/server/api/v1/testdata/filter-workers-valid-filter-POST.json b/server/api/v1/testdata/filter-workers-valid-filter-POST.json
new file mode 100644 (file)
index 0000000..020b18c
--- /dev/null
@@ -0,0 +1 @@
+[{"WorkerUUID":"0","State":"IDLE","Groups":["Lędzianie"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"1","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"1","architecture":"AArch64"}}]
\ No newline at end of file
index 7fb8fd4..a7d5746 100644 (file)
@@ -1 +1 @@
-{"error":"not implemented yet: list workers"}
\ No newline at end of file
+[{"WorkerUUID":"0","State":"IDLE","Groups":["Lędzianie"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"1","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"1","architecture":"AArch64"}},{"WorkerUUID":"2","State":"IDLE","Groups":["Malinowy Chruśniak","Lędzianie"],"Caps":{"UUID":"3","architecture":"RISC-V"}},{"WorkerUUID":"3","State":"FAILED","Groups":["Malinowy Chruśniak"],"Caps":{"UUID":"3","architecture":"RISC-V"}}]
\ No newline at end of file
diff --git a/server/api/v1/testdata/list-workers-filter-POST.json b/server/api/v1/testdata/list-workers-filter-POST.json
deleted file mode 100644 (file)
index 7fb8fd4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-{"error":"not implemented yet: list workers"}
\ No newline at end of file