diff --git a/examples/petstore/main.go b/examples/petstore/main.go index a93c697..357aaa6 100644 --- a/examples/petstore/main.go +++ b/examples/petstore/main.go @@ -28,10 +28,13 @@ func main() { option.Request(new(Pet)), option.Response(201, new(Pet)), ) - pet.Get("/findByStatus", + pet.Get( + "/findByStatus", option.OperationID("findPetsByStatus"), option.Summary("Find pets by status"), - option.Description("Finds Pets by status. Multiple status values can be provided with comma separated strings."), + option.Description( + "Finds Pets by status. Multiple status values can be provided with comma separated strings.", + ), option.Request(new(struct { Status string `query:"status" enum:"available,pending,sold"` // Enum values for pet status })), diff --git a/internal/builder/builder.go b/internal/builder/builder.go index ac91107..8de180c 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -171,7 +171,7 @@ func (b *Builder) ensurePathParameters(target string, op *openapi.Operation) { return } for _, m := range matches { - name := m[1] + name, _, _ := strings.Cut(m[1], ":") if _, ok := existing[name]; ok { continue } diff --git a/internal/validate/path.go b/internal/validate/path.go index 0edf144..d1f2817 100644 --- a/internal/validate/path.go +++ b/internal/validate/path.go @@ -81,7 +81,8 @@ func ValidatePathParams(path, method string, params []*openapi.Parameter) []erro matches := pathParamRe.FindAllStringSubmatch(path, -1) templateNames := map[string]struct{}{} for _, match := range matches { - templateNames[match[1]] = struct{}{} + name, _, _ := strings.Cut(match[1], ":") + templateNames[name] = struct{}{} } declared := map[string]bool{} for _, p := range params { @@ -104,7 +105,7 @@ func ValidatePathParams(path, method string, params []*openapi.Parameter) []erro } } for _, match := range matches { - name := match[1] + name, _, _ := strings.Cut(match[1], ":") if required, ok := declared[name]; !ok { errs = append(errs, Errorf("%s %s missing path parameter %q", strings.ToUpper(method), path, name)) } else if !required { diff --git a/internal/validate/utils.go b/internal/validate/utils.go index ee1aa5a..d68e038 100644 --- a/internal/validate/utils.go +++ b/internal/validate/utils.go @@ -14,7 +14,7 @@ import ( ) var ( - pathParamRe = regexp.MustCompile(`\{([^{}]+)\}`) + pathParamRe = regexp.MustCompile(`\{((?:\\.|[^{}\\]|\{(?:\\.|[^{}\\])*\})*)\}`) componentRe = regexp.MustCompile(`^[a-zA-Z0-9.\-_]+$`) responseCodeRe = regexp.MustCompile(`^[1-5]([0-9]{2}|XX)$`) ) diff --git a/internal/validate/utils_test.go b/internal/validate/utils_test.go index 24e3458..d0bbc37 100644 --- a/internal/validate/utils_test.go +++ b/internal/validate/utils_test.go @@ -14,6 +14,15 @@ import ( func TestNormalizeTemplatedPath(t *testing.T) { assert.Equal(t, "/users/{}", validate.NormalizeTemplatedPath("/users/{id}")) assert.Equal(t, "/orgs/{}/repos/{}", validate.NormalizeTemplatedPath("/orgs/{org}/repos/{repo}")) + assert.Equal(t, "/type/{}", validate.NormalizeTemplatedPath("/type/{type:foo|bar}")) + assert.Equal(t, "/more/{}/{}", validate.NormalizeTemplatedPath("/more/{param}/{type:foo|bar}")) + assert.Equal(t, "/complex/quantifier/{}", validate.NormalizeTemplatedPath("/complex/quantifier/{t:(\\d{4})}")) + assert.Equal( + t, + "/foo/bar/{}/two/{}/8/nine/{}", + validate.NormalizeTemplatedPath("/foo/bar/{one}/two/{three:(four|five){6,7}(eight|nine)}/8/nine/{wtf}"), + ) + assert.Equal(t, "/curly/brace/{}/{}", validate.NormalizeTemplatedPath(`/curly/brace/{one:\{}/{two:\}\}}`)) assert.Equal(t, "/static", validate.NormalizeTemplatedPath("/static")) }