-
Notifications
You must be signed in to change notification settings - Fork 0
/
api_gen.go
170 lines (149 loc) · 7.41 KB
/
api_gen.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
package hureg
import (
"github.com/danielgtaylor/huma/v2"
"github.com/cardinalby/hureg/pkg/huma/humaapi"
"github.com/cardinalby/hureg/pkg/huma/op_handler"
)
type ExtraHumaApiInstance struct {
humaAPI huma.API
bubblingRegMiddlewares RegMiddlewares
}
// APIGen is a core type of the library that wraps huma.API and stores RegMiddlewares that should be
// applied to operations before registration in Huma.
// It provides a fluent API to create derived APIGen instances with own set of actions/changes to an
// operation before its registration.
type APIGen struct {
humaAPIWrapper humaApiWrapper
regMiddlewares RegMiddlewares
bubblingRegMiddlewares RegMiddlewares
transformers []huma.Transformer
extraHumaAPIs []ExtraHumaApiInstance
}
// NewAPIGen creates a new APIGen instance with the given huma.API.
func NewAPIGen(humaApi huma.API) APIGen {
return APIGen{
humaAPIWrapper: newHumaApiWrapper(humaApi),
}
}
// ReplaceHumaAPI returns a new APIGen instance with replaced huma.API pointer.
// Can be useful for some tricky cases when you create huma.API instance based on another adapter but with
// the same config (and OpenAPI object pointer) to utilize router-specific middlewares for a group.
func (a APIGen) ReplaceHumaAPI(humaApi huma.API) APIGen {
a.humaAPIWrapper = newHumaApiWrapper(humaApi)
return a
}
// GetHumaAPI returns the wrapped huma.API.
func (a APIGen) GetHumaAPI() huma.API {
return a.humaAPIWrapper
}
// AddRegMiddleware returns a new APIGen instance with the given RegMiddlewares added to the stored RegMiddlewares.
func (a APIGen) AddRegMiddleware(regMiddlewares ...RegMiddleware) APIGen {
a.regMiddlewares = append(a.regMiddlewares, regMiddlewares...)
return a
}
// GetRegMiddlewares returns the stored RegMiddlewares.
func (a APIGen) GetRegMiddlewares() RegMiddlewares {
return a.regMiddlewares
}
// AddBubblingRegMiddleware returns a new APIGen instance with the given RegMiddlewares added to the
// stored bubbling RegMiddlewares. Bubbling RegMiddlewares will be applied to the operation in the opposite order
// after normal RegMiddlewares are done allowing you to observe changes made by all previous RegMiddlewares.
func (a APIGen) AddBubblingRegMiddleware(regMiddlewares ...RegMiddleware) APIGen {
a.bubblingRegMiddlewares = append(a.bubblingRegMiddlewares, regMiddlewares...)
return a
}
// GetBubblingRegMiddlewares returns the stored bubbling RegMiddlewares.
func (a APIGen) GetBubblingRegMiddlewares() RegMiddlewares {
return a.bubblingRegMiddlewares
}
// AddOpHandler returns a new APIGen instance with the given OperationHandlers added to it.
func (a APIGen) AddOpHandler(handlers ...op_handler.OperationHandler) APIGen {
for _, opHandler := range handlers {
a.regMiddlewares = append(a.regMiddlewares, NewRegMiddleware(opHandler))
}
return a
}
// AddTransformers returns a new APIGen instance with the given transformers that will be applied
// to the responses of the handlers registered by this APIGen.
func (a APIGen) AddTransformers(transformers ...huma.Transformer) APIGen {
a.transformers = append(a.transformers, transformers...)
return a
}
// GetTransformers returns the stored transformers that will be applied to the responses of the handlers
// registered by this APIGen.
func (a APIGen) GetTransformers() []huma.Transformer {
return a.transformers
}
// AddBasePath returns a new APIGen instance that will add the given basePath segment to an operation`s Path.
// It will append `basePath` to the previously added base paths if any.
// Adding a base path also re-generates the OperationID and Summary fields of an operation if it wasn't explicitly set.
// It's an alternative to Route, Group methods of go routes.
func (a APIGen) AddBasePath(basePath string) APIGen {
return a.AddOpHandler(
op_handler.AddBasePath(basePath),
op_handler.UpdateOperationID(func(op *huma.Operation) string {
// Unlike fan-out, adding base path doesn't create multiple operation registrations
// and doesn't require mandatory operation ID update if it's explicitly provided,
// but generated operation IDs will be updated
return op.OperationID
}),
op_handler.UpdateGeneratedSummary,
)
}
// AddMultiBasePaths returns a new APIGen instance that will register the same operation with multiple base paths.
// It respects base paths added before and after this method call.
// Since it leads to multiple registrations of the same operation, it requires OperationID to be updated to
// avoid registration of multiple operations with the same OperationID.
// If an operation has generated OperationID, it will be re-generated by default Huma operation ID builder.
// The same is true for the Summary field.
// For the case of explicitly set OperationID, you can provide a custom `explicitOpIDBuilder` builder.
// It will receive an operation with modified Path and metadata.KeyBasePath and should return a new OperationID.
// If `explicitOpIDBuilder` is nil, the built-in approach will be used. It will take metadata.KeyBasePath as
// a prefix for the OperationID, turning it into kebab-case and will append it to the metadata.KeyInitOperationID
// (that stores the initial user-provided OperationID).
func (a APIGen) AddMultiBasePaths(
explicitOpIDBuilder func(*huma.Operation) string,
basePaths ...string,
) APIGen {
basePathsRegMiddlewares := make(RegMiddlewares, len(basePaths))
for i, basePath := range basePaths {
basePathsRegMiddlewares[i] = NewRegMiddleware(
op_handler.AddBasePath(basePath),
)
}
return a.
AddRegMiddleware(basePathsRegMiddlewares.FanOut()).
AddOpHandler(
op_handler.UpdateOperationID(explicitOpIDBuilder),
op_handler.UpdateGeneratedSummary,
)
}
// AddMiddlewares returns a new APIGen instance that will add the given middlewares to the operation.
func (a APIGen) AddMiddlewares(middlewares ...func(huma.Context, func(huma.Context))) APIGen {
return a.AddOpHandler(op_handler.AddMiddlewares(middlewares...))
}
// AddExtraHumaAPI returns a new APIGen instance with the given huma.API added to the extraHumaAPIs.
// All operations registered with this APIGen instance or derived instances will be passed to the given `api`
// additionally to the main huma.API. Optional bubbling RegMiddlewares will be applied to the operations before
// the registration in the `api`.
func (a APIGen) AddExtraHumaAPI(api huma.API, bubblingRegMiddlewares ...RegMiddleware) APIGen {
a.extraHumaAPIs = append(a.extraHumaAPIs, ExtraHumaApiInstance{
humaAPI: api,
bubblingRegMiddlewares: bubblingRegMiddlewares,
})
return a
}
// GetExtraHumaAPIs returns the stored extraHumaAPIs.
func (a APIGen) GetExtraHumaAPIs() []ExtraHumaApiInstance {
return a.extraHumaAPIs
}
// AddOwnOpenAPI is a shortcut function to create a new APIGen instance and a OpenAPI object instance together.
// All operations registered with this APIGen instance or derived instances will be added to the OpenAPI object.
// `bubbleRegMiddlewares` will be additionally applied to the operations before the registration in the OpenAPI object.
// It allows you to have separate OpenAPI spec that contains only operations registered with this APIGen instance
// or derived instances.
// Use it in combination with `pkg/huma/oapi_handlers` package to serve the created OpenAPI spec.
func (a APIGen) AddOwnOpenAPI(apiConfig huma.Config, bubblingRegMiddlewares ...RegMiddleware) (APIGen, *huma.OpenAPI) {
humaApi := humaapi.NewDummyHumaAPI(apiConfig)
return a.AddExtraHumaAPI(humaApi, bubblingRegMiddlewares...), humaApi.OpenAPI()
}