-
-
Notifications
You must be signed in to change notification settings - Fork 242
Fix wildcard path patterns in OpenAPI spec output for known routers #934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
93f460c
f226a2d
ce19d37
b1c18ac
0e6c1bd
69c6e14
0ca8d47
3393a28
7d8957e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,8 @@ import ( | |||||||||||||||
| "encoding/json" | ||||||||||||||||
| "net/http" | ||||||||||||||||
| "reflect" | ||||||||||||||||
| "regexp" | ||||||||||||||||
| "strings" | ||||||||||||||||
| "time" | ||||||||||||||||
|
|
||||||||||||||||
| "github.com/danielgtaylor/huma/v2/yaml" | ||||||||||||||||
|
|
@@ -1541,7 +1543,7 @@ func (o *OpenAPI) MarshalJSON() ([]byte, error) { | |||||||||||||||
| {"info", o.Info, omitNever}, | ||||||||||||||||
| {"jsonSchemaDialect", o.JSONSchemaDialect, omitEmpty}, | ||||||||||||||||
| {"servers", o.Servers, omitEmpty}, | ||||||||||||||||
| {"paths", o.Paths, omitEmpty}, | ||||||||||||||||
| {"paths", FixWildcardPaths(o.Paths), omitEmpty}, | ||||||||||||||||
| {"webhooks", o.Webhooks, omitEmpty}, | ||||||||||||||||
| {"components", o.Components, omitEmpty}, | ||||||||||||||||
| {"security", o.Security, omitNil}, | ||||||||||||||||
|
|
@@ -1677,3 +1679,77 @@ func (o *OpenAPI) DowngradeYAML() ([]byte, error) { | |||||||||||||||
| } | ||||||||||||||||
| return buf.Bytes(), err | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // Patterns for router-specific wildcards | ||||||||||||||||
| var ( | ||||||||||||||||
| // Matches {name...} (ServeMux) | ||||||||||||||||
| serveMuxWildcard = regexp.MustCompile(`\{([^}]+)\.\.\.}`) | ||||||||||||||||
| // Matches {name:.*} (Gorilla Mux) | ||||||||||||||||
| gorillaMuxWildcard = regexp.MustCompile(`\{([^:}]+):\.\*}`) | ||||||||||||||||
| // Matches *name at end of path (Gin, HttpRouter, BunRouter) | ||||||||||||||||
| starNameWildcard = regexp.MustCompile(`/\*([a-zA-Z_][a-zA-Z0-9_]*)$`) | ||||||||||||||||
| ) | ||||||||||||||||
|
|
||||||||||||||||
| // fixWildcardPath converts router-specific wildcard patterns to OpenAPI-compatible path parameters | ||||||||||||||||
| func fixWildcardPath(path string) string { | ||||||||||||||||
| // ServeMux: {name...} -> {name} | ||||||||||||||||
| if replaced := serveMuxWildcard.ReplaceAllString(path, "{$1}"); replaced != path { | ||||||||||||||||
| return replaced | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // Gorilla Mux: {name:.*} -> {name} | ||||||||||||||||
| if replaced := gorillaMuxWildcard.ReplaceAllString(path, "{$1}"); replaced != path { | ||||||||||||||||
| return replaced | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // Gin, HttpRouter, BunRouter: /*name -> /{name} | ||||||||||||||||
| if replaced := starNameWildcard.ReplaceAllString(path, "/{$1}"); replaced != path { | ||||||||||||||||
| return replaced | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // Chi, Echo, Fiber: trailing /* or /+ -> /{path} | ||||||||||||||||
| if strings.HasSuffix(path, "/*") { | ||||||||||||||||
| return strings.TrimSuffix(path, "/*") + "/{path}" | ||||||||||||||||
| } | ||||||||||||||||
| if strings.HasSuffix(path, "/+") { | ||||||||||||||||
| return strings.TrimSuffix(path, "/+") + "/{path}" | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+1710
to
+1716
|
||||||||||||||||
|
|
||||||||||||||||
| // No match, return original | ||||||||||||||||
| return path | ||||||||||||||||
phrozen marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // FixWildcardPaths returns a copy of the paths map with wildcard patterns normalized for OpenAPI. | ||||||||||||||||
| // | ||||||||||||||||
| // Different routers use different syntax for wildcard/catch-all path parameters | ||||||||||||||||
| // (e.g., {path...}, /*name, /*, /+), but the OpenAPI specification only supports | ||||||||||||||||
| // the standard {paramName} format. This function transforms router-specific | ||||||||||||||||
| // wildcard patterns into OpenAPI-compatible path parameters. | ||||||||||||||||
| // | ||||||||||||||||
| // This transformation is applied during JSON marshaling of the OpenAPI spec, | ||||||||||||||||
| // so the internal Paths map retains the original router-specific patterns for | ||||||||||||||||
| // correct request routing, while the generated OpenAPI document uses standard | ||||||||||||||||
| // path parameter syntax for compatibility with OpenAPI tools and clients. | ||||||||||||||||
| // | ||||||||||||||||
| // The PathItem values are preserved (same pointer references), only the map keys | ||||||||||||||||
| // are transformed. | ||||||||||||||||
|
||||||||||||||||
| // are transformed. | |
| // are transformed. | |
| // | |
| // This helper is primarily used internally by huma during OpenAPI generation | |
| // and by tests that validate path normalization behavior. Typical users of the | |
| // huma package do not need to call FixWildcardPaths directly; instead, it is | |
| // applied automatically when serializing the OpenAPI definition. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs direction, testing on a different package is weird. What would make more sense is to review implementation with the current test, and once validated remove it and un-export the function.
Uh oh!
There was an error while loading. Please reload this page.