Skip to content

Commit e0ea1eb

Browse files
committed
fix: correctly map request body field by name
Fixes #325
1 parent 07f56d6 commit e0ea1eb

8 files changed

Lines changed: 260 additions & 22 deletions

File tree

gengokit/httptransport/httptransport.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ func NewBinding(i int, meth *svcdef.ServiceMethod) *Binding {
156156
newField.IsEnum = field.Type.Enum != nil
157157
newField.ConvertFunc, newField.ConvertFuncNeedsErrorCheck = createDecodeConvertFunc(newField)
158158
newField.TypeConversion = createDecodeTypeConversion(newField)
159+
if newField.Location == "body_root" {
160+
newField.Location = "body"
161+
nBinding.RequestRootField = &newField
162+
}
159163

160164
nBinding.Fields = append(nBinding.Fields, &newField)
161165

gengokit/httptransport/httptransport_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,97 @@ func init() {
2121
gopath = filepath.SplitList(os.Getenv("GOPATH"))
2222
}
2323

24+
func TestNewMethodWithBody(t *testing.T) {
25+
defStr := `
26+
syntax = "proto3";
27+
28+
// General package
29+
package general;
30+
31+
import "github.com/metaverse/truss/deftree/googlethirdparty/annotations.proto";
32+
33+
message Inner {
34+
string a = 1;
35+
}
36+
37+
message SumRequest {
38+
int64 a = 1;
39+
Inner in = 2;
40+
}
41+
42+
message SumReply {
43+
int64 v = 1;
44+
string err = 2;
45+
}
46+
47+
service SumSvc {
48+
rpc Sum(SumRequest) returns (SumReply) {
49+
option (google.api.http) = {
50+
put: "/sum/{a}"
51+
body: "in"
52+
};
53+
}
54+
}
55+
`
56+
sd, err := svcdef.NewFromString(defStr, gopath)
57+
if err != nil {
58+
t.Fatal(err, "Failed to create a service from the definition string")
59+
}
60+
innerField := Field{
61+
Name: "In",
62+
QueryParamName: "in",
63+
CamelName: "In",
64+
LowCamelName: "in",
65+
LocalName: "InSum",
66+
Location: "body",
67+
GoType: "pb.Inner",
68+
ConvertFunc: "\nvar InSum *pb.Inner\nInSum = &pb.Inner{}\nerr = json.Unmarshal([]byte(InSumStr), InSum)\nif err != nil {\n\treturn nil, errors.Wrapf(err, \"couldn't decode InSum from %v\", InSumStr)\n}",
69+
ConvertFuncNeedsErrorCheck: false,
70+
TypeConversion: "InSum",
71+
IsBaseType: false,
72+
}
73+
binding := &Binding{
74+
Label: "SumZero",
75+
PathTemplate: "/sum/{a}",
76+
BasePath: "/sum/",
77+
Verb: "put",
78+
RequestRootField: &innerField,
79+
Fields: []*Field{
80+
&Field{
81+
Name: "A",
82+
QueryParamName: "a",
83+
CamelName: "A",
84+
LowCamelName: "a",
85+
LocalName: "ASum",
86+
Location: "path",
87+
GoType: "int64",
88+
ConvertFunc: "ASum, err := strconv.ParseInt(ASumStr, 10, 64)",
89+
ConvertFuncNeedsErrorCheck: true,
90+
TypeConversion: "ASum",
91+
IsBaseType: true,
92+
},
93+
&innerField,
94+
},
95+
}
96+
97+
meth := &Method{
98+
Name: "Sum",
99+
RequestType: "SumRequest",
100+
ResponseType: "SumReply",
101+
Bindings: []*Binding{
102+
binding,
103+
},
104+
}
105+
binding.Parent = meth
106+
107+
newMeth := NewMethod(sd.Service.Methods[0])
108+
t.Logf("%v\n", spew.Sdump(sd.Service.Methods[0]))
109+
if got, want := newMeth, meth; !reflect.DeepEqual(got, want) {
110+
diff := gentesthelper.DiffStrings(spew.Sdump(got), spew.Sdump(want))
111+
t.Errorf("got != want; methods differ: %v\n", diff)
112+
}
113+
}
114+
24115
func TestNewMethod(t *testing.T) {
25116
defStr := `
26117
syntax = "proto3";

gengokit/httptransport/templates/server.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ var ServerDecodeTemplate = `
99
// body. Primarily useful in a server.
1010
func DecodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request) (interface{}, error) {
1111
defer r.Body.Close()
12+
1213
var req pb.{{GoName $binding.Parent.RequestType}}
14+
{{$req_field := "req" -}}
15+
{{if $binding.RequestRootField -}}
16+
{{$req_field = print "req" ($binding.RequestRootField.Name) -}}
17+
var {{$req_field}} {{$binding.RequestRootField.GoType}}
18+
{{end -}}
19+
1320
buf, err := ioutil.ReadAll(r.Body)
1421
if err != nil {
1522
return nil, errors.Wrapf(err, "cannot read body of http request")
@@ -19,7 +26,7 @@ var ServerDecodeTemplate = `
1926
unmarshaller := jsonpb.Unmarshaler{
2027
AllowUnknownFields: true,
2128
}
22-
if err = unmarshaller.Unmarshal(bytes.NewBuffer(buf), &req); err != nil {
29+
if err = unmarshaller.Unmarshal(bytes.NewBuffer(buf), &{{$req_field}}); err != nil {
2330
const size = 8196
2431
if len(buf) > size {
2532
buf = buf[:size]
@@ -31,6 +38,10 @@ var ServerDecodeTemplate = `
3138
}
3239
}
3340
41+
{{if $binding.RequestRootField}}
42+
req.{{$binding.RequestRootField.Name}} = &{{$req_field}}
43+
{{end}}
44+
3445
pathParams := mux.Vars(r)
3546
_ = pathParams
3647

gengokit/httptransport/templates_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,15 @@ func TestGenServerDecode(t *testing.T) {
151151
if err != nil {
152152
t.Errorf("Failed to generate server decode code: %v", err)
153153
}
154+
154155
desired := `
155156
156157
// DecodeHTTPSumZeroRequest is a transport/http.DecodeRequestFunc that
157158
// decodes a JSON-encoded sum request from the HTTP request
158159
// body. Primarily useful in a server.
159160
func DecodeHTTPSumZeroRequest(_ context.Context, r *http.Request) (interface{}, error) {
160161
defer r.Body.Close()
162+
161163
var req pb.SumRequest
162164
buf, err := ioutil.ReadAll(r.Body)
163165
if err != nil {
@@ -211,3 +213,132 @@ func DecodeHTTPSumZeroRequest(_ context.Context, r *http.Request) (interface{},
211213
t.Log(gentesthelper.DiffStrings(got, want))
212214
}
213215
}
216+
217+
func TestGenServerDecodeWithBody(t *testing.T) {
218+
innerField := &Field{
219+
Name: "c",
220+
QueryParamName: "c",
221+
CamelName: "C",
222+
LowCamelName: "c",
223+
LocalName: "CSum",
224+
Location: "body",
225+
GoType: "pb.Inner",
226+
ConvertFunc: "",
227+
ConvertFuncNeedsErrorCheck: true,
228+
TypeConversion: "CSum",
229+
IsBaseType: true,
230+
}
231+
binding := &Binding{
232+
Label: "SumZero",
233+
PathTemplate: "/sum/{a}",
234+
BasePath: "/sum/",
235+
Verb: "get",
236+
RequestRootField: innerField,
237+
Fields: []*Field{
238+
&Field{
239+
Name: "a",
240+
QueryParamName: "a",
241+
CamelName: "A",
242+
LowCamelName: "a",
243+
LocalName: "ASum",
244+
Location: "path",
245+
GoType: "int64",
246+
ConvertFunc: "ASum, err := strconv.ParseInt(ASumStr, 10, 64)",
247+
ConvertFuncNeedsErrorCheck: true,
248+
TypeConversion: "ASum",
249+
IsBaseType: true,
250+
},
251+
&Field{
252+
Name: "b",
253+
QueryParamName: "b",
254+
CamelName: "B",
255+
LowCamelName: "b",
256+
LocalName: "BSum",
257+
Location: "query",
258+
GoType: "int64",
259+
ConvertFunc: "BSum, err := strconv.ParseInt(BSumStr, 10, 64)",
260+
ConvertFuncNeedsErrorCheck: true,
261+
TypeConversion: "BSum",
262+
IsBaseType: true,
263+
},
264+
innerField,
265+
},
266+
}
267+
meth := &Method{
268+
Name: "Sum",
269+
RequestType: "SumRequest",
270+
ResponseType: "SumReply",
271+
Bindings: []*Binding{
272+
binding,
273+
},
274+
}
275+
binding.Parent = meth
276+
277+
str, err := binding.GenServerDecode()
278+
if err != nil {
279+
t.Errorf("Failed to generate server decode code: %v", err)
280+
}
281+
desired := `
282+
283+
// DecodeHTTPSumZeroRequest is a transport/http.DecodeRequestFunc that
284+
// decodes a JSON-encoded sum request from the HTTP request
285+
// body. Primarily useful in a server.
286+
func DecodeHTTPSumZeroRequest(_ context.Context, r *http.Request) (interface{}, error) {
287+
defer r.Body.Close()
288+
289+
var req pb.SumRequest
290+
var reqc pb.Inner
291+
buf, err := ioutil.ReadAll(r.Body)
292+
if err != nil {
293+
return nil, errors.Wrapf(err, "cannot read body of http request")
294+
}
295+
if len(buf) > 0 {
296+
// AllowUnknownFields stops the unmarshaler from failing if the JSON contains unknown fields.
297+
unmarshaller := jsonpb.Unmarshaler{
298+
AllowUnknownFields: true,
299+
}
300+
if err = unmarshaller.Unmarshal(bytes.NewBuffer(buf), &reqc); err != nil {
301+
const size = 8196
302+
if len(buf) > size {
303+
buf = buf[:size]
304+
}
305+
return nil, httpError{errors.Wrapf(err, "request body '%s': cannot parse non-json request body", buf),
306+
http.StatusBadRequest,
307+
nil,
308+
}
309+
}
310+
}
311+
312+
req.c = &reqc
313+
314+
pathParams := mux.Vars(r)
315+
_ = pathParams
316+
317+
queryParams := r.URL.Query()
318+
_ = queryParams
319+
320+
ASumStr := pathParams["a"]
321+
ASum, err := strconv.ParseInt(ASumStr, 10, 64)
322+
if err != nil {
323+
return nil, errors.Wrap(err, fmt.Sprintf("Error while extracting ASum from path, pathParams: %v", pathParams))
324+
}
325+
req.A = ASum
326+
327+
if BSumStrArr, ok := queryParams["b"]; ok {
328+
BSumStr := BSumStrArr[0]
329+
BSum, err := strconv.ParseInt(BSumStr, 10, 64)
330+
if err != nil {
331+
return nil, errors.Wrap(err, fmt.Sprintf("Error while extracting BSum from query, queryParams: %v", queryParams))
332+
}
333+
req.B = BSum
334+
}
335+
336+
return &req, err
337+
}
338+
339+
`
340+
if got, want := strings.TrimSpace(str), strings.TrimSpace(desired); got != want {
341+
t.Errorf("Generated code differs from result.\ngot = %s\nwant = %s", got, want)
342+
t.Log(gentesthelper.DiffStrings(got, want))
343+
}
344+
}

gengokit/httptransport/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Binding struct {
2828
Verb string
2929
Fields []*Field
3030
OneofFields []*OneofField
31+
RequestRootField *Field
3132
// A pointer back to the parent method of this binding. Used within some
3233
// binding methods
3334
Parent *Method

gengokit/template/template.go

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

svcdef/consolidate_http.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ func paramLocation(field *Field, binding *svcparse.HTTPBinding) string {
148148
if optField.Value == "*" {
149149
return "body"
150150
} else if optField.Value == field.Name {
151-
return "body"
151+
return "body_root"
152152
// Have to CamelCase the fields from the protobuf file, as they may
153153
// be lowercase while the name from the Go file will be CamelCased.
154154
} else if gogen.CamelCase(strings.Split(optField.Value, ".")[0]) == field.Name {
155-
return "body"
155+
return "body_root"
156156
}
157157
}
158158
}

svcdef/consolidate_http_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ service Map {
124124
Name string
125125
Location string
126126
}{
127-
{"A", "body"},
127+
{"A", "body_root"},
128128
{"AA", "query"},
129129
{"C", "query"},
130130
{"MapField", "query"},

0 commit comments

Comments
 (0)