From d762991e4a41d3e4b9dbab0a48548d9ba8f6fbc6 Mon Sep 17 00:00:00 2001 From: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Date: Thu, 29 Aug 2019 00:51:02 +0800 Subject: [PATCH] refactor toolbar/server (#390) * migrate * rename --- toolbar/federation/api/handler.go | 5 +- toolbar/federation/api/server.go | 201 +---------------------- toolbar/{federation/api => server}/common.go | 2 +- toolbar/{federation/api => server}/display.go | 2 +- toolbar/{federation/api => server}/errors.go | 2 +- toolbar/server/handle.go | 196 ++++++++++++++++++++++ toolbar/{federation/api => server}/pagination.go | 2 +- toolbar/{federation/api => server}/response.go | 2 +- 8 files changed, 210 insertions(+), 202 deletions(-) rename toolbar/{federation/api => server}/common.go (95%) rename toolbar/{federation/api => server}/display.go (98%) rename toolbar/{federation/api => server}/errors.go (96%) create mode 100644 toolbar/server/handle.go rename toolbar/{federation/api => server}/pagination.go (99%) rename toolbar/{federation/api => server}/response.go (99%) diff --git a/toolbar/federation/api/handler.go b/toolbar/federation/api/handler.go index d8f1445b..8a23b9f1 100644 --- a/toolbar/federation/api/handler.go +++ b/toolbar/federation/api/handler.go @@ -10,11 +10,12 @@ import ( "github.com/vapor/errors" "github.com/vapor/toolbar/federation/common" "github.com/vapor/toolbar/federation/database/orm" + serverCommon "github.com/vapor/toolbar/server" ) -type listCrosschainTxsReq struct{ Display } +type listCrosschainTxsReq struct{ serverCommon.Display } -func (s *Server) ListCrosschainTxs(c *gin.Context, listTxsReq *listCrosschainTxsReq, query *PaginationQuery) ([]*orm.CrossTransaction, error) { +func (s *Server) ListCrosschainTxs(c *gin.Context, listTxsReq *listCrosschainTxsReq, query *serverCommon.PaginationQuery) ([]*orm.CrossTransaction, error) { var ormTxs []*orm.CrossTransaction txFilter := &orm.CrossTransaction{} diff --git a/toolbar/federation/api/server.go b/toolbar/federation/api/server.go index 2aff6625..3df39255 100644 --- a/toolbar/federation/api/server.go +++ b/toolbar/federation/api/server.go @@ -1,16 +1,11 @@ package api import ( - "encoding/json" - "net/http" - "reflect" - "strings" - "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" - "github.com/vapor/errors" "github.com/vapor/toolbar/federation/config" + serverCommon "github.com/vapor/toolbar/server" ) type Server struct { @@ -31,201 +26,17 @@ func NewServer(db *gorm.DB, cfg *config.Config) *Server { return server } -func (server *Server) setupRouter() { +func (s *Server) setupRouter() { r := gin.Default() - r.Use(server.middleware()) + r.Use(serverCommon.Middleware(s)) v1 := r.Group("/api/v1") - v1.POST("/federation/list-crosschain-txs", handlerMiddleware(server.ListCrosschainTxs)) - v1.GET("/federation/list-chains", handlerMiddleware(server.ListChains)) + v1.POST("/federation/list-crosschain-txs", serverCommon.HandlerMiddleware(s.ListCrosschainTxs)) + v1.GET("/federation/list-chains", serverCommon.HandlerMiddleware(s.ListChains)) - server.engine = r + s.engine = r } func (s *Server) Run() { s.engine.Run(":9886") } - -func (s *Server) middleware() gin.HandlerFunc { - return func(c *gin.Context) { - // add Access-Control-Allow-Origin - c.Header("Access-Control-Allow-Origin", "*") - c.Header("Access-Control-Allow-Headers", "Content-Type") - c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") - if c.Request.Method == "OPTIONS" { - c.AbortWithStatus(http.StatusOK) - return - } - - c.Set(serverLabel, s) - c.Next() - } -} - -type handlerFun interface{} - -func handlerMiddleware(handleFunc interface{}) func(*gin.Context) { - if err := validateFuncType(handleFunc); err != nil { - panic(err) - } - - return func(context *gin.Context) { - handleRequest(context, handleFunc) - } -} - -func validateFuncType(fun handlerFun) error { - ft := reflect.TypeOf(fun) - if ft.Kind() != reflect.Func || ft.IsVariadic() { - return errors.New("need nonvariadic func in " + ft.String()) - } - - if ft.NumIn() < 1 || ft.NumIn() > 3 { - return errors.New("need one or two or three parameters in " + ft.String()) - } - - if ft.In(0) != contextType { - return errors.New("the first parameter must point of context in " + ft.String()) - } - - if ft.NumIn() == 2 && ft.In(1).Kind() != reflect.Ptr { - return errors.New("the second parameter must point in " + ft.String()) - } - - if ft.NumIn() == 3 && ft.In(2) != paginationQueryType { - return errors.New("the third parameter of pagination must point of paginationQuery in " + ft.String()) - } - - if ft.NumOut() < 1 || ft.NumOut() > 2 { - return errors.New("the size of return value must one or two in " + ft.String()) - } - - // if has pagination, the first return value must slice or array - if ft.NumIn() == 3 && ft.Out(0).Kind() != reflect.Slice && ft.Out(0).Kind() != reflect.Array { - return errors.New("the first return value of pagination must slice of array in " + ft.String()) - } - - if !ft.Out(ft.NumOut() - 1).Implements(errorType) { - return errors.New("the last return value must error in " + ft.String()) - } - return nil -} - -// handleRequest get a handler function to process the request by request url -func handleRequest(context *gin.Context, fun handlerFun) { - args, err := buildHandleFuncArgs(fun, context) - if err != nil { - respondErrorResp(context, err) - return - } - - result := callHandleFunc(fun, args...) - if err := result[len(result)-1]; err != nil { - respondErrorResp(context, err.(error)) - return - } - - if exist := processPaginationIfPresent(fun, args, result, context); exist { - return - } - - if len(result) == 1 { - respondSuccessResp(context, nil) - return - } - - respondSuccessResp(context, result[0]) -} - -func buildHandleFuncArgs(fun handlerFun, context *gin.Context) ([]interface{}, error) { - args := []interface{}{context} - - req, err := createHandleReqArg(fun, context) - if err != nil { - return nil, errors.Wrap(err, "createHandleReqArg") - } - - if err := checkDisplayOrder(req); err != nil { - return nil, err - } - - if req != nil { - args = append(args, req) - } - - ft := reflect.TypeOf(fun) - - // no pagination exists - if ft.NumIn() != 3 { - return args, nil - } - - query, err := parsePagination(context) - if err != nil { - return nil, errors.Wrap(err, "ParsePagination") - } - - args = append(args, query) - return args, nil -} - -func createHandleReqArg(fun handlerFun, context *gin.Context) (interface{}, error) { - ft := reflect.TypeOf(fun) - if ft.NumIn() == 1 { - return nil, nil - } - argType := ft.In(1).Elem() - - reqArg := reflect.New(argType).Interface() - if err := context.ShouldBindJSON(reqArg); err != nil { - return nil, errors.Wrap(err, "bind reqArg") - } - - b, err := json.Marshal(reqArg) - if err != nil { - return nil, errors.Wrap(err, "json marshal") - } - - context.Set(reqBodyLabel, string(b)) - - return reqArg, nil -} - -func checkDisplayOrder(req interface{}) error { - if req == nil { - return nil - } - - reqType := reflect.TypeOf(req).Elem() - reqVal := reflect.ValueOf(req).Elem() - - for i := 0; i < reqType.NumField(); i++ { - field := reqType.Field(i) - if field.Type != reflect.TypeOf(Display{}) { - continue - } - display := reqVal.Field(i).Interface().(Display) - - order := strings.Trim(display.Sorter.Order, "") - if order != "desc" && order != "asc" { - reqVal.Field(i).Set(reflect.ValueOf(Display{Filter: display.Filter, Sorter: Sorter{By: display.Sorter.By, Order: "desc"}})) - } - } - return nil -} - -func callHandleFunc(fun handlerFun, args ...interface{}) []interface{} { - fv := reflect.ValueOf(fun) - - params := make([]reflect.Value, len(args)) - for i, arg := range args { - params[i] = reflect.ValueOf(arg) - } - - rs := fv.Call(params) - result := make([]interface{}, len(rs)) - for i, r := range rs { - result[i] = r.Interface() - } - return result -} diff --git a/toolbar/federation/api/common.go b/toolbar/server/common.go similarity index 95% rename from toolbar/federation/api/common.go rename to toolbar/server/common.go index 161667a5..f3122d3c 100644 --- a/toolbar/federation/api/common.go +++ b/toolbar/server/common.go @@ -1,4 +1,4 @@ -package api +package server import ( "reflect" diff --git a/toolbar/federation/api/display.go b/toolbar/server/display.go similarity index 98% rename from toolbar/federation/api/display.go rename to toolbar/server/display.go index fed1506b..caa4e564 100644 --- a/toolbar/federation/api/display.go +++ b/toolbar/server/display.go @@ -1,4 +1,4 @@ -package api +package server import ( "github.com/vapor/errors" diff --git a/toolbar/federation/api/errors.go b/toolbar/server/errors.go similarity index 96% rename from toolbar/federation/api/errors.go rename to toolbar/server/errors.go index f2163259..8ecfdfb3 100644 --- a/toolbar/federation/api/errors.go +++ b/toolbar/server/errors.go @@ -1,4 +1,4 @@ -package api +package server import ( "github.com/vapor/errors" diff --git a/toolbar/server/handle.go b/toolbar/server/handle.go new file mode 100644 index 00000000..b891556f --- /dev/null +++ b/toolbar/server/handle.go @@ -0,0 +1,196 @@ +package server + +import ( + "encoding/json" + "net/http" + "reflect" + "strings" + + "github.com/gin-gonic/gin" + + "github.com/vapor/errors" +) + +func Middleware(serverInstance interface{}) gin.HandlerFunc { + return func(c *gin.Context) { + // add Access-Control-Allow-Origin + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Headers", "Content-Type") + c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(http.StatusOK) + return + } + + c.Set(serverLabel, serverInstance) + c.Next() + } +} + +type handlerFun interface{} + +func HandlerMiddleware(handleFunc interface{}) func(*gin.Context) { + if err := validateFuncType(handleFunc); err != nil { + panic(err) + } + + return func(context *gin.Context) { + handleRequest(context, handleFunc) + } +} + +func validateFuncType(fun handlerFun) error { + ft := reflect.TypeOf(fun) + if ft.Kind() != reflect.Func || ft.IsVariadic() { + return errors.New("need nonvariadic func in " + ft.String()) + } + + if ft.NumIn() < 1 || ft.NumIn() > 3 { + return errors.New("need one or two or three parameters in " + ft.String()) + } + + if ft.In(0) != contextType { + return errors.New("the first parameter must point of context in " + ft.String()) + } + + if ft.NumIn() == 2 && ft.In(1).Kind() != reflect.Ptr { + return errors.New("the second parameter must point in " + ft.String()) + } + + if ft.NumIn() == 3 && ft.In(2) != paginationQueryType { + return errors.New("the third parameter of pagination must point of paginationQuery in " + ft.String()) + } + + if ft.NumOut() < 1 || ft.NumOut() > 2 { + return errors.New("the size of return value must one or two in " + ft.String()) + } + + // if has pagination, the first return value must slice or array + if ft.NumIn() == 3 && ft.Out(0).Kind() != reflect.Slice && ft.Out(0).Kind() != reflect.Array { + return errors.New("the first return value of pagination must slice of array in " + ft.String()) + } + + if !ft.Out(ft.NumOut() - 1).Implements(errorType) { + return errors.New("the last return value must error in " + ft.String()) + } + return nil +} + +// handleRequest get a handler function to process the request by request url +func handleRequest(context *gin.Context, fun handlerFun) { + args, err := buildHandleFuncArgs(fun, context) + if err != nil { + respondErrorResp(context, err) + return + } + + result := callHandleFunc(fun, args...) + if err := result[len(result)-1]; err != nil { + respondErrorResp(context, err.(error)) + return + } + + if exist := processPaginationIfPresent(fun, args, result, context); exist { + return + } + + if len(result) == 1 { + respondSuccessResp(context, nil) + return + } + + respondSuccessResp(context, result[0]) +} + +func buildHandleFuncArgs(fun handlerFun, context *gin.Context) ([]interface{}, error) { + args := []interface{}{context} + + req, err := createHandleReqArg(fun, context) + if err != nil { + return nil, errors.Wrap(err, "createHandleReqArg") + } + + if err := checkDisplayOrder(req); err != nil { + return nil, err + } + + if req != nil { + args = append(args, req) + } + + ft := reflect.TypeOf(fun) + + // no pagination exists + if ft.NumIn() != 3 { + return args, nil + } + + query, err := parsePagination(context) + if err != nil { + return nil, errors.Wrap(err, "ParsePagination") + } + + args = append(args, query) + return args, nil +} + +func createHandleReqArg(fun handlerFun, context *gin.Context) (interface{}, error) { + ft := reflect.TypeOf(fun) + if ft.NumIn() == 1 { + return nil, nil + } + argType := ft.In(1).Elem() + + reqArg := reflect.New(argType).Interface() + if err := context.ShouldBindJSON(reqArg); err != nil { + return nil, errors.Wrap(err, "bind reqArg") + } + + b, err := json.Marshal(reqArg) + if err != nil { + return nil, errors.Wrap(err, "json marshal") + } + + context.Set(reqBodyLabel, string(b)) + + return reqArg, nil +} + +func checkDisplayOrder(req interface{}) error { + if req == nil { + return nil + } + + reqType := reflect.TypeOf(req).Elem() + reqVal := reflect.ValueOf(req).Elem() + + for i := 0; i < reqType.NumField(); i++ { + field := reqType.Field(i) + if field.Type != reflect.TypeOf(Display{}) { + continue + } + display := reqVal.Field(i).Interface().(Display) + + order := strings.Trim(display.Sorter.Order, "") + if order != "desc" && order != "asc" { + reqVal.Field(i).Set(reflect.ValueOf(Display{Filter: display.Filter, Sorter: Sorter{By: display.Sorter.By, Order: "desc"}})) + } + } + return nil +} + +func callHandleFunc(fun handlerFun, args ...interface{}) []interface{} { + fv := reflect.ValueOf(fun) + + params := make([]reflect.Value, len(args)) + for i, arg := range args { + params[i] = reflect.ValueOf(arg) + } + + rs := fv.Call(params) + result := make([]interface{}, len(rs)) + for i, r := range rs { + result[i] = r.Interface() + } + return result +} diff --git a/toolbar/federation/api/pagination.go b/toolbar/server/pagination.go similarity index 99% rename from toolbar/federation/api/pagination.go rename to toolbar/server/pagination.go index f93c1bdd..74bf2976 100644 --- a/toolbar/federation/api/pagination.go +++ b/toolbar/server/pagination.go @@ -1,4 +1,4 @@ -package api +package server import ( "fmt" diff --git a/toolbar/federation/api/response.go b/toolbar/server/response.go similarity index 99% rename from toolbar/federation/api/response.go rename to toolbar/server/response.go index ebb98749..d7ec9d6a 100644 --- a/toolbar/federation/api/response.go +++ b/toolbar/server/response.go @@ -1,4 +1,4 @@ -package api +package server import ( "fmt" -- 2.11.0