Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Commit

Permalink
refactor: use package server/api to register supernode's API
Browse files Browse the repository at this point in the history
Signed-off-by: lowzj <[email protected]>
  • Loading branch information
lowzj committed May 28, 2020
1 parent e8a6514 commit 3e559bd
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 221 deletions.
23 changes: 15 additions & 8 deletions supernode/server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ func newCategory(name, prefix string) *category {
apiCategories[name] = &category{
name: name,
prefix: prefix,
handlerSpecs: []*HandlerSpec{
listHandler(name),
},
}
return apiCategories[name]
}
Expand All @@ -59,11 +62,12 @@ type category struct {
}

// Register registers an API into this API category.
func (c *category) Register(h *HandlerSpec) *category {
if !validate(h) {
return c
func (c *category) Register(handlers ...*HandlerSpec) *category {
for _, h := range handlers {
if valid(h) {
c.handlerSpecs = append(c.handlerSpecs, h)
}
}
c.handlerSpecs = append(c.handlerSpecs, h)
return c
}

Expand All @@ -82,8 +86,11 @@ func (c *category) Handlers() []*HandlerSpec {
return c.handlerSpecs
}

// -----------------------------------------------------------------------------

func validate(h *HandlerSpec) bool {
return h != nil && h.HandlerFunc != nil && h.Method != ""
// Range traverses all the handlers in this category.
func (c *category) Range(f func(prefix string, h *HandlerSpec)) {
for _, h := range c.handlerSpecs {
if h != nil {
f(c.prefix, h)
}
}
}
53 changes: 44 additions & 9 deletions supernode/server/api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,28 @@ func ParseJSONRequest(req io.Reader, target interface{}, validator ValidateFunc)
return nil
}

// EncodeResponse encodes response in json.
// The response body is empty if the data is nil or empty value.
func EncodeResponse(w http.ResponseWriter, code int, data interface{}) error {
// SendResponse encodes response in json.
//
// TODO:
// Should the response body should be empty if the data is nil or empty
// string? Now it's incompatible with the client.
func SendResponse(w http.ResponseWriter, code int, data interface{}) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
if util.IsNil(data) || data == "" {
return nil
}
return json.NewEncoder(w).Encode(data)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
return enc.Encode(data)
}

// HandleErrorResponse handles err from server side and constructs response
// for client side.
func HandleErrorResponse(w http.ResponseWriter, err error) {
switch e := err.(type) {
case *errortypes.HTTPError:
_ = EncodeResponse(w, e.Code, errResp(e.Code, e.Msg))
_ = SendResponse(w, e.Code, errResp(e.Code, e.Msg))
default:
// By default, server side returns code 500 if error happens.
_ = EncodeResponse(w, http.StatusInternalServerError,
_ = SendResponse(w, http.StatusInternalServerError,
errResp(http.StatusInternalServerError, e.Error()))
}
}
Expand Down Expand Up @@ -101,3 +103,36 @@ func errResp(code int, msg string) *types.ErrorResponse {
Message: msg,
}
}

func valid(h *HandlerSpec) bool {
return h != nil && h.HandlerFunc != nil && h.Method != ""
}

func listHandler(name string) *HandlerSpec {
h := &HandlerSpec{
Method: http.MethodGet,
Path: "/",
HandlerFunc: func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
c := apiCategories[name]
if c == nil {
return errortypes.NewHTTPError(http.StatusBadRequest, "no such category")
}

result := map[string]interface{}{
"category": c.name,
"prefix": c.prefix,
}
handlers := make([]map[string]string, len(c.handlerSpecs))
for i, v := range c.handlerSpecs {
handlers[i] = map[string]string{
"method": v.Method,
"path": v.Path,
}
}
result["api"] = handlers

return SendResponse(rw, http.StatusOK, result)
},
}
return h
}
13 changes: 6 additions & 7 deletions supernode/server/api/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (
"github.com/stretchr/testify/suite"

"github.com/dragonflyoss/Dragonfly/pkg/errortypes"
"github.com/dragonflyoss/Dragonfly/pkg/util"
)

func TestUtil(t *testing.T) {
Expand Down Expand Up @@ -89,7 +88,7 @@ func (s *TestUtilSuite) TestEncodeResponse() {
err string
}{
{200, "", ""},
{200, nil, ""},
{200, (*testStruct)(nil), ""},
{200, 0, ""},
{200, newT(1), ""},
{400, newT(1), ""},
Expand All @@ -98,12 +97,12 @@ func (s *TestUtilSuite) TestEncodeResponse() {
for i, c := range cases {
msg := fmt.Sprintf("case %d: %v", i, c)
w := httptest.NewRecorder()
e := EncodeResponse(w, c.code, c.data)
e := SendResponse(w, c.code, c.data)
if c.err == "" {
s.Nil(e, msg)
s.Equal(c.code, w.Code, msg)
if util.IsNil(c.data) {
s.Equal("", strings.TrimSpace(w.Body.String()), msg)
if c.data == "" {
s.Equal("\"\"", strings.TrimSpace(w.Body.String()), msg)
} else {
s.Equal(fmt.Sprintf("%v", c.data), strings.TrimSpace(w.Body.String()), msg)
}
Expand Down Expand Up @@ -146,7 +145,7 @@ func (s *TestUtilSuite) TestWrapHandler() {
case "POST":
return errortypes.NewHTTPError(400, "test")
}
_ = EncodeResponse(rw, 200, "test")
_ = SendResponse(rw, 200, "test")
return nil
}
cases := []struct {
Expand Down Expand Up @@ -201,7 +200,7 @@ func (t *testStruct) validate(registry strfmt.Registry) error {

func (t *testStruct) String() string {
if t == nil {
return ""
return "null"
}
return fmt.Sprintf("{\"A\":%d}", t.A)
}
41 changes: 0 additions & 41 deletions supernode/server/handler.go

This file was deleted.

85 changes: 13 additions & 72 deletions supernode/server/preheat_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,27 @@ package server

import (
"context"
"encoding/json"
"io"
"net/http"

"github.com/dragonflyoss/Dragonfly/apis/types"
"github.com/dragonflyoss/Dragonfly/pkg/errortypes"
"github.com/dragonflyoss/Dragonfly/supernode/server/api"

"github.com/go-openapi/strfmt"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
)

// ---------------------------------------------------------------------------
// handlers of preheat http apis

func (s *Server) createPreheatTask(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
request := &types.PreheatCreateRequest{}
if err := parseRequest(req.Body, request, request.Validate); err != nil {
if err := api.ParseJSONRequest(req.Body, request, request.Validate); err != nil {
return err
}
preheatID, err := s.PreheatMgr.Create(ctx, request)
if err != nil {
return err
return httpErr(err)
}
resp := types.PreheatCreateResponse{ID: preheatID}
return EncodeResponse(rw, http.StatusCreated, resp)
Expand All @@ -49,7 +47,7 @@ func (s *Server) createPreheatTask(ctx context.Context, rw http.ResponseWriter,
func (s *Server) getAllPreheatTasks(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
tasks, err := s.PreheatMgr.GetAll(ctx)
if err != nil {
return err
return httpErr(err)
}
return EncodeResponse(rw, http.StatusOK, tasks)
}
Expand All @@ -58,7 +56,7 @@ func (s *Server) getPreheatTask(ctx context.Context, rw http.ResponseWriter, req
id := mux.Vars(req)["id"]
task, err := s.PreheatMgr.Get(ctx, id)
if err != nil {
return err
return httpErr(err)
}
resp := types.PreheatInfo{
ID: task.ID,
Expand All @@ -72,84 +70,27 @@ func (s *Server) getPreheatTask(ctx context.Context, rw http.ResponseWriter, req
func (s *Server) deletePreheatTask(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
id := mux.Vars(req)["id"]
if err := s.PreheatMgr.Delete(ctx, id); err != nil {
return err
return httpErr(err)
}
return EncodeResponse(rw, http.StatusOK, true)
}

// ---------------------------------------------------------------------------
// helper functions

type validateFunc func(registry strfmt.Registry) error

func parseRequest(body io.Reader, request interface{}, validator validateFunc) error {
if err := json.NewDecoder(body).Decode(request); err != nil {
if err == io.EOF {
return errortypes.New(http.StatusBadRequest, "empty body")
}
return errortypes.New(http.StatusBadRequest, err.Error())
}
if validator != nil {
if err := validator(strfmt.NewFormats()); err != nil {
return errortypes.New(http.StatusBadRequest, err.Error())
}
func httpErr(err error) error {
if e, ok := err.(*errortypes.DfError); ok {
return errortypes.NewHTTPError(e.Code, e.Msg)
}
return nil
return err
}

// initPreheatHandlers register preheat apis
func initPreheatHandlers(s *Server, r *mux.Router) {
handlers := []*HandlerSpec{
// preheatHandlers returns all the preheats handlers.
func preheatHandlers(s *Server) []*api.HandlerSpec {
return []*api.HandlerSpec{
{Method: http.MethodPost, Path: "/preheats", HandlerFunc: s.createPreheatTask},
{Method: http.MethodGet, Path: "/preheats", HandlerFunc: s.getAllPreheatTasks},
{Method: http.MethodGet, Path: "/preheats/{id}", HandlerFunc: s.getPreheatTask},
{Method: http.MethodDelete, Path: "/preheats/{id}", HandlerFunc: s.deletePreheatTask},
}
// register API
for _, h := range handlers {
if h != nil {
r.Path(versionMatcher + h.Path).Methods(h.Method).
Handler(m.instrumentHandler(h.Path, postPreheatHandler(h.HandlerFunc)))
r.Path("/api/v1" + h.Path).Methods(h.Method).
Handler(m.instrumentHandler(h.Path, postPreheatHandler(h.HandlerFunc)))
r.Path(h.Path).Methods(h.Method).
Handler(m.instrumentHandler(h.Path, postPreheatHandler(h.HandlerFunc)))
}
}
}

func postPreheatHandler(h Handler) http.HandlerFunc {
pctx := context.Background()

return func(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithCancel(pctx)
defer cancel()

// Start to handle request.
err := h(ctx, w, req)
if err != nil {
// Handle error if request handling fails.
handlePreheatErrorResponse(w, err)
}
logrus.Debugf("%s %v err:%v", req.Method, req.URL, err)
}
}

func handlePreheatErrorResponse(w http.ResponseWriter, err error) {
var (
code int
errMsg string
)

// By default, daemon side returns code 500 if error happens.
code = http.StatusInternalServerError
if e, ok := err.(*errortypes.DfError); ok {
code = e.Code
errMsg = e.Msg
}

_ = EncodeResponse(w, code, types.ErrorResponse{
Code: int64(code),
Message: errMsg,
})
}
Loading

0 comments on commit 3e559bd

Please sign in to comment.