format api routes

This commit is contained in:
Kyle Mendell
2026-07-12 13:42:05 -05:00
parent 3893376cba
commit 7b5d6f5a9c
22 changed files with 1010 additions and 411 deletions
+38 -1
View File
@@ -132,7 +132,13 @@ func TestCookiesStreamingAndOpenAPI(t *testing.T) {
_, _ = io.Copy(ctx.BodyWriter(), reader)
}}, nil
})
AddRawOperation(api, "test-raw", http.MethodPost, "/api/test-raw", "Raw test", []string{"Test"}, nil)
AddRawOperation(api, huma.Operation{
OperationID: "test-raw",
Method: http.MethodPost,
Path: "/api/test-raw",
Summary: "Raw test",
Tags: []string{"Test"},
})
response := httptest.NewRecorder()
router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/api/test-cookies", nil))
@@ -160,3 +166,34 @@ func TestCookiesStreamingAndOpenAPI(t *testing.T) {
require.Contains(t, response.Header().Get("Content-Security-Policy"), "worker-src blob:")
require.NotContains(t, response.Header().Get("Content-Security-Policy"), "script-src 'unsafe-inline'")
}
func TestRegisterAppliesDecoratorsInOrder(t *testing.T) {
router, api := newTestAPI(t)
var order []string
first := func(operation *huma.Operation) {
operation.Middlewares = append(operation.Middlewares, func(ctx huma.Context, next func(huma.Context)) {
order = append(order, "first")
next(ctx)
})
}
second := func(ctx huma.Context, next func(huma.Context)) {
order = append(order, "second")
next(ctx)
}
Register(api, huma.Operation{
OperationID: "test-decorator-order",
Method: http.MethodGet,
Path: "/api/test-decorator-order",
DefaultStatus: http.StatusNoContent,
}, func(context.Context, *struct{}) (*struct{}, error) {
order = append(order, "handler")
return &struct{}{}, nil
}, first, WithMiddleware(second))
response := httptest.NewRecorder()
router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/test-decorator-order", nil))
require.Equal(t, http.StatusNoContent, response.Code)
require.Equal(t, []string{"first", "second", "handler"}, order)
}
+3 -10
View File
@@ -8,7 +8,7 @@ import (
)
// AddRawOperation documents a Gin endpoint that must retain direct response control
func AddRawOperation(api huma.API, operationID, method, path, summary string, tags []string, security []map[string][]string, statuses ...int) {
func AddRawOperation(api huma.API, operation huma.Operation, statuses ...int) {
if len(statuses) == 0 {
statuses = []int{http.StatusOK}
}
@@ -16,13 +16,6 @@ func AddRawOperation(api huma.API, operationID, method, path, summary string, ta
for _, status := range statuses {
responses[strconv.Itoa(status)] = &huma.Response{Description: http.StatusText(status)}
}
api.OpenAPI().AddOperation(&huma.Operation{
OperationID: operationID,
Method: method,
Path: path,
Summary: summary,
Tags: tags,
Security: security,
Responses: responses,
})
operation.Responses = responses
api.OpenAPI().AddOperation(&operation)
}
+12 -1
View File
@@ -6,8 +6,19 @@ import (
"github.com/danielgtaylor/huma/v2"
)
// WithMiddleware appends operation middleware at the point the decorator is applied
func WithMiddleware(middleware func(huma.Context, func(huma.Context))) func(*huma.Operation) {
return func(operation *huma.Operation) {
operation.Middlewares = append(operation.Middlewares, middleware)
}
}
// Register adds a typed operation while preserving Pocket ID error and body-reading behavior
func Register[I, O any](api huma.API, operation huma.Operation, handler func(context.Context, *I) (*O, error)) {
func Register[I, O any](api huma.API, operation huma.Operation, handler func(context.Context, *I) (*O, error), decorators ...func(*huma.Operation)) {
for _, decorator := range decorators {
decorator(&operation)
}
if operation.MaxBodyBytes == 0 {
operation.MaxBodyBytes = -1
}