Skip to content

Commit

Permalink
Merge pull request #1099 from tdakkota/fix/multipart-complex-fields
Browse files Browse the repository at this point in the history
fix(gen): handle multipart complex fields according to spec
  • Loading branch information
ernado authored Dec 2, 2023
2 parents e3e1c77 + 4ce928c commit b8073f1
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 272 deletions.
1 change: 1 addition & 0 deletions _testdata/positive/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"multipart/form-data": {
"encoding": {
"deepObject": {
"contentType": "application/x-www-form-urlencoded",
"style": "deepObject"
}
},
Expand Down
16 changes: 15 additions & 1 deletion gen/_template/request_decode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,21 @@ func (s *{{ if $op.WebhookInfo }}Webhook{{ end }}Server) decode{{ $op.Name }}Req
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
{{- template "uri/decode" $el }}
{{- if $p.Spec.Content }}
val, err := d.DecodeValue()
if err != nil {
return err
}
if err := func(d *jx.Decoder) error {
{{- template "json/dec" $el }}
return nil
}(jx.DecodeStr(val)); err != nil {
return err
}
return nil
{{- else }}
{{- template "uri/decode" $el }}
{{- end }}
}); err != nil {
return req, close, errors.Wrap(err, {{ printf "decode %q" $p.Spec.Name | quote }})
}
Expand Down
11 changes: 10 additions & 1 deletion gen/_template/request_encode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,16 @@ func encode{{ $op.Name }}Request(
Explode: {{ if $param.Spec.Explode }}true{{ else }}false{{ end }},
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
{{- template "uri/encode" elem $param.Type (printf "request.%s" $param.Name) }}
{{- $el := elem $param.Type (printf "request.%s" $param.Name) }}
{{- if $param.Spec.Content }}
var enc jx.Encoder
func(e *jx.Encoder) {
{{- template "json/enc" $el }}
}(&enc)
return e.EncodeValue(string(enc.Bytes()))
{{- else }}
{{- template "uri/encode" $el }}
{{- end }}
}); err != nil {
return errors.Wrap(err, "encode query")
}
Expand Down
63 changes: 53 additions & 10 deletions gen/gen_contents.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ func (g *Generator) generateFormContent(
return nil, &ErrNotImplemented{"complex form schema"}
}

getEncoding := func(f *ir.Field) (ct ir.Encoding) {
if e, ok := media.Encoding[f.Tag.JSON]; ok {
ct = ir.Encoding(e.ContentType)
}
if ct == "" && encoding.MultipartForm() && isComplexMultipartType(f.Spec.Schema) {
ct = ir.EncodingJSON
}
return ct
}

var override generateSchemaOverride
switch encoding {
case ir.EncodingFormURLEncoded:
Expand Down Expand Up @@ -140,7 +150,17 @@ func (g *Generator) generateFormContent(
t.AddFeature("multipart-file")
return nil
}
f.Type.AddFeature("uri")
switch ct := getEncoding(f); ct {
case "", ir.EncodingFormURLEncoded:
f.Type.AddFeature("uri")
case ir.EncodingJSON:
f.Type.AddFeature("json")
default:
return errors.Wrapf(
&ErrNotImplemented{"form content encoding"},
"%q", ct,
)
}
return nil
}
}
Expand Down Expand Up @@ -188,17 +208,25 @@ func (g *Generator) generateFormContent(
if e, ok := media.Encoding[tag]; ok {
spec.Style = e.Style
spec.Explode = e.Explode
if e.ContentType != "" {
return &ErrNotImplemented{"parameter content-type"}
}
}

if err := isSupportedParamStyle(spec); err != nil {
return err
}
switch ct := getEncoding(f); ct {
case "", ir.EncodingFormURLEncoded:
if err := isSupportedParamStyle(spec); err != nil {
return err
}

if err := isParamAllowed(f.Type, true, map[*ir.Type]struct{}{}); err != nil {
return err
if err := isParamAllowed(f.Type, true, map[*ir.Type]struct{}{}); err != nil {
return err
}
case ir.EncodingJSON:
spec.Content = &openapi.ParameterContent{
Name: ct.String(),
}
default:
return errors.Wrapf(
&ErrNotImplemented{"form content encoding"},
"%q", ct,
)
}

return nil
Expand All @@ -211,6 +239,21 @@ func (g *Generator) generateFormContent(
return t, nil
}

func isComplexMultipartType(s *jsonschema.Schema) bool {
if s == nil {
return true
}

switch s.Type {
case jsonschema.Object, jsonschema.Empty:
return true
case jsonschema.Array:
return len(s.Items) > 0 || isComplexMultipartType(s.Item)
default:
return false
}
}

func (g *Generator) generateContents(
ctx *genctx,
name string,
Expand Down
14 changes: 9 additions & 5 deletions internal/integration/form_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ func testFormMultipart() *api.TestFormMultipart {
}
}

func checkTestFormValues(a *assert.Assertions, form url.Values) {
func checkTestFormValues(a *assert.Assertions, form url.Values, multipartForm bool) {
a.Equal("10", form.Get("id"))
a.Equal("00000000-0000-0000-0000-000000000000", form.Get("uuid"))
a.Equal("foobar", form.Get("description"))
a.Equal([]string{"foo", "bar"}, form["array"])
a.Equal("10", form.Get("min"))
a.Equal("10", form.Get("max"))
if multipartForm {
a.JSONEq(`{"min":10,"max":10}`, form.Get("object"))
} else {
a.Equal("10", form.Get("min"))
a.Equal("10", form.Get("max"))
}
a.Equal("10", form.Get("deepObject[min]"))
a.Equal("10", form.Get("deepObject[max]"))
}
Expand Down Expand Up @@ -196,7 +200,7 @@ func TestURIEncodingE2E(t *testing.T) {

s := tt.serverSetup(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
a.NoError(req.ParseForm())
checkTestFormValues(a, req.PostForm)
checkTestFormValues(a, req.PostForm, false)
apiServer.ServeHTTP(w, req)
}))
defer s.Close()
Expand Down Expand Up @@ -224,7 +228,7 @@ func TestMultipartEncodingE2E(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
a.NoError(req.ParseMultipartForm(32 << 20))
form := url.Values(req.MultipartForm.Value)
checkTestFormValues(a, form)
checkTestFormValues(a, form, true)
apiServer.ServeHTTP(w, req)
}))
defer s.Close()
Expand Down
30 changes: 26 additions & 4 deletions internal/integration/test_allof/oas_request_decoders_gen.go

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

12 changes: 10 additions & 2 deletions internal/integration/test_allof/oas_request_encoders_gen.go

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

122 changes: 0 additions & 122 deletions internal/integration/test_allof/oas_uri_gen.go

This file was deleted.

Loading

0 comments on commit b8073f1

Please sign in to comment.