-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathform.go
135 lines (120 loc) · 3.65 KB
/
form.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package chimera
import (
"context"
"net/http"
"reflect"
"strings"
"github.com/go-playground/form/v4"
"github.com/invopop/jsonschema"
)
var (
formBodyDecoder = form.NewDecoder()
)
// FormRequest[Body, Params any] is a request type that decodes request bodies to a
// user-defined struct for the Body and Params
type FormRequest[Body, Params any] struct {
request *http.Request
Body Body
Params Params
}
// Context returns the context that was part of the original http.Request
func (r *FormRequest[Body, Params]) Context() context.Context {
if r.request != nil {
return r.request.Context()
}
return nil
}
// ReadRequest reads the body of an http request and assigns it to the Body field using
// http.Request.ParseForm and the "go-playground/form" package.
// This function also reads the parameters using UnmarshalParams and assigns it to the Params field.
// NOTE: the body of the request is closed after this function is run.
func (r *FormRequest[Body, Params]) ReadRequest(req *http.Request) error {
defer req.Body.Close()
err := req.ParseForm()
if err != nil {
return err
}
r.Body = *new(Body)
if _, ok := any(r.Body).(Nil); !ok {
err = formBodyDecoder.Decode(&r.Body, req.PostForm)
if err != nil {
return err
}
}
r.Params = *new(Params)
if _, ok := any(r.Params).(Nil); !ok {
err = UnmarshalParams(req, &r.Params)
if err != nil {
return err
}
}
r.request = req
return nil
}
// flattenFormSchemas is kind of a jank way to convert jsonschema.Schema objects to be of a "form"
// style using patternProperties to represent arrays/object paths
func flattenFormSchemas(schema *jsonschema.Schema, properties map[string]*jsonschema.Schema, refs jsonschema.Definitions, prefix string) {
if schema.Ref != "" && len(refs) > 0 {
name := strings.Split(schema.Ref, "/")
schema = refs[name[len(name)-1]]
if schema != nil {
delete(refs, name[len(name)-1])
} else {
properties["^"+prefix+".*$"] = &jsonschema.Schema{}
return
}
}
switch schema.Type {
case "object":
if prefix != "" {
prefix += "."
}
for p := schema.Properties.Oldest(); p != nil; p = p.Next() {
flattenFormSchemas(p.Value, properties, refs, prefix+p.Key)
}
case "array":
flattenFormSchemas(schema.Items, properties, refs, prefix+"\\[\\d+\\]")
default:
properties["^"+prefix+"$"] = schema
}
}
// OpenAPIRequestSpec returns the Request definition of a FormRequest
// It attempts to utilize patternProperties to try to define the body schema
// i.e. objects/arrays use dotted/bracketed paths X.Y.Z[i]
func (r *FormRequest[Body, Params]) OpenAPIRequestSpec() RequestSpec {
bType := reflect.TypeOf(new(Body))
for ; bType.Kind() == reflect.Pointer; bType = bType.Elem() {
}
schema := RequestSpec{}
if bType != reflect.TypeOf(Nil{}) {
s := (&jsonschema.Reflector{FieldNameTag: "form"}).Reflect(new(Body))
// s.ID = jsonschema.ID(bType.PkgPath() + "_" + bType.Name())
if s.PatternProperties == nil {
s.PatternProperties = make(map[string]*jsonschema.Schema)
}
sType := s.Type
if s.Ref != "" && len(s.Definitions) > 0 {
name := strings.Split(s.Ref, "/")
sType = s.Definitions[name[len(name)-1]].Type
}
flattenFormSchemas(s, s.PatternProperties, s.Definitions, "")
s.Type = sType
s.Ref = ""
schema.RequestBody = &RequestBody{
Content: map[string]MediaType{
"application/x-www-form-urlencoded ": {
Schema: s,
},
},
Required: reflect.TypeOf(*new(Body)).Kind() != reflect.Pointer,
}
}
pType := reflect.TypeOf(new(Params))
for ; pType.Kind() == reflect.Pointer; pType = pType.Elem() {
}
if pType != reflect.TypeOf(Nil{}) {
schema.Parameters = CacheRequestParamsType(pType)
}
// flatten form schemas
return schema
}