OSDN Git Service

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