-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathregister.go
213 lines (193 loc) · 6.9 KB
/
register.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package hureg
import (
"context"
"net/http"
"slices"
"github.com/danielgtaylor/huma/v2"
"github.com/cardinalby/hureg/pkg/huma/metadata"
"github.com/cardinalby/hureg/pkg/huma/middlewares"
"github.com/cardinalby/hureg/pkg/huma/op_handler"
)
var testHumaRegisterer func(api huma.API, operation huma.Operation, handler any)
// API is wrapper of huma.API that stores RegMiddlewares that should be applied to operations
// before registration in Huma.
type API interface {
GetHumaAPI() huma.API
GetExtraHumaAPIs() []ExtraHumaApiInstance
GetRegMiddlewares() RegMiddlewares
GetBubblingRegMiddlewares() RegMiddlewares
GetTransformers() []huma.Transformer
}
// Register registers a handler for an explicitly provided operation.
// It will apply all RegMiddlewares stored in API to the operation and passed operationHandlers
// after that. Depending on RegMiddlewares, an operation can be modified, registered multiple times with
// different base paths or not registered at all.
// Before passing an operation to the RegMiddlewares, it will be initialized with the metadata keys required
// for the library functionality.
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will be set to `true`
// See other keys in metadata package for details.
func Register[I, O any](
api API,
operation huma.Operation,
handler func(context.Context, *I) (*O, error),
operationHandlers ...func(o *huma.Operation),
) {
registerImpl(api, &operation, true, handler, operationHandlers)
}
// Get is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
func Get[I, O any](
api API,
path string,
handler func(context.Context, *I) (*O, error),
operationHandlers ...func(o *huma.Operation),
) {
convenience(api, http.MethodGet, path, handler, operationHandlers...)
}
// Post is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
//
//goland:noinspection GoUnusedExportedFunction
func Post[I, O any](
api API,
path string,
handler func(context.Context, *I) (*O, error),
operationHandlers ...func(o *huma.Operation),
) {
convenience(api, http.MethodPost, path, handler, operationHandlers...)
}
// Put is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
//
//goland:noinspection GoUnusedExportedFunction
func Put[I, O any](
api API,
path string,
handler func(context.Context, *I) (*O, error),
operationHandlers ...func(o *huma.Operation),
) {
convenience(api, http.MethodPut, path, handler, operationHandlers...)
}
// Patch is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
//
//goland:noinspection GoUnusedExportedFunction
func Patch[I, O any](
api API,
path string,
handler func(context.Context, *I) (*O, error),
operationHandlers ...func(o *huma.Operation),
) {
convenience(api, http.MethodPatch, path, handler, operationHandlers...)
}
// Delete is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
//
//goland:noinspection GoUnusedExportedFunction
func Delete[I, O any](
api API,
path string,
handler func(
context.Context,
*I,
) (*O, error), operationHandlers ...func(o *huma.Operation)) {
convenience(api, http.MethodDelete, path, handler, operationHandlers...)
}
// Options is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
//
//goland:noinspection GoUnusedExportedFunction
func Options[I, O any](
api API,
path string,
handler func(
context.Context,
*I,
) (*O, error), operationHandlers ...func(o *huma.Operation)) {
convenience(api, http.MethodOptions, path, handler, operationHandlers...)
}
// Head is a shortcut for Register method that implicitly generates Operation object
// metadata.KeyIsExplicitOperationID, metadata.KeyIsExplicitSummary keys will not be set.
//
//goland:noinspection GoUnusedExportedFunction
func Head[I, O any](
api API,
path string,
handler func(
context.Context,
*I,
) (*O, error), operationHandlers ...func(o *huma.Operation)) {
convenience(api, http.MethodHead, path, handler, operationHandlers...)
}
func convenience[I, O any](
api API,
method,
path string,
handler func(context.Context, *I) (*O, error),
operationHandlers ...func(o *huma.Operation),
) {
operation := huma.Operation{
Method: method,
Path: path,
}
registerImpl(api, &operation, false, handler, operationHandlers)
}
func registerImpl[I, O any](
api API,
operation *huma.Operation,
isExplicit bool,
handler func(context.Context, *I) (*O, error),
operationHandlers []func(o *huma.Operation),
) {
humaAPI := api.GetHumaAPI()
initOpMetadata[I, O](humaAPI, operation, isExplicit)
regMiddlewares := api.GetRegMiddlewares()
for _, oh := range operationHandlers {
regMiddlewares = append(regMiddlewares, NewRegMiddleware(oh))
}
bubblingRegMiddlewares := api.GetBubblingRegMiddlewares()
for i := len(bubblingRegMiddlewares) - 1; i >= 0; i-- {
regMiddlewares = append(regMiddlewares, bubblingRegMiddlewares[i])
}
regMiddlewares.Handler(func(op huma.Operation) {
// Pass transformers saved in api to the ctx key, that will be later picked up by
// humaApiWrapper.Transform method
if apiTransformers := api.GetTransformers(); len(apiTransformers) > 0 {
op.Middlewares = append(op.Middlewares, middlewares.SetCtxTransformers(apiTransformers))
}
registerWithHuma := func(humaApi huma.API, op huma.Operation) {
if testHumaRegisterer == nil {
huma.Register(humaApi, op, handler)
} else {
testHumaRegisterer(humaApi, op, handler)
}
}
registerWithHuma(humaAPI, op)
for _, extraHumaAPI := range api.GetExtraHumaAPIs() {
extraBubblingRegMiddlewares := slices.Clone(extraHumaAPI.bubblingRegMiddlewares)
slices.Reverse(extraBubblingRegMiddlewares)
extraBubblingRegMiddlewares.Handler(func(op huma.Operation) {
registerWithHuma(extraHumaAPI.humaAPI, op)
})(op)
}
})(*operation)
}
func initOpMetadata[I, O any](humaApi huma.API, op *huma.Operation, isExplicit bool) {
if op.Metadata == nil {
op.Metadata = make(map[string]any)
}
op.Metadata[metadata.KeyOpenApiObj] = humaApi.OpenAPI()
op.Metadata[metadata.KeyInitPath] = op.Path
var input *I
var output *O
op.Metadata[metadata.KeyInputObjPtr] = input
op.Metadata[metadata.KeyOutputObjPtr] = output
if isExplicit {
op.Metadata[metadata.KeyIsExplicitOperationID] = true
op.Metadata[metadata.KeyIsExplicitSummary] = true
} else {
op_handler.GenerateOperationID(true)(op)
op_handler.GenerateSummary(true)(op)
}
op.Metadata[metadata.KeyInitOperationID] = op.OperationID
}