Skip to content

Commit

Permalink
Support gRPC transcoding dot notation syntax in path template
Browse files Browse the repository at this point in the history
  • Loading branch information
toctan committed Nov 3, 2020
1 parent 03a2faa commit e9b5e99
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 29 deletions.
37 changes: 37 additions & 0 deletions gengokit/httptransport/embeddable_funcs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package httptransport

import (
"encoding/json"
"fmt"
"strings"
)
Expand Down Expand Up @@ -69,3 +70,39 @@ func RemoveBraces(val string) string {
val = strings.Replace(val, "}", "", -1)
return val
}

// encodePathParams encodes `mux.Vars()` with dot notations into JSON objects
// to be unmarshaled into non-basetype fields.
// e.g. {"book.name": "books/1"} -> {"book": {"name": "books/1"}}
func encodePathParams(vars map[string]string) map[string]string {
var recur func(path, value string, data map[string]interface{})
recur = func(path, value string, data map[string]interface{}) {
parts := strings.SplitN(path, ".", 2)
key := parts[0]
if len(parts) == 1 {
data[key] = value
} else {
if _, ok := data[key]; !ok {
data[key] = make(map[string]interface{})
}
recur(parts[1], value, data[key].(map[string]interface{}))
}
}

data := make(map[string]interface{})
for key, val := range vars {
recur(key, val, data)
}

ret := make(map[string]string)
for key, val := range data {
switch val := val.(type) {
case string:
ret[key] = val
case map[string]interface{}:
m, _ := json.Marshal(val)
ret[key] = string(m)
}
}
return ret
}
69 changes: 69 additions & 0 deletions gengokit/httptransport/embeddable_funcs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package httptransport

import (
"reflect"
"testing"
)

func TestEncodePathParams(t *testing.T) {
tests := []struct {
name string
vars map[string]string
want map[string]string
}{
{
name: "simple",
vars: map[string]string{
"parent": "shelves/shelf1",
},
want: map[string]string{
"parent": "shelves/shelf1",
},
},
{
name: "dot notation - single value",
vars: map[string]string{
"book.name": "shelves/shelf1/books/book1",
},
want: map[string]string{
"book": `{"name":"shelves/shelf1/books/book1"}`,
},
},
{
name: "dot notation - multiple values",
vars: map[string]string{
"book.name": "shelves/shelf1/books/book1",
"book.version": "v1",
},
want: map[string]string{
"book": `{"name":"shelves/shelf1/books/book1","version":"v1"}`,
},
},
{
name: "dot notation - multiple levels",
vars: map[string]string{
"book.version.name": "versions/v1",
},
want: map[string]string{
"book": `{"version":{"name":"versions/v1"}}`,
},
},
{
name: "dot notation - multiple values in multiple levels",
vars: map[string]string{
"book.name": "shelves/shelf1/books/book1",
"book.version.name": "versions/v1",
},
want: map[string]string{
"book": `{"name":"shelves/shelf1/books/book1","version":{"name":"versions/v1"}}`,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := encodePathParams(tt.vars); !reflect.DeepEqual(got, tt.want) {
t.Errorf("encodePathParams() = %v, want %v", got, tt.want)
}
})
}
}
26 changes: 17 additions & 9 deletions gengokit/httptransport/httptransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,11 @@ func GenServerTemplate(exec interface{}) (string, error) {
if err != nil {
return "", err
}
code = FormatCode(code)
encodeFuncSource, err := FuncSourceCode(encodePathParams)
if err != nil {
return "", err
}
code = FormatCode(code + encodeFuncSource)
return code, nil
}

Expand Down Expand Up @@ -258,13 +262,19 @@ func (b *Binding) PathSections() []string {
parts := strings.Split(path, "/")
for _, part := range parts {
if len(part) > 2 && part[0] == '{' && part[len(part)-1] == '}' {
name := RemoveBraces(part)
if _, ok := isEnum[gogen.CamelCase(name)]; ok {
convert := fmt.Sprintf("fmt.Sprintf(\"%%d\", req.%v)", gogen.CamelCase(name))
name := part[1 : len(part)-1]
parts := strings.Split(name, ".")
for idx, part := range parts {
parts[idx] = gogen.CamelCase(part)
}
camelName := strings.Join(parts, ".")

if _, ok := isEnum[camelName]; ok {
convert := fmt.Sprintf("fmt.Sprintf(\"%%d\", req.%v)", camelName)
rv = append(rv, convert)
continue
}
convert := fmt.Sprintf("fmt.Sprint(req.%v)", gogen.CamelCase(name))
convert := fmt.Sprintf("fmt.Sprint(req.%v)", camelName)
rv = append(rv, convert)
} else {
// Add quotes around things which'll be embeded as string literals,
Expand All @@ -291,7 +301,7 @@ if {{.LocalName}}StrArr, ok := {{.Location}}Params["{{.QueryParamName}}"]; ok {
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("Error while extracting {{.LocalName}} from {{.Location}}, {{.Location}}Params: %v", {{.Location}}Params))
}{{end}}
req.{{.CamelName}} = {{.TypeConversion}}
{{if or .Repeated .IsBaseType .IsEnum}}req.{{.CamelName}} = {{.TypeConversion}}{{end}}
`
mergedLogic := queryParamLogic + genericLogic + "}"
if f.Location == "path" {
Expand Down Expand Up @@ -389,9 +399,7 @@ func createDecodeConvertFunc(f Field) (string, bool) {
// pointer as well. So we special case args of a single custom message
// type so that the variable LocalName is declared as a pointer.
singleCustomTypeUnmarshalTmpl := `
var {{.LocalName}} *{{.GoType}}
{{.LocalName}} = &{{.GoType}}{}
err = json.Unmarshal([]byte({{.LocalName}}Str), {{.LocalName}})`
err = json.Unmarshal([]byte({{.LocalName}}Str), req.{{.CamelName}})`

errorCheckingTmpl := `
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions gengokit/httptransport/httptransport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,15 @@ func TestBinding_PathSections(t *testing.T) {
`"books"`,
},
},
{
name: "dot notation",
pathTemplate: `/v1/{book.name:shelves/[^/]+/books/[^/]+}`,
want: []string{
`""`,
`"v1"`,
"fmt.Sprint(req.Book.Name)",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion gengokit/httptransport/templates/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var ServerDecodeTemplate = `
}
}
pathParams := mux.Vars(r)
pathParams := encodePathParams(mux.Vars(r))
_ = pathParams
queryParams := r.URL.Query()
Expand Down
2 changes: 1 addition & 1 deletion gengokit/httptransport/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func DecodeHTTPSumZeroRequest(_ context.Context, r *http.Request) (interface{},
}
}
pathParams := mux.Vars(r)
pathParams := encodePathParams(mux.Vars(r))
_ = pathParams
queryParams := r.URL.Query()
Expand Down
36 changes: 18 additions & 18 deletions gengokit/template/template.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.13
require (
github.com/davecgh/go-spew v1.1.1
github.com/gogo/protobuf v1.2.2-0.20190601103108-21df5aa0e680
github.com/kevinburke/go-bindata v3.22.0+incompatible // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/pkg/errors v0.8.1
github.com/pmezard/go-difflib v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gogo/protobuf v1.2.2-0.20190601103108-21df5aa0e680 h1:C0sYRtvp5PUPDdevTvhGWbyiRrLM1ci+tHfyJymCNzM=
github.com/gogo/protobuf v1.2.2-0.20190601103108-21df5aa0e680/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/kevinburke/go-bindata v3.22.0+incompatible h1:/JmqEhIWQ7GRScV0WjX/0tqBrC5D21ALg0H0U/KZ/ts=
github.com/kevinburke/go-bindata v3.22.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
Expand Down

0 comments on commit e9b5e99

Please sign in to comment.