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 {
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
-}