Skip to content

Commit

Permalink
reproduce and fix issue getkin#382
Browse files Browse the repository at this point in the history
Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com>
  • Loading branch information
fenollp committed Jul 23, 2021
1 parent 6b4444b commit 25120a3
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 20 deletions.
15 changes: 15 additions & 0 deletions openapi3/issue382_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package openapi3

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestOverridingGlobalParametersValidation(t *testing.T) {
loader := NewLoader()
doc, err := loader.LoadFromFile("testdata/Test_param_override.yml")
require.NoError(t, err)
err = doc.Validate(loader.Context)
require.NoError(t, err)
}
60 changes: 40 additions & 20 deletions openapi3/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,43 @@ func (value Paths) Validate(ctx context.Context) error {
pathItem = value[path]
}

normalizedPath, pathParamsCount := normalizeTemplatedPath(path)
normalizedPath, _, varsInPath := normalizeTemplatedPath(path)
if oldPath, ok := normalizedPaths[normalizedPath]; ok {
return fmt.Errorf("conflicting paths %q and %q", path, oldPath)
}
normalizedPaths[path] = path

var globalCount uint
var commonParams []string
for _, parameterRef := range pathItem.Parameters {
if parameterRef != nil {
if parameter := parameterRef.Value; parameter != nil && parameter.In == ParameterInPath {
globalCount++
commonParams = append(commonParams, parameter.Name)
}
}
}
for method, operation := range pathItem.Operations() {
var count uint
var setParams []string
for _, parameterRef := range operation.Parameters {
if parameterRef != nil {
if parameter := parameterRef.Value; parameter != nil && parameter.In == ParameterInPath {
count++
setParams = append(setParams, parameter.Name)
}
}
}
if count+globalCount != pathParamsCount {
return fmt.Errorf("operation %s %s must define exactly all path parameters", method, path)
if expected := len(setParams) + len(commonParams); expected != len(varsInPath) {
expected -= len(varsInPath)
if expected < 0 {
expected *= -1
}
missing := make(map[string]struct{}, expected)
for _, name := range append(setParams, commonParams...) {
if _, ok := varsInPath[name]; !ok {
missing[name] = struct{}{}
}
}
if len(missing) != 0 {
return fmt.Errorf("operation %s %s must define exactly all path parameters (missing: %v)", method, path, missing)
}
}
}

Expand Down Expand Up @@ -75,53 +87,61 @@ func (paths Paths) Find(key string) *PathItem {
return pathItem
}

normalizedPath, expected := normalizeTemplatedPath(key)
normalizedPath, expected, _ := normalizeTemplatedPath(key)
for path, pathItem := range paths {
pathNormalized, got := normalizeTemplatedPath(path)
pathNormalized, got, _ := normalizeTemplatedPath(path)
if got == expected && pathNormalized == normalizedPath {
return pathItem
}
}
return nil
}

func normalizeTemplatedPath(path string) (string, uint) {
func normalizeTemplatedPath(path string) (string, uint, map[string]struct{}) {
if strings.IndexByte(path, '{') < 0 {
return path, 0
return path, 0, nil
}

var buf strings.Builder
buf.Grow(len(path))
var buffTpl strings.Builder
buffTpl.Grow(len(path))

var (
cc rune
count uint
isVariable bool
vars = make(map[string]struct{})
buffVar strings.Builder
)
for i, c := range path {
if isVariable {
if c == '}' {
// End path variables
// End path variable
isVariable = false

vars[buffVar.String()] = struct{}{}
buffVar = strings.Builder{}

// First append possible '*' before this character
// The character '}' will be appended
if i > 0 && cc == '*' {
buf.WriteRune(cc)
buffTpl.WriteRune(cc)
}
isVariable = false
} else {
// Skip this character
buffVar.WriteRune(c)
continue
}

} else if c == '{' {
// Begin path variable
// The character '{' will be appended
isVariable = true

// The character '{' will be appended
count++
}

// Append the character
buf.WriteRune(c)
buffTpl.WriteRune(c)
cc = c
}
return buf.String(), count
return buffTpl.String(), count, vars
}
40 changes: 40 additions & 0 deletions openapi3/testdata/Test_param_override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
openapi: 3.0.0
info:
title: customer
version: '1.0'
servers:
- url: 'httpbin.kwaf-demo.test'
paths:
'/customers/{customer_id}':
parameters:
- schema:
type: integer
name: customer_id
in: path
required: true
get:
parameters:
- schema:
type: integer
maximum: 100
name: customer_id
in: path
required: true
summary: customer
tags: []
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
customer_name:
type: string
operationId: get-customers-customer_id
description: Retrieve a specific customer by ID
components:
schemas: {}

0 comments on commit 25120a3

Please sign in to comment.