Repository: getkin/kin-openapi
Pull request #213 (2020-05-24 22:27:43)

handle digits in $ref.s
Fixes https://github.com/getkin/kin-openapi/issues/212


Patch:
diff --git a/openapi2conv/openapi2_conv.go b/openapi2conv/openapi2_conv.go
index e5db109..6e0b10e 100644
--- a/openapi2conv/openapi2_conv.go
+++ b/openapi2conv/openapi2_conv.go
@@ -11,6 +11,7 @@ import (
 	"github.com/getkin/kin-openapi/openapi3"
 )
 
+// ToV3Swagger converts an OpenAPIv2 spec to an OpenAPIv3 spec
 func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) {
 	result := &openapi3.Swagger{
 		OpenAPI:    "3.0.2",
@@ -396,8 +397,7 @@ func FromV3Swagger(swagger *openapi3.Swagger) (*openapi2.Swagger, error) {
 	}
 	result.Parameters = map[string]*openapi2.Parameter{}
 	for name, param := range swagger.Components.Parameters {
-		result.Parameters[name], err = FromV3Parameter(param)
-		if err != nil {
+		if result.Parameters[name], err = FromV3Parameter(param); err != nil {
 			return nil, err
 		}
 	}
diff --git a/openapi3/swagger_loader.go b/openapi3/swagger_loader.go
index 5db1552..3925e0e 100644
--- a/openapi3/swagger_loader.go
+++ b/openapi3/swagger_loader.go
@@ -10,6 +10,7 @@ import (
 	"net/url"
 	"path"
 	"reflect"
+	"strconv"
 	"strings"
 
 	"github.com/ghodss/yaml"
@@ -285,9 +286,7 @@ func (swaggerLoader *SwaggerLoader) resolveComponent(swagger *Swagger, ref strin
 }
 
 func drillIntoSwaggerField(cursor interface{}, fieldName string) (interface{}, error) {
-	val := reflect.Indirect(reflect.ValueOf(cursor))
-
-	switch val.Kind() {
+	switch val := reflect.Indirect(reflect.ValueOf(cursor)); val.Kind() {
 	case reflect.Map:
 		elementValue := val.MapIndex(reflect.ValueOf(fieldName))
 		if !elementValue.IsValid() {
@@ -295,6 +294,17 @@ func drillIntoSwaggerField(cursor interface{}, fieldName string) (interface{}, e
 		}
 		return elementValue.Interface(), nil
 
+	case reflect.Slice:
+		i, err := strconv.ParseUint(fieldName, 10, 32)
+		if err != nil {
+			return nil, err
+		}
+		index := int(i)
+		if index >= val.Len() {
+			return nil, errors.New("slice index out of bounds")
+		}
+		return val.Index(index).Interface(), nil
+
 	case reflect.Struct:
 		for i := 0; i < val.NumField(); i++ {
 			field := val.Type().Field(i)
@@ -311,8 +321,10 @@ func drillIntoSwaggerField(cursor interface{}, fieldName string) (interface{}, e
 		}
 		// give up
 		return nil, fmt.Errorf("Struct field not found: %v", fieldName)
+
+	default:
+		return nil, errors.New("not a map, slice nor struct")
 	}
-	return nil, errors.New("Not a map or struct")
 }
 
 func (swaggerLoader *SwaggerLoader) resolveRefSwagger(swagger *Swagger, ref string, path *url.URL) (*Swagger, string, *url.URL, error) {


Test patch:
diff --git a/openapi3/swagger_loader_issue212_test.go b/openapi3/swagger_loader_issue212_test.go
new file mode 100644
index 0000000..1999db4
--- /dev/null
+++ b/openapi3/swagger_loader_issue212_test.go
@@ -0,0 +1,95 @@
+package openapi3
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestIssue212(t *testing.T) {
+	spec := `
+openapi: 3.0.1
+info:
+  title: 'test'
+  version: 1.0.0
+servers:
+  - url: /api
+
+paths:
+  /available-products:
+    get:
+      operationId: getAvailableProductCollection
+      responses:
+        "200":
+          description: test
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/AvailableProduct"
+
+components:
+  schemas:
+    AvailableProduct:
+      type: object
+      properties:
+        id:
+          type: string
+        type:
+          type: string
+        name:
+          type: string
+        media:
+          type: object
+          properties:
+            documents:
+              type: array
+              items:
+                allOf:
+                  - $ref: "#/components/schemas/AvailableProduct/properties/previewImage/allOf/0"
+                  - type: object
+                    properties:
+                      uri:
+                        type: string
+                        pattern: ^\/documents\/[0-9a-f]{64}$
+        previewImage:
+          allOf:
+            - type: object
+              required:
+                - id
+                - uri
+              properties:
+                id:
+                  type: string
+                uri:
+                  type: string
+            - type: object
+              properties:
+                uri:
+                  type: string
+                  pattern: ^\/images\/[0-9a-f]{64}$
+`
+
+	loader := NewSwaggerLoader()
+	doc, err := loader.LoadSwaggerFromData([]byte(spec))
+	require.NoError(t, err)
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	expected, err := json.Marshal(&Schema{
+		Type:     "object",
+		Required: []string{"id", "uri"},
+		Properties: map[string]*SchemaRef{
+			"id":  {Value: &Schema{Type: "string"}},
+			"uri": {Value: &Schema{Type: "string"}},
+		},
+	},
+	)
+	require.NoError(t, err)
+	got, err := json.Marshal(doc.Components.Schemas["AvailableProduct"].Value.Properties["media"].Value.Properties["documents"].Value.Items.Value.AllOf[0].Value)
+	require.NoError(t, err)
+
+	require.Equal(t, expected, got)
+}