// Worker - MuxPi with a target device, which executes the Jobs from the Server.
package boruta
+//go:generate mockgen -destination=mocks/mock_requests.go -package=mocks git.tizen.org/tools/boruta Requests
+//go:generate mockgen -destination=mocks/mock_workers.go -package=mocks git.tizen.org/tools/boruta Workers
+
import (
"crypto/rsa"
"net"
--- /dev/null
+// Code generated by MockGen. DO NOT EDIT.
+// Source: git.tizen.org/tools/boruta (interfaces: Requests)
+
+// Package mocks is a generated GoMock package.
+package mocks
+
+import (
+ boruta "git.tizen.org/tools/boruta"
+ gomock "github.com/golang/mock/gomock"
+ reflect "reflect"
+ time "time"
+)
+
+// MockRequests is a mock of Requests interface
+type MockRequests struct {
+ ctrl *gomock.Controller
+ recorder *MockRequestsMockRecorder
+}
+
+// MockRequestsMockRecorder is the mock recorder for MockRequests
+type MockRequestsMockRecorder struct {
+ mock *MockRequests
+}
+
+// NewMockRequests creates a new mock instance
+func NewMockRequests(ctrl *gomock.Controller) *MockRequests {
+ mock := &MockRequests{ctrl: ctrl}
+ mock.recorder = &MockRequestsMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockRequests) EXPECT() *MockRequestsMockRecorder {
+ return m.recorder
+}
+
+// AcquireWorker mocks base method
+func (m *MockRequests) AcquireWorker(arg0 boruta.ReqID) (boruta.AccessInfo, error) {
+ ret := m.ctrl.Call(m, "AcquireWorker", arg0)
+ ret0, _ := ret[0].(boruta.AccessInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// AcquireWorker indicates an expected call of AcquireWorker
+func (mr *MockRequestsMockRecorder) AcquireWorker(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireWorker", reflect.TypeOf((*MockRequests)(nil).AcquireWorker), arg0)
+}
+
+// CloseRequest mocks base method
+func (m *MockRequests) CloseRequest(arg0 boruta.ReqID) error {
+ ret := m.ctrl.Call(m, "CloseRequest", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// CloseRequest indicates an expected call of CloseRequest
+func (mr *MockRequestsMockRecorder) CloseRequest(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseRequest", reflect.TypeOf((*MockRequests)(nil).CloseRequest), arg0)
+}
+
+// GetRequestInfo mocks base method
+func (m *MockRequests) GetRequestInfo(arg0 boruta.ReqID) (boruta.ReqInfo, error) {
+ ret := m.ctrl.Call(m, "GetRequestInfo", arg0)
+ ret0, _ := ret[0].(boruta.ReqInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetRequestInfo indicates an expected call of GetRequestInfo
+func (mr *MockRequestsMockRecorder) GetRequestInfo(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequestInfo", reflect.TypeOf((*MockRequests)(nil).GetRequestInfo), arg0)
+}
+
+// ListRequests mocks base method
+func (m *MockRequests) ListRequests(arg0 boruta.ListFilter) ([]boruta.ReqInfo, error) {
+ ret := m.ctrl.Call(m, "ListRequests", arg0)
+ ret0, _ := ret[0].([]boruta.ReqInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ListRequests indicates an expected call of ListRequests
+func (mr *MockRequestsMockRecorder) ListRequests(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRequests", reflect.TypeOf((*MockRequests)(nil).ListRequests), arg0)
+}
+
+// NewRequest mocks base method
+func (m *MockRequests) NewRequest(arg0 boruta.Capabilities, arg1 boruta.Priority, arg2 boruta.UserInfo, arg3, arg4 time.Time) (boruta.ReqID, error) {
+ ret := m.ctrl.Call(m, "NewRequest", arg0, arg1, arg2, arg3, arg4)
+ ret0, _ := ret[0].(boruta.ReqID)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// NewRequest indicates an expected call of NewRequest
+func (mr *MockRequestsMockRecorder) NewRequest(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRequest", reflect.TypeOf((*MockRequests)(nil).NewRequest), arg0, arg1, arg2, arg3, arg4)
+}
+
+// ProlongAccess mocks base method
+func (m *MockRequests) ProlongAccess(arg0 boruta.ReqID) error {
+ ret := m.ctrl.Call(m, "ProlongAccess", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// ProlongAccess indicates an expected call of ProlongAccess
+func (mr *MockRequestsMockRecorder) ProlongAccess(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProlongAccess", reflect.TypeOf((*MockRequests)(nil).ProlongAccess), arg0)
+}
+
+// UpdateRequest mocks base method
+func (m *MockRequests) UpdateRequest(arg0 *boruta.ReqInfo) error {
+ ret := m.ctrl.Call(m, "UpdateRequest", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// UpdateRequest indicates an expected call of UpdateRequest
+func (mr *MockRequestsMockRecorder) UpdateRequest(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRequest", reflect.TypeOf((*MockRequests)(nil).UpdateRequest), arg0)
+}
--- /dev/null
+// Code generated by MockGen. DO NOT EDIT.
+// Source: git.tizen.org/tools/boruta (interfaces: Workers)
+
+// Package mocks is a generated GoMock package.
+package mocks
+
+import (
+ boruta "git.tizen.org/tools/boruta"
+ gomock "github.com/golang/mock/gomock"
+ reflect "reflect"
+)
+
+// MockWorkers is a mock of Workers interface
+type MockWorkers struct {
+ ctrl *gomock.Controller
+ recorder *MockWorkersMockRecorder
+}
+
+// MockWorkersMockRecorder is the mock recorder for MockWorkers
+type MockWorkersMockRecorder struct {
+ mock *MockWorkers
+}
+
+// NewMockWorkers creates a new mock instance
+func NewMockWorkers(ctrl *gomock.Controller) *MockWorkers {
+ mock := &MockWorkers{ctrl: ctrl}
+ mock.recorder = &MockWorkersMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockWorkers) EXPECT() *MockWorkersMockRecorder {
+ return m.recorder
+}
+
+// Deregister mocks base method
+func (m *MockWorkers) Deregister(arg0 boruta.WorkerUUID) error {
+ ret := m.ctrl.Call(m, "Deregister", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Deregister indicates an expected call of Deregister
+func (mr *MockWorkersMockRecorder) Deregister(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deregister", reflect.TypeOf((*MockWorkers)(nil).Deregister), arg0)
+}
+
+// GetWorkerInfo mocks base method
+func (m *MockWorkers) GetWorkerInfo(arg0 boruta.WorkerUUID) (boruta.WorkerInfo, error) {
+ ret := m.ctrl.Call(m, "GetWorkerInfo", arg0)
+ ret0, _ := ret[0].(boruta.WorkerInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetWorkerInfo indicates an expected call of GetWorkerInfo
+func (mr *MockWorkersMockRecorder) GetWorkerInfo(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkerInfo", reflect.TypeOf((*MockWorkers)(nil).GetWorkerInfo), arg0)
+}
+
+// ListWorkers mocks base method
+func (m *MockWorkers) ListWorkers(arg0 boruta.Groups, arg1 boruta.Capabilities) ([]boruta.WorkerInfo, error) {
+ ret := m.ctrl.Call(m, "ListWorkers", arg0, arg1)
+ ret0, _ := ret[0].([]boruta.WorkerInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ListWorkers indicates an expected call of ListWorkers
+func (mr *MockWorkersMockRecorder) ListWorkers(arg0, arg1 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkers", reflect.TypeOf((*MockWorkers)(nil).ListWorkers), arg0, arg1)
+}
+
+// SetGroups mocks base method
+func (m *MockWorkers) SetGroups(arg0 boruta.WorkerUUID, arg1 boruta.Groups) error {
+ ret := m.ctrl.Call(m, "SetGroups", arg0, arg1)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SetGroups indicates an expected call of SetGroups
+func (mr *MockWorkersMockRecorder) SetGroups(arg0, arg1 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetGroups", reflect.TypeOf((*MockWorkers)(nil).SetGroups), arg0, arg1)
+}
+
+// SetState mocks base method
+func (m *MockWorkers) SetState(arg0 boruta.WorkerUUID, arg1 boruta.WorkerState) error {
+ ret := m.ctrl.Call(m, "SetState", arg0, arg1)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SetState indicates an expected call of SetState
+func (mr *MockWorkersMockRecorder) SetState(arg0, arg1 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetState", reflect.TypeOf((*MockWorkers)(nil).SetState), arg0, arg1)
+}
--- /dev/null
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+// Package v1 provides HTTP API version 1 of Boruta. Through this API clients may:
+// * list, create, manage and get details of requests;
+// * list, acquire, prolong access to and get details of workers.
+package v1
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "github.com/dimfeld/httptreemux"
+)
+
+// responseData type denotes data returned by HTTP request handler functions.
+// Returned values are directly converted to JSON responses.
+type responseData interface{}
+
+// reqHandler denotes function that parses HTTP request and returns responseData.
+type reqHandler func(*http.Request, map[string]string) responseData
+
+// API provides HTTP API handlers.
+type API struct {
+ r *httptreemux.TreeMux
+}
+
+// jsonMustMarshal tries to marshal responseData to JSON. Panics if error occurs.
+// TODO(mwereski): check type of data.
+func jsonMustMarshal(data responseData) []byte {
+ res, err := json.Marshal(data)
+ if err != nil {
+ msg := "unable to marshal JSON:" + err.Error()
+ panic(newServerError(ErrInternalServerError, msg))
+ }
+ return res
+}
+
+// panicHandler is intended as a httptreemux PanicHandler function. It sends
+// InternalServerError with details to client whose request caused panic.
+func panicHandler(w http.ResponseWriter, r *http.Request, err interface{}) {
+ var reason interface{}
+ var status = http.StatusInternalServerError
+ switch srvErr := err.(type) {
+ case *serverError:
+ reason = srvErr.Err
+ status = srvErr.Status
+ default:
+ reason = srvErr
+ }
+ // Because marshalling JSON may fail, data is sent in plaintext.
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ w.WriteHeader(status)
+ w.Write([]byte(fmt.Sprintf("Internal Server Error:\n%s", reason)))
+}
+
+// routerSetHandler wraps fn by adding HTTP headers, handling error and
+// marshalling. Such wrapped function is then registered in the API router as a
+// handler for given path, provided methods and HTTP success status that should
+// be used when funcion succeeds.
+func routerSetHandler(grp *httptreemux.Group, path string, fn reqHandler,
+ status int, methods ...string) {
+ newHandler := func(handle reqHandler) httptreemux.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request,
+ ps map[string]string) {
+ status := status
+ rdata := handle(r, ps)
+ if data, isErr := rdata.(*serverError); isErr &&
+ data != nil {
+ status = data.Status
+ }
+ if status != http.StatusNoContent {
+ w.Header().Set("Content-Type", "application/json")
+ }
+ w.WriteHeader(status)
+ if status != http.StatusNoContent {
+ w.Write(jsonMustMarshal(rdata))
+ }
+ }
+ }
+ for _, method := range methods {
+ grp.Handle(method, path, newHandler(fn))
+ }
+}
+
+// NewAPI takes router and registers HTTP API in it. httptreemux.PanicHandler
+// function is set. Also other setting of the router may be modified.
+func NewAPI(router *httptreemux.TreeMux) (api *API) {
+ api = new(API)
+
+ api.r = router
+ api.r.PanicHandler = panicHandler
+
+ root := api.r.NewGroup("/api/v1")
+ reqs := root.NewGroup("/reqs")
+ workers := root.NewGroup("/workers")
+
+ // Requests API
+ routerSetHandler(reqs, "/", api.listRequestsHandler, http.StatusOK,
+ http.MethodGet, http.MethodHead)
+ routerSetHandler(reqs, "/list", api.listRequestsHandler, http.StatusOK,
+ http.MethodPost)
+ routerSetHandler(reqs, "/", api.newRequestHandler, http.StatusCreated,
+ http.MethodPost)
+ routerSetHandler(reqs, "/:id", api.getRequestInfoHandler, http.StatusOK,
+ http.MethodGet, http.MethodHead)
+ routerSetHandler(reqs, "/:id", api.updateRequestHandler,
+ http.StatusNoContent, http.MethodPost)
+ routerSetHandler(reqs, "/:id/close", api.closeRequestHandler,
+ http.StatusNoContent, http.MethodPost)
+ routerSetHandler(reqs, "/:id/acquire_worker", api.acquireWorkerHandler,
+ http.StatusOK, http.MethodPost)
+ routerSetHandler(reqs, "/:id/prolong", api.prolongAccessHandler,
+ http.StatusNoContent, http.MethodPost)
+
+ // Workers API
+ routerSetHandler(workers, "/", api.listWorkersHandler, http.StatusOK,
+ http.MethodGet, http.MethodHead)
+ routerSetHandler(workers, "/list", api.listWorkersHandler, http.StatusOK,
+ http.MethodPost)
+ routerSetHandler(workers, "/:id", api.getWorkerInfoHandler, http.StatusOK,
+ http.MethodGet, http.MethodHead)
+
+ return
+}
--- /dev/null
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package v1
+
+import (
+ "errors"
+ "flag"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "git.tizen.org/tools/boruta/mocks"
+ "github.com/dimfeld/httptreemux"
+ "github.com/golang/mock/gomock"
+ "github.com/stretchr/testify/assert"
+)
+
+const contentTypeJSON = "application/json"
+
+var update bool
+
+type requestTest struct {
+ name string
+ path string
+ methods []string
+ json string
+ contentType string
+ status int
+}
+
+type allMocks struct {
+ ctrl *gomock.Controller
+ rq *mocks.MockRequests
+ wm *mocks.MockWorkers
+}
+
+func TestMain(m *testing.M) {
+ flag.BoolVar(&update, "update", false, "update testdata")
+ flag.Parse()
+ os.Exit(m.Run())
+}
+
+func initTest(t *testing.T) (*assert.Assertions, *allMocks, *API) {
+ ctrl := gomock.NewController(t)
+ m := &allMocks{
+ ctrl: ctrl,
+ rq: mocks.NewMockRequests(ctrl),
+ wm: mocks.NewMockWorkers(ctrl),
+ }
+ return assert.New(t), m, NewAPI(httptreemux.New())
+}
+
+func (m *allMocks) finish() {
+ m.ctrl.Finish()
+}
+
+func runTests(assert *assert.Assertions, api *API, tests []requestTest) {
+ srv := httptest.NewServer(api.r)
+ defer srv.Close()
+ var req *http.Request
+ var err error
+ var tcaseErrStr string
+
+ for _, test := range tests {
+ tcaseErrStr = test.name + ": FAILED"
+ for _, method := range test.methods {
+ // prepare and do HTTP request
+ if test.json == "" {
+ req, err = http.NewRequest(method,
+ srv.URL+test.path, nil)
+ } else {
+ req, err = http.NewRequest(method,
+ srv.URL+test.path,
+ strings.NewReader(test.json))
+ }
+ assert.Nil(err)
+ req.Header["Content-Type"] = []string{test.contentType}
+ resp, err := srv.Client().Do(req)
+ assert.Nil(err)
+ defer resp.Body.Close()
+
+ // read expected results from file or generate the file
+ tdata := filepath.Join("testdata", test.name+"-"+method+".json")
+ body, err := ioutil.ReadAll(resp.Body)
+ assert.Nil(err)
+ if update && method != http.MethodHead {
+ err = ioutil.WriteFile(tdata, body, 0644)
+ assert.Nil(err)
+ }
+
+ // check status code
+ assert.Equal(test.status, resp.StatusCode, tcaseErrStr)
+ if resp.StatusCode == http.StatusNoContent {
+ continue
+ }
+ // check content type
+ assert.Equal(test.contentType,
+ resp.Header.Get("Content-Type"))
+ if method == http.MethodHead {
+ assert.Zero(len(body), tcaseErrStr)
+ continue
+ }
+ // if update was set then file was just generated,
+ // so there's no sense in rereading and comparing it.
+ if update {
+ continue
+ }
+ // check result JSON
+ expected, err := ioutil.ReadFile(tdata)
+ assert.Nil(err, tcaseErrStr)
+ assert.JSONEq(string(expected), string(body), tcaseErrStr)
+ }
+ }
+}
+
+func TestNewAPI(t *testing.T) {
+ assert, m, api := initTest(t)
+ assert.NotNil(api)
+ m.finish()
+}
+
+func TestNewServerError(t *testing.T) {
+ assert := assert.New(t)
+ badRequest := &serverError{
+ Err: "invalid request: foo",
+ Status: http.StatusBadRequest,
+ }
+ nobody := "no body provided in HTTP request"
+ missingBody := &serverError{
+ Err: nobody,
+ Status: http.StatusBadRequest,
+ }
+ notImplemented := &serverError{
+ Err: ErrNotImplemented.Error(),
+ Status: http.StatusNotImplemented,
+ }
+ internalErr := &serverError{
+ Err: ErrInternalServerError.Error(),
+ Status: http.StatusInternalServerError,
+ }
+ customErr := &serverError{
+ Err: "invalid request: more details",
+ Status: http.StatusBadRequest,
+ }
+ 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.Nil(newServerError(nil))
+}
+
+func TestJsonMustMarshal(t *testing.T) {
+ assert := assert.New(t)
+ assert.Panics(func() { jsonMustMarshal(make(chan bool)) })
+}
+
+func TestPanicHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+ msg := "Test PanicHandler: server panic"
+ otherErr := "some error"
+ tests := [...]struct {
+ name string
+ path string
+ err interface{}
+ }{
+ {
+ name: "panic-server-error",
+ path: "/priv/api/panic/srvErr/",
+ err: &serverError{
+ Err: msg,
+ Status: http.StatusInternalServerError,
+ },
+ },
+ {
+ name: "panic-other-error",
+ path: "/priv/api/panic/otherErr/",
+ err: otherErr,
+ },
+ }
+ contentType := "text/plain; charset=utf-8"
+
+ newHandler := func(err interface{}) reqHandler {
+ return func(r *http.Request, ps map[string]string) responseData {
+ panic(err)
+ }
+ }
+ for _, test := range tests {
+ routerSetHandler(api.r.NewGroup("/"), test.path, newHandler(test.err),
+ http.StatusOK, http.MethodGet)
+ }
+ var tcaseErrStr string
+ srv := httptest.NewServer(api.r)
+ assert.NotNil(srv)
+ defer srv.Close()
+ for _, test := range tests {
+ tcaseErrStr = test.name + ": FAILED"
+ resp, err := http.Get(srv.URL + test.path)
+ assert.Nil(err)
+ tdata := filepath.Join("testdata", test.name+".txt")
+ body, err := ioutil.ReadAll(resp.Body)
+ assert.Nil(err)
+ if update {
+ ioutil.WriteFile(tdata, body, 0644)
+ }
+ expected, err := ioutil.ReadFile(tdata)
+ assert.Nil(err)
+ assert.Equal(http.StatusInternalServerError, resp.StatusCode, tcaseErrStr)
+ assert.Equal(contentType, resp.Header.Get("Content-Type"), tcaseErrStr)
+ assert.Equal(expected, body, tcaseErrStr)
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+// File server/api/v1/errors.go provides errors that may occur when interacting
+// with Boruta HTTP API.
+
+package v1
+
+import (
+ "errors"
+ "io"
+ "net/http"
+)
+
+// serverError represents error that occured while creating response.
+type serverError struct {
+ // Err contains general error string.
+ Err string `json:"error"`
+ // Status contains HTTP error code that should be returned with the error.
+ Status int `json:"-"`
+}
+
+var (
+ // ErrNotImplemented is returned when requested functionality isn't
+ // implemented yet.
+ ErrNotImplemented = errors.New("not implemented yet")
+ // ErrInternalServerError is returned when serious error in the server
+ // occurs which isn't users' fault.
+ ErrInternalServerError = errors.New("internal server error")
+ // ErrBadRequest is returned when User request is invalid.
+ ErrBadRequest = errors.New("invalid request")
+)
+
+// newServerError provides pointer to initialized serverError.
+func newServerError(err error, details ...string) (ret *serverError) {
+ if err == nil {
+ return nil
+ }
+
+ ret = new(serverError)
+
+ ret.Err = err.Error()
+ if len(details) > 0 {
+ ret.Err += ": " + details[0]
+ }
+
+ switch err {
+ case ErrNotImplemented:
+ ret.Status = http.StatusNotImplemented
+ case ErrInternalServerError:
+ ret.Status = http.StatusInternalServerError
+ case ErrBadRequest:
+ ret.Status = http.StatusBadRequest
+ case io.EOF:
+ ret.Err = "no body provided in HTTP request"
+ ret.Status = http.StatusBadRequest
+ default:
+ ret.Err = ErrBadRequest.Error() + ": " + ret.Err
+ ret.Status = http.StatusBadRequest
+ }
+
+ return
+}
--- /dev/null
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+// File server/api/v1/handlers.go contains all handlers that are used in v1 API.
+
+package v1
+
+import (
+ "net/http"
+)
+
+// newRequestHandler parses HTTP request for creating new Boruta request and
+// calls NewRequest().
+func (api *API) newRequestHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "new request")
+}
+
+// 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")
+}
+
+// updateRequestHandler parses HTTP request for modification of existing Boruta
+// request and calls appropriate methods: SetRequestValidAfter(),
+// SetRequestDeadline() and SetRequestPriority().
+func (api *API) updateRequestHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "update request")
+}
+
+// getRequestInfoHandler parses HTTP request for getting information about Boruta
+// request and calls GetRequestInfo().
+func (api *API) getRequestInfoHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "get request info")
+}
+
+// listRequestsHandler parses HTTP request for listing Boruta requests and calls
+// ListRequests().
+func (api *API) listRequestsHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "list requests")
+}
+
+// acquireWorkerHandler parses HTTP request for acquiring worker for Boruta
+// request and calls AcquireWorker().
+func (api *API) acquireWorkerHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "acquire worker")
+}
+
+// prolongAccessHandler parses HTTP request for prolonging previously acquired
+// worker and calls ProlongAccess().
+func (api *API) prolongAccessHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "prolong access")
+}
+
+// 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")
+}
+
+// getWorkerInfoHandler parses HTTP request for obtaining worker information and
+// calls GetWorkerInfo().
+func (api *API) getWorkerInfoHandler(r *http.Request, ps map[string]string) responseData {
+ return newServerError(ErrNotImplemented, "get worker info")
+}
--- /dev/null
+/* * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package v1
+
+import (
+ "net/http"
+ "testing"
+)
+
+func TestNewRequestHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "new-req",
+ path: "/api/v1/reqs/",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestCloseRequestHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "close-req",
+ path: "/api/v1/reqs/8/close",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestUpdateRequestHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "update-req",
+ path: "/api/v1/reqs/8",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestGetRequestInfoHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "req-info",
+ path: "/api/v1/reqs/8",
+ methods: []string{http.MethodGet, http.MethodHead},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestListRequestsHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "list-reqs-all",
+ path: "/api/v1/reqs/",
+ methods: []string{http.MethodGet, http.MethodHead},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ {
+ name: "list-reqs-filter",
+ path: "/api/v1/reqs/list",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestAcquireWorkerHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "acquire-worker",
+ path: "/api/v1/reqs/8/acquire_worker",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestProlongAccessHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "prolong-access",
+ path: "/api/v1/reqs/8/prolong",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestListWorkersHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "list-workers-all",
+ path: "/api/v1/workers/",
+ methods: []string{http.MethodGet, http.MethodHead},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ {
+ name: "list-workers-filter",
+ path: "/api/v1/workers/list",
+ methods: []string{http.MethodPost},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
+
+func TestGetWorkerInfoHandler(t *testing.T) {
+ assert, m, api := initTest(t)
+ defer m.finish()
+
+ tests := []requestTest{
+ {
+ name: "worker-info",
+ path: "/api/v1/workers/8",
+ methods: []string{http.MethodGet, http.MethodHead},
+ json: ``,
+ contentType: contentTypeJSON,
+ status: http.StatusNotImplemented,
+ },
+ }
+
+ runTests(assert, api, tests)
+}
--- /dev/null
+{"error":"not implemented yet: acquire worker"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: close request"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: list requests"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: list requests"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: list workers"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: list workers"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: new request"}
\ No newline at end of file
--- /dev/null
+Internal Server Error:
+some error
\ No newline at end of file
--- /dev/null
+Internal Server Error:
+Test PanicHandler: server panic
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: prolong access"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: get request info"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: update request"}
\ No newline at end of file
--- /dev/null
+{"error":"not implemented yet: get worker info"}
\ No newline at end of file