Timo Vetter
c3318cc1de
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 51s
227 lines
5.8 KiB
Go
227 lines
5.8 KiB
Go
package ginext
|
|
|
|
import (
|
|
"github.com/gin-gonic/gin"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
|
"net/http"
|
|
"path"
|
|
"reflect"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
var anyMethods = []string{
|
|
http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch,
|
|
http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect,
|
|
http.MethodTrace,
|
|
}
|
|
|
|
type GinRoutesWrapper struct {
|
|
wrapper *GinWrapper
|
|
routes gin.IRouter
|
|
absPath string
|
|
defaultHandler []gin.HandlerFunc
|
|
}
|
|
|
|
type GinRouteBuilder struct {
|
|
routes *GinRoutesWrapper
|
|
|
|
method string
|
|
relPath string
|
|
absPath string
|
|
handlers []gin.HandlerFunc
|
|
}
|
|
|
|
func (w *GinWrapper) Routes() *GinRoutesWrapper {
|
|
return &GinRoutesWrapper{
|
|
wrapper: w,
|
|
routes: w.engine,
|
|
absPath: "",
|
|
defaultHandler: make([]gin.HandlerFunc, 0),
|
|
}
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) Group(relativePath string) *GinRoutesWrapper {
|
|
return &GinRoutesWrapper{
|
|
wrapper: w.wrapper,
|
|
routes: w.routes.Group(relativePath),
|
|
defaultHandler: langext.ArrCopy(w.defaultHandler),
|
|
absPath: joinPaths(w.absPath, relativePath),
|
|
}
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) Use(middleware ...gin.HandlerFunc) *GinRoutesWrapper {
|
|
defHandler := langext.ArrCopy(w.defaultHandler)
|
|
defHandler = append(defHandler, middleware...)
|
|
return &GinRoutesWrapper{wrapper: w.wrapper, routes: w.routes, defaultHandler: defHandler}
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) WithJSONFilter(filter string) *GinRoutesWrapper {
|
|
defHandler := langext.ArrCopy(w.defaultHandler)
|
|
defHandler = append(defHandler, func(g *gin.Context) {
|
|
g.Set("ginext.jsonfilter", filter)
|
|
})
|
|
return &GinRoutesWrapper{wrapper: w.wrapper, routes: w.routes, defaultHandler: defHandler}
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) GET(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodGet, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) POST(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodPost, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) DELETE(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodDelete, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) PATCH(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodPatch, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) PUT(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodPut, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) OPTIONS(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodOptions, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) HEAD(relativePath string) *GinRouteBuilder {
|
|
return w._route(http.MethodHead, relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) COUNT(relativePath string) *GinRouteBuilder {
|
|
return w._route("COUNT", relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) Any(relativePath string) *GinRouteBuilder {
|
|
return w._route("*", relativePath)
|
|
}
|
|
|
|
func (w *GinRoutesWrapper) _route(method string, relativePath string) *GinRouteBuilder {
|
|
return &GinRouteBuilder{
|
|
routes: w,
|
|
method: method,
|
|
relPath: relativePath,
|
|
absPath: joinPaths(w.absPath, relativePath),
|
|
handlers: langext.ArrCopy(w.defaultHandler),
|
|
}
|
|
}
|
|
|
|
func (w *GinRouteBuilder) Use(middleware ...gin.HandlerFunc) *GinRouteBuilder {
|
|
w.handlers = append(w.handlers, middleware...)
|
|
return w
|
|
}
|
|
|
|
func (w *GinRouteBuilder) WithJSONFilter(filter string) *GinRouteBuilder {
|
|
w.handlers = append(w.handlers, func(g *gin.Context) {
|
|
g.Set("ginext.jsonfilter", filter)
|
|
})
|
|
return w
|
|
}
|
|
|
|
func (w *GinRouteBuilder) Handle(handler WHandlerFunc) {
|
|
|
|
if w.routes.wrapper.bufferBody {
|
|
arr := make([]gin.HandlerFunc, 0, len(w.handlers)+1)
|
|
arr = append(arr, BodyBuffer)
|
|
arr = append(arr, w.handlers...)
|
|
w.handlers = arr
|
|
}
|
|
|
|
middlewareNames := langext.ArrMap(w.handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) })
|
|
handlerName := nameOfFunction(handler)
|
|
|
|
w.handlers = append(w.handlers, Wrap(w.routes.wrapper, handler))
|
|
|
|
methodName := w.method
|
|
|
|
if w.method == "*" {
|
|
methodName = "ANY"
|
|
for _, method := range anyMethods {
|
|
w.routes.routes.Handle(method, w.relPath, w.handlers...)
|
|
}
|
|
} else {
|
|
w.routes.routes.Handle(w.method, w.relPath, w.handlers...)
|
|
}
|
|
|
|
w.routes.wrapper.routeSpecs = append(w.routes.wrapper.routeSpecs, ginRouteSpec{
|
|
Method: methodName,
|
|
URL: w.absPath,
|
|
Middlewares: middlewareNames,
|
|
Handler: handlerName,
|
|
})
|
|
}
|
|
|
|
func (w *GinWrapper) NoRoute(handler WHandlerFunc) {
|
|
|
|
handlers := make([]gin.HandlerFunc, 0)
|
|
|
|
if w.bufferBody {
|
|
handlers = append(handlers, BodyBuffer)
|
|
}
|
|
|
|
middlewareNames := langext.ArrMap(handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) })
|
|
handlerName := nameOfFunction(handler)
|
|
|
|
handlers = append(handlers, Wrap(w, handler))
|
|
|
|
w.engine.NoRoute(handlers...)
|
|
|
|
w.routeSpecs = append(w.routeSpecs, ginRouteSpec{
|
|
Method: "ANY",
|
|
URL: "[NO_ROUTE]",
|
|
Middlewares: middlewareNames,
|
|
Handler: handlerName,
|
|
})
|
|
}
|
|
|
|
func nameOfFunction(f any) string {
|
|
|
|
fname := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
|
|
|
split := strings.Split(fname, "/")
|
|
if len(split) == 0 {
|
|
return ""
|
|
}
|
|
|
|
fname = split[len(split)-1]
|
|
|
|
// https://stackoverflow.com/a/32925345/1761622
|
|
if strings.HasSuffix(fname, "-fm") {
|
|
fname = fname[:len(fname)-len("-fm")]
|
|
}
|
|
|
|
suffix := rext.W(regexp.MustCompile(`\.func[0-9]+(?:\.[0-9]+)*$`))
|
|
|
|
if match, ok := suffix.MatchFirst(fname); ok {
|
|
fname = fname[:len(fname)-match.FullMatch().Length()]
|
|
}
|
|
|
|
return fname
|
|
}
|
|
|
|
// joinPaths is copied verbatim from gin@v1.9.1/gin.go
|
|
func joinPaths(absolutePath, relativePath string) string {
|
|
if relativePath == "" {
|
|
return absolutePath
|
|
}
|
|
|
|
finalPath := path.Join(absolutePath, relativePath)
|
|
if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' {
|
|
return finalPath + "/"
|
|
}
|
|
return finalPath
|
|
}
|
|
|
|
func lastChar(str string) uint8 {
|
|
if str == "" {
|
|
panic("The length of the string can't be 0")
|
|
}
|
|
return str[len(str)-1]
|
|
}
|