Last active
February 2, 2021 11:55
-
-
Save cyberhck/37a286cbc3040ea3e61642fc151e2f5d to your computer and use it in GitHub Desktop.
test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"github.com/cyberhck/local/tonic" | |
) | |
type User struct { | |
Name string `json:"name"` | |
Email string `json:"email"` | |
Authorization string `in:"header" name:"Authorization"` | |
} | |
type UserResponse struct { | |
Name string `json:"name"` | |
Value string `json:"value"` | |
} | |
func main() { | |
t := tonic.New() | |
t.POST("/", NewController) | |
t.POST("/hello", NewController1) | |
t.Run() | |
} | |
func NewController(user User) UserResponse { | |
return UserResponse{ | |
Name: user.Name + user.Email, | |
Value: user.Authorization, | |
} | |
} | |
func NewController1(user User) UserResponse { | |
return UserResponse{ | |
Name: user.Name + user.Email, | |
Value: user.Authorization, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tonic | |
import ( | |
"encoding/json" | |
"github.com/gin-gonic/gin" | |
"github.com/go-openapi/spec" | |
"reflect" | |
"strconv" | |
) | |
type Tonic struct { | |
router *gin.Engine | |
docs spec.Swagger | |
} | |
func New() Tonic { | |
tonic := Tonic{ | |
router: gin.Default(), | |
docs: spec.Swagger{ | |
SwaggerProps: spec.SwaggerProps{ | |
Swagger: "2.0", | |
Consumes: []string{"application/json"}, | |
Produces: []string{"application/json"}, | |
Schemes: []string{"http", "https"}, | |
Info: &spec.Info{ | |
InfoProps: spec.InfoProps{ | |
Description: "local server api docs", | |
Title: "local server", | |
TermsOfService: "TOS", | |
Contact: &spec.ContactInfo{ | |
ContactInfoProps: spec.ContactInfoProps{ | |
Name: "opn", | |
Email: "[email protected]", | |
}, | |
}, | |
Version: "2.0", | |
}, | |
}, | |
Paths: &spec.Paths{ | |
Paths: map[string]spec.PathItem{ | |
}, | |
}, | |
}, | |
}, | |
} | |
tonic.router.GET("/swagger", func(context *gin.Context) { | |
context.JSON(200, tonic.getDocs()) | |
}) | |
return tonic | |
} | |
func (t Tonic) getDocs() spec.Swagger { | |
return t.docs | |
} | |
func (t Tonic) POST(path string, handler interface{}) { | |
t.docs.SwaggerProps.Paths.Paths[path] = spec.PathItem{ | |
PathItemProps: spec.PathItemProps{ | |
Post: &spec.Operation{ | |
OperationProps: spec.OperationProps{ | |
}, | |
}, | |
}, | |
} | |
t.router.POST(path, t.Handler(handler)) | |
} | |
func (t Tonic) GET(path string, handler interface{}) { | |
t.router.GET(path, t.Handler(handler)) | |
} | |
func (t Tonic) Run() { | |
t.router.Run() | |
} | |
func (t Tonic) verifyConditions(handler interface{}) { | |
if !t.isFunction(handler) { | |
panic("handler must be a function") | |
} | |
if t.getParameterCount(handler) != 1 { | |
panic("handler must accept exactly 1 argument") | |
} | |
if t.getFirstArgumentType(handler) != reflect.Struct { | |
panic("only parameter must be a struct") | |
} | |
} | |
func (t Tonic) isFunction(i interface{}) bool { | |
return reflect.TypeOf(i).Kind() == reflect.Func | |
} | |
func (t Tonic) getParameterCount(i interface{}) int { | |
return reflect.TypeOf(i).NumIn() | |
} | |
type factoryString = func(ctx *gin.Context) string | |
type factoryInt = func(ctx *gin.Context) int | |
func (t Tonic) getFactories(request reflect.Type) (map[string]factoryString, map[string]factoryInt) { | |
mString := make(map[string]factoryString) | |
mInt := make(map[string]factoryInt) | |
for i := 0; i < request.NumField(); i++ { | |
field := request.Field(i) | |
if field.Tag.Get("in") == "" { | |
continue | |
} | |
if field.Type.Kind() == reflect.Int { | |
mInt[field.Name] = t.getIntFactoryFor(field.Tag) | |
continue | |
} | |
if field.Type.Kind() == reflect.String { | |
mString[field.Name] = t.getStringFactoryFor(request.Field(i).Tag) | |
continue | |
} | |
panic("unsupported type: " + field.Type.Kind().String()) | |
} | |
return mString, mInt | |
} | |
func (t Tonic) getStringFactoryFor(tag reflect.StructTag) factoryString { | |
if tag.Get("in") != "header" { | |
panic("currently only header supported on `in` tag") // implement others? | |
} | |
return func(ctx *gin.Context) string { | |
headerName := tag.Get("name") | |
return ctx.Request.Header.Get(headerName) | |
} | |
} | |
func (t Tonic) getIntFactoryFor(tag reflect.StructTag) factoryInt { | |
if tag.Get("in") != "header" { | |
panic("currently only header supported on `in` tag") // implement others? | |
} | |
return func(ctx *gin.Context) int { | |
headerName := tag.Get("name") | |
value, _ := strconv.Atoi(ctx.Request.Header.Get(headerName)) | |
return value | |
} | |
} | |
func (t Tonic) Handler(handler interface{}) func(context *gin.Context) { | |
t.verifyConditions(handler) | |
request := reflect.TypeOf(handler).In(0) | |
stringFac, intFac := t.getFactories(request) | |
return func(context *gin.Context) { | |
req := reflect.New(request).Interface() | |
err := json.NewDecoder(context.Request.Body).Decode(&req) | |
if err != nil { | |
panic(err) | |
} | |
for key, value := range stringFac { | |
result := value(context) | |
reflect.ValueOf(req).Elem().FieldByName(key).SetString(result) | |
} | |
for key, value := range intFac { | |
result := value(context) | |
reflect.ValueOf(req).Elem().FieldByName(key).SetInt(int64(result)) | |
} | |
response := reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(req).Elem()}) | |
context.JSON(200, response[0].Interface()) | |
} | |
} | |
func (t Tonic) getFirstArgumentType(i interface{}) reflect.Kind { | |
return reflect.TypeOf(i).In(0).Kind() | |
} | |
func (t Tonic) getReturnArgCount(i interface{}) int { | |
return reflect.TypeOf(i).NumOut() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment