OSDN Git Service

feat(warder): add warder backbone (#181)
[bytom/vapor.git] / federation / api / server.go
1 package api
2
3 import (
4         "encoding/json"
5         "fmt"
6         "net/http"
7         "reflect"
8         "strings"
9
10         "github.com/gin-gonic/gin"
11         "github.com/jinzhu/gorm"
12
13         "github.com/vapor/errors"
14         "github.com/vapor/federation/config"
15 )
16
17 type Server struct {
18         cfg    *config.Config
19         db     *gorm.DB
20         engine *gin.Engine
21 }
22
23 func NewServer(db *gorm.DB, cfg *config.Config) *Server {
24         server := &Server{
25                 cfg: cfg,
26                 db:  db,
27         }
28         if cfg.API.IsReleaseMode {
29                 gin.SetMode(gin.ReleaseMode)
30         }
31         server.setupRouter()
32         return server
33 }
34
35 func (server *Server) setupRouter() {
36         r := gin.Default()
37         r.Use(server.middleware())
38
39         v1 := r.Group("/api/v1")
40         v1.POST("/federation/list-crosschain-txs", handlerMiddleware(server.ListCrosschainTxs))
41
42         server.engine = r
43 }
44
45 func (s *Server) Run() {
46         s.engine.Run(fmt.Sprintf(":%d", s.cfg.API.ListeningPort))
47 }
48
49 func (s *Server) middleware() gin.HandlerFunc {
50         return func(c *gin.Context) {
51                 // add Access-Control-Allow-Origin
52                 c.Header("Access-Control-Allow-Origin", "*")
53                 c.Header("Access-Control-Allow-Headers", "Content-Type")
54                 c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
55                 if c.Request.Method == "OPTIONS" {
56                         c.AbortWithStatus(http.StatusOK)
57                         return
58                 }
59
60                 c.Set(serverLabel, s)
61                 c.Next()
62         }
63 }
64
65 type handlerFun interface{}
66
67 func handlerMiddleware(handleFunc interface{}) func(*gin.Context) {
68         if err := validateFuncType(handleFunc); err != nil {
69                 panic(err)
70         }
71
72         return func(context *gin.Context) {
73                 handleRequest(context, handleFunc)
74         }
75 }
76
77 func validateFuncType(fun handlerFun) error {
78         ft := reflect.TypeOf(fun)
79         if ft.Kind() != reflect.Func || ft.IsVariadic() {
80                 return errors.New("need nonvariadic func in " + ft.String())
81         }
82
83         if ft.NumIn() < 1 || ft.NumIn() > 3 {
84                 return errors.New("need one or two or three parameters in " + ft.String())
85         }
86
87         if ft.In(0) != contextType {
88                 return errors.New("the first parameter must point of context in " + ft.String())
89         }
90
91         if ft.NumIn() == 2 && ft.In(1).Kind() != reflect.Ptr {
92                 return errors.New("the second parameter must point in " + ft.String())
93         }
94
95         if ft.NumIn() == 3 && ft.In(2) != paginationQueryType {
96                 return errors.New("the third parameter of pagination must point of paginationQuery in " + ft.String())
97         }
98
99         if ft.NumOut() < 1 || ft.NumOut() > 2 {
100                 return errors.New("the size of return value must one or two in " + ft.String())
101         }
102
103         // if has pagination, the first return value must slice or array
104         if ft.NumIn() == 3 && ft.Out(0).Kind() != reflect.Slice && ft.Out(0).Kind() != reflect.Array {
105                 return errors.New("the first return value of pagination must slice of array in " + ft.String())
106         }
107
108         if !ft.Out(ft.NumOut() - 1).Implements(errorType) {
109                 return errors.New("the last return value must error in " + ft.String())
110         }
111         return nil
112 }
113
114 // handleRequest get a handler function to process the request by request url
115 func handleRequest(context *gin.Context, fun handlerFun) {
116         args, err := buildHandleFuncArgs(fun, context)
117         if err != nil {
118                 respondErrorResp(context, err)
119                 return
120         }
121
122         result := callHandleFunc(fun, args...)
123         if err := result[len(result)-1]; err != nil {
124                 respondErrorResp(context, err.(error))
125                 return
126         }
127
128         if exist := processPaginationIfPresent(fun, args, result, context); exist {
129                 return
130         }
131
132         if len(result) == 1 {
133                 respondSuccessResp(context, nil)
134                 return
135         }
136
137         respondSuccessResp(context, result[0])
138 }
139
140 func buildHandleFuncArgs(fun handlerFun, context *gin.Context) ([]interface{}, error) {
141         args := []interface{}{context}
142
143         req, err := createHandleReqArg(fun, context)
144         if err != nil {
145                 return nil, errors.Wrap(err, "createHandleReqArg")
146         }
147
148         if err := checkDisplayOrder(req); err != nil {
149                 return nil, err
150         }
151
152         if req != nil {
153                 args = append(args, req)
154         }
155
156         ft := reflect.TypeOf(fun)
157
158         // no pagination exists
159         if ft.NumIn() != 3 {
160                 return args, nil
161         }
162
163         query, err := parsePagination(context)
164         if err != nil {
165                 return nil, errors.Wrap(err, "ParsePagination")
166         }
167
168         args = append(args, query)
169         return args, nil
170 }
171
172 func createHandleReqArg(fun handlerFun, context *gin.Context) (interface{}, error) {
173         ft := reflect.TypeOf(fun)
174         if ft.NumIn() == 1 {
175                 return nil, nil
176         }
177         argType := ft.In(1).Elem()
178
179         reqArg := reflect.New(argType).Interface()
180         if err := context.ShouldBindJSON(reqArg); err != nil {
181                 return nil, errors.Wrap(err, "bind reqArg")
182         }
183
184         b, err := json.Marshal(reqArg)
185         if err != nil {
186                 return nil, errors.Wrap(err, "json marshal")
187         }
188
189         context.Set(reqBodyLabel, string(b))
190
191         return reqArg, nil
192 }
193
194 func checkDisplayOrder(req interface{}) error {
195         if req == nil {
196                 return nil
197         }
198
199         reqType := reflect.TypeOf(req).Elem()
200         reqVal := reflect.ValueOf(req).Elem()
201
202         for i := 0; i < reqType.NumField(); i++ {
203                 field := reqType.Field(i)
204                 if field.Type != reflect.TypeOf(Display{}) {
205                         continue
206                 }
207                 display := reqVal.Field(i).Interface().(Display)
208
209                 order := strings.Trim(display.Sorter.Order, "")
210                 if order != "desc" && order != "asc" {
211                         reqVal.Field(i).Set(reflect.ValueOf(Display{Filter: display.Filter, Sorter: Sorter{By: display.Sorter.By, Order: "desc"}}))
212                 }
213         }
214         return nil
215 }
216
217 func callHandleFunc(fun handlerFun, args ...interface{}) []interface{} {
218         fv := reflect.ValueOf(fun)
219
220         params := make([]reflect.Value, len(args))
221         for i, arg := range args {
222                 params[i] = reflect.ValueOf(arg)
223         }
224
225         rs := fv.Call(params)
226         result := make([]interface{}, len(rs))
227         for i, r := range rs {
228                 result[i] = r.Interface()
229         }
230         return result
231 }