Skip to content

Commit

Permalink
tonic: bind keys from gin.Context
Browse files Browse the repository at this point in the history
  • Loading branch information
wwwxu committed Apr 24, 2024
1 parent deb3e10 commit 29c3d9e
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 10 deletions.
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ require (
github.com/go-playground/validator/v10 v10.19.0
github.com/google/uuid v1.6.0
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f
github.com/mattn/go-sqlite3 v1.14.22
github.com/juju/testing v0.0.0-20210302031854-2c7ee8570c07 // indirect
github.com/lib/pq v1.9.0 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pires/go-proxyproto v0.7.0
github.com/poy/onpar v1.1.2 // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
golang.org/x/sys v0.17.0 // indirect
sigs.k8s.io/yaml v1.4.0
)

Expand All @@ -21,22 +28,15 @@ require (
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/testing v0.0.0-20210302031854-2c7ee8570c07 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lib/pq v1.9.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/poy/onpar v1.1.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
Expand Down
5 changes: 5 additions & 0 deletions tonic/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ func Handler(h interface{}, status int, options ...func(*Route)) gin.HandlerFunc
handleError(c, err)
return
}
// Bind context-keys
if err := bind(c, input, ContextTag, extractContext); err != nil {
handleError(c, err)
return
}
// validating query and path inputs if they have a validate tag
initValidator()
args = append(args, input)
Expand Down
25 changes: 25 additions & 0 deletions tonic/tonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
QueryTag = "query"
PathTag = "path"
HeaderTag = "header"
ContextTag = "context"
EnumTag = "enum"
RequiredTag = "required"
DefaultTag = "default"
Expand Down Expand Up @@ -354,6 +355,30 @@ func extractHeader(c *gin.Context, tag string) (string, []string, error) {
return name, []string{header}, nil
}

// extractContext is an extractor that operates on the gin.Context
// of a request.
func extractContext(c *gin.Context, tag string) (string, []string, error) {
name, required, defaultVal, err := parseTagKey(tag)
if err != nil {
return "", nil, err
}
context := c.GetString(name)

// XXX: deprecated, use of "default" tag is preferred
if context == "" && defaultVal != "" {
return name, []string{defaultVal}, nil
}
// XXX: deprecated, use of "validate" tag is preferred
if required && context == "" {
return "", nil, fmt.Errorf("missing header parameter: %s", name)
}

if len(context) == 0 {
return name, []string{}, nil
}
return name, []string{context}, nil
}

// Public signature does not expose "required" and "default" because
// they are deprecated in favor of the "validate" and "default" tags
func parseTagKey(tag string) (string, bool, string, error) {
Expand Down
39 changes: 39 additions & 0 deletions tonic/tonic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,31 @@ func TestMain(m *testing.M) {
tonic.SetErrorHook(errorHook)

g := gin.Default()

// for context test
g.Use(func(c *gin.Context) {
if c.FullPath() == "/context" {
if val, ok := c.GetQuery("param"); ok {
c.Set("param", val)
}
if val, ok := c.GetQuery("param-optional"); ok {
c.Set("param-optional", val)
}
if val, ok := c.GetQuery("param-optional-validated"); ok {
c.Set("param-optional-validated", val)
}
}
c.Next()
})

g.GET("/simple", tonic.Handler(simpleHandler, 200))
g.GET("/scalar", tonic.Handler(scalarHandler, 200))
g.GET("/error", tonic.Handler(errorHandler, 200))
g.GET("/path/:param", tonic.Handler(pathHandler, 200))
g.GET("/query", tonic.Handler(queryHandler, 200))
g.GET("/query-old", tonic.Handler(queryHandlerOld, 200))
g.POST("/body", tonic.Handler(bodyHandler, 200))
g.GET("/context", tonic.Handler(contextHandler, 200))

r = g

Expand Down Expand Up @@ -130,6 +148,17 @@ func TestBody(t *testing.T) {
tester.Run()
}

func TestContext(t *testing.T) {
tester := iffy.NewTester(t, r)

tester.AddCall("context", "GET", "/context?param=foo", ``).Checkers(iffy.ExpectStatus(200), expectString("param", "foo"))
tester.AddCall("context", "GET", "/context", ``).Checkers(iffy.ExpectStatus(400))
tester.AddCall("context", "GET", "/context?param=foo&param-optional=bar", ``).Checkers(iffy.ExpectStatus(200), expectString("param-optional", "bar"))
tester.AddCall("context", "GET", "/context?param=foo&param-optional-validated=foo", ``).Checkers(iffy.ExpectStatus(200), expectString("param-optional-validated", "foo"))

tester.Run()
}

func errorHandler(c *gin.Context) error {
return errors.New("error")
}
Expand Down Expand Up @@ -199,6 +228,16 @@ func bodyHandler(c *gin.Context, in *bodyIn) (*bodyIn, error) {
return in, nil
}

type ContextIn struct {
Param string `context:"param" json:"param" validate:"required"`
ParamOptional string `context:"param-optional" json:"param-optional"`
ValidatedParamOptional string `context:"param-optional-validated" json:"param-optional-validated" validate:"eq=|eq=foo|gt=10"`
}

func contextHandler(c *gin.Context, in *ContextIn) (*ContextIn, error) {
return in, nil
}

func expectEmptyBody(r *http.Response, body string, obj interface{}) error {
if len(body) != 0 {
return fmt.Errorf("Body '%s' should be empty", body)
Expand Down

0 comments on commit 29c3d9e

Please sign in to comment.