diff options
Diffstat (limited to 'api/api.go')
-rw-r--r-- | api/api.go | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..7b7be49 --- /dev/null +++ b/api/api.go | |||
@@ -0,0 +1,130 @@ | |||
1 | package api | ||
2 | |||
3 | import ( | ||
4 | "net/http" | ||
5 | "unicode" | ||
6 | |||
7 | "github.com/gin-gonic/gin" | ||
8 | ) | ||
9 | |||
10 | type Error struct { | ||
11 | Code ErrorCode | ||
12 | UserMessage string | ||
13 | err error | ||
14 | } | ||
15 | |||
16 | func (e Error) Err() error { | ||
17 | if e.err != nil { | ||
18 | return e | ||
19 | } | ||
20 | |||
21 | return nil | ||
22 | } | ||
23 | |||
24 | func (e Error) Error() string { | ||
25 | if e.err != nil { | ||
26 | return e.err.Error() | ||
27 | } | ||
28 | |||
29 | return "" | ||
30 | } | ||
31 | |||
32 | func NewInternalError(err error) *Error { | ||
33 | return &Error{InternalError, "internal error", err} | ||
34 | } | ||
35 | |||
36 | func ToSnake(in string) string { | ||
37 | runes := []rune(in) | ||
38 | length := len(runes) | ||
39 | |||
40 | var out []rune | ||
41 | for i := 0; i < length; i++ { | ||
42 | if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) { | ||
43 | out = append(out, '_') | ||
44 | } | ||
45 | out = append(out, unicode.ToLower(runes[i])) | ||
46 | } | ||
47 | |||
48 | return string(out) | ||
49 | } | ||
50 | |||
51 | type Response struct { | ||
52 | StatusCode Status `json:"-"` | ||
53 | ErrorCode ErrorCode `json:"-"` | ||
54 | |||
55 | Status string `json:"status"` | ||
56 | Code string `json:"code,omitempty"` | ||
57 | Response interface{} `json:"response,omitempty"` | ||
58 | Message string `json:"message,omitempty"` | ||
59 | } | ||
60 | |||
61 | func (r Response) populateStatus() Response { | ||
62 | r.Status = ToSnake(r.StatusCode.String()) | ||
63 | |||
64 | if r.ErrorCode != 0 { | ||
65 | r.Code = ToSnake(r.ErrorCode.String()) | ||
66 | } | ||
67 | |||
68 | return r | ||
69 | } | ||
70 | |||
71 | func ErrorResponse(code ErrorCode, message string) Response { | ||
72 | return Response{ | ||
73 | StatusCode: NOK, | ||
74 | ErrorCode: code, | ||
75 | Message: message, | ||
76 | } | ||
77 | } | ||
78 | |||
79 | func SuccessResponse(data interface{}) Response { | ||
80 | return Response{ | ||
81 | StatusCode: OK, | ||
82 | Response: data, | ||
83 | } | ||
84 | } | ||
85 | |||
86 | func WriteJsonResponse(response Response, c *gin.Context) { | ||
87 | response = response.populateStatus() | ||
88 | |||
89 | c.JSON(StatusToHttpCode(response.StatusCode, response.ErrorCode), response) | ||
90 | } | ||
91 | |||
92 | func WriteBinary(contentType string, b []byte, c *gin.Context) { | ||
93 | c.Data(http.StatusOK, contentType, b) | ||
94 | } | ||
95 | |||
96 | type Middleware func(*gin.Context) *Error | ||
97 | |||
98 | func M(handler Middleware) gin.HandlerFunc { | ||
99 | return func(c *gin.Context) { | ||
100 | err := handler(c) | ||
101 | |||
102 | if err != nil { | ||
103 | WriteJsonResponse(ErrorResponse(err.Code, err.UserMessage), c) | ||
104 | c.Error(err) | ||
105 | c.Abort() | ||
106 | } else { | ||
107 | c.Next() | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | type Query interface { | ||
113 | ValidateParams() *Error | ||
114 | Run() (interface{}, *Error) | ||
115 | } | ||
116 | |||
117 | func RunQuery(query Query, c *gin.Context) { | ||
118 | if err := query.ValidateParams(); err != nil { | ||
119 | WriteJsonResponse(ErrorResponse(err.Code, err.UserMessage), c) | ||
120 | c.Error(err) | ||
121 | return | ||
122 | } | ||
123 | |||
124 | if out, err := query.Run(); err != nil { | ||
125 | WriteJsonResponse(ErrorResponse(err.Code, err.UserMessage), c) | ||
126 | c.Error(err) | ||
127 | } else { | ||
128 | WriteJsonResponse(SuccessResponse(out), c) | ||
129 | } | ||
130 | } | ||