Repository: deepmap/oapi-codegen
Pull request #394 (2021-06-30 00:34:52)

fix(codegen): RefPathToGoType depth check
Fix the depth check in RefPathToGoType which was incorrectly resulting in an error when processing URL or Remote references.

Also leverage sub tests to add more tests to TestRefPathToGoType to cover all the different types of expected refs.


Patch:
diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go
index a41b554..7b7ba1c 100644
--- a/pkg/codegen/template_helpers.go
+++ b/pkg/codegen/template_helpers.go
@@ -95,8 +95,6 @@ func genResponsePayload(operationID string) string {
 
 // genResponseUnmarshal generates unmarshaling steps for structured response payloads
 func genResponseUnmarshal(op *OperationDefinition) string {
-	var buffer = bytes.NewBufferString("")
-
 	var handledCaseClauses = make(map[string]string)
 	var unhandledCaseClauses = make(map[string]string)
 
@@ -106,7 +104,13 @@ func genResponseUnmarshal(op *OperationDefinition) string {
 		panic(err)
 	}
 
+	if len(typeDefinitions) == 0 {
+		// No types.
+		return ""
+	}
+
 	// Add a case for each possible response:
+	buffer := new(bytes.Buffer)
 	responses := op.Spec.Responses
 	for _, typeDefinition := range typeDefinitions {
 
@@ -198,6 +202,11 @@ func genResponseUnmarshal(op *OperationDefinition) string {
 		}
 	}
 
+	if len(handledCaseClauses)+len(unhandledCaseClauses) == 0 {
+		// switch would be empty.
+		return ""
+	}
+
 	// Now build the switch statement in order of most-to-least specific:
 	// See: https://github.com/deepmap/oapi-codegen/issues/127 for why we handle this in two separate
 	// groups.
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index d5169d6..a798f97 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -210,15 +210,25 @@ func StringInArray(str string, array []string) bool {
 // #/components/parameters/Bar -> Bar
 // #/components/responses/Baz -> Baz
 // Remote components (document.json#/Foo) are supported if they present in --import-mapping
-// URL components (http://deepmap.com/schemas/document.json#Foo) are supported if they present in --import-mapping
-//
+// URL components (http://deepmap.com/schemas/document.json#/Foo) are supported if they present in --import-mapping
+// Remote and URL also support standard local paths even though the spec doesn't mention them.
 func RefPathToGoType(refPath string) (string, error) {
+	return refPathToGoType(refPath, true)
+}
+
+// refPathToGoType returns the Go typename for refPath given its
+func refPathToGoType(refPath string, local bool) (string, error) {
 	if refPath[0] == '#' {
 		pathParts := strings.Split(refPath, "/")
-		if depth := len(pathParts); depth != 4 {
-			return "", fmt.Errorf("Parameter nesting is deeper than supported: %s has %d", refPath, depth)
+		depth := len(pathParts)
+		if local {
+			if depth != 4 {
+				return "", fmt.Errorf("unexpected reference depth: %d for ref: %s local: %t", depth, refPath, local)
+			}
+		} else if depth != 4 && depth != 2 {
+			return "", fmt.Errorf("unexpected reference depth: %d for ref: %s local: %t", depth, refPath, local)
 		}
-		return SchemaNameToTypeName(pathParts[3]), nil
+		return SchemaNameToTypeName(pathParts[len(pathParts)-1]), nil
 	}
 	pathParts := strings.Split(refPath, "#")
 	if len(pathParts) != 2 {
@@ -228,7 +238,7 @@ func RefPathToGoType(refPath string) (string, error) {
 	if goImport, ok := importMapping[remoteComponent]; !ok {
 		return "", fmt.Errorf("unrecognized external reference '%s'; please provide the known import for this reference using option --import-mapping", remoteComponent)
 	} else {
-		goType, err := RefPathToGoType("#" + flatComponent)
+		goType, err := refPathToGoType("#"+flatComponent, false)
 		if err != nil {
 			return "", err
 		}
diff --git a/pkg/runtime/bindparam.go b/pkg/runtime/bindparam.go
index f988e7b..751cc7d 100644
--- a/pkg/runtime/bindparam.go
+++ b/pkg/runtime/bindparam.go
@@ -14,6 +14,7 @@
 package runtime
 
 import (
+	"encoding"
 	"encoding/json"
 	"fmt"
 	"net/url"
@@ -65,6 +66,15 @@ func BindStyledParameterWithLocation(style string, explode bool, paramName strin
 		// Headers and cookies aren't escaped.
 	}
 
+	// If the destination implements encoding.TextUnmarshaler we use it for binding
+	if tu, ok := dest.(encoding.TextUnmarshaler); ok {
+		if err := tu.UnmarshalText([]byte(value)); err != nil {
+			return fmt.Errorf("error unmarshaling '%s' text as %T: %s", value, dest, err)
+		}
+
+		return nil
+	}
+
 	// Everything comes in by pointer, dereference it
 	v := reflect.Indirect(reflect.ValueOf(dest))
 


Test patch:
diff --git a/internal/test/client/client.gen.go b/internal/test/client/client.gen.go
index 8cb1f2b..629d3a3 100644
--- a/internal/test/client/client.gen.go
+++ b/internal/test/client/client.gen.go
@@ -775,9 +775,6 @@ func ParsePostBothResponse(rsp *http.Response) (*PostBothResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -794,9 +791,6 @@ func ParseGetBothResponse(rsp *http.Response) (*GetBothResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -813,9 +807,6 @@ func ParsePostJsonResponse(rsp *http.Response) (*PostJsonResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -832,9 +823,6 @@ func ParseGetJsonResponse(rsp *http.Response) (*GetJsonResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -851,9 +839,6 @@ func ParsePostOtherResponse(rsp *http.Response) (*PostOtherResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -870,9 +855,6 @@ func ParseGetOtherResponse(rsp *http.Response) (*GetOtherResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -889,9 +871,6 @@ func ParseGetJsonWithTrailingSlashResponse(rsp *http.Response) (*GetJsonWithTrai
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go
index 3aa31bb..f69e092 100644
--- a/internal/test/components/components.gen.go
+++ b/internal/test/components/components.gen.go
@@ -1255,9 +1255,6 @@ func ParseParamsWithAddPropsResponse(rsp *http.Response) (*ParamsWithAddPropsRes
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -1274,9 +1271,6 @@ func ParseBodyWithAddPropsResponse(rsp *http.Response) (*BodyWithAddPropsRespons
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
diff --git a/internal/test/parameters/parameters.gen.go b/internal/test/parameters/parameters.gen.go
index 896539c..7f0c64c 100644
--- a/internal/test/parameters/parameters.gen.go
+++ b/internal/test/parameters/parameters.gen.go
@@ -2107,9 +2107,6 @@ func ParseGetContentObjectResponse(rsp *http.Response) (*GetContentObjectRespons
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2126,9 +2123,6 @@ func ParseGetCookieResponse(rsp *http.Response) (*GetCookieResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2145,9 +2139,6 @@ func ParseGetHeaderResponse(rsp *http.Response) (*GetHeaderResponse, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2164,9 +2155,6 @@ func ParseGetLabelExplodeArrayResponse(rsp *http.Response) (*GetLabelExplodeArra
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2183,9 +2171,6 @@ func ParseGetLabelExplodeObjectResponse(rsp *http.Response) (*GetLabelExplodeObj
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2202,9 +2187,6 @@ func ParseGetLabelNoExplodeArrayResponse(rsp *http.Response) (*GetLabelNoExplode
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2221,9 +2203,6 @@ func ParseGetLabelNoExplodeObjectResponse(rsp *http.Response) (*GetLabelNoExplod
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2240,9 +2219,6 @@ func ParseGetMatrixExplodeArrayResponse(rsp *http.Response) (*GetMatrixExplodeAr
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2259,9 +2235,6 @@ func ParseGetMatrixExplodeObjectResponse(rsp *http.Response) (*GetMatrixExplodeO
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2278,9 +2251,6 @@ func ParseGetMatrixNoExplodeArrayResponse(rsp *http.Response) (*GetMatrixNoExplo
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2297,9 +2267,6 @@ func ParseGetMatrixNoExplodeObjectResponse(rsp *http.Response) (*GetMatrixNoExpl
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2316,9 +2283,6 @@ func ParseGetPassThroughResponse(rsp *http.Response) (*GetPassThroughResponse, e
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2335,9 +2299,6 @@ func ParseGetDeepObjectResponse(rsp *http.Response) (*GetDeepObjectResponse, err
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2354,9 +2315,6 @@ func ParseGetQueryFormResponse(rsp *http.Response) (*GetQueryFormResponse, error
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2373,9 +2331,6 @@ func ParseGetSimpleExplodeArrayResponse(rsp *http.Response) (*GetSimpleExplodeAr
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2392,9 +2347,6 @@ func ParseGetSimpleExplodeObjectResponse(rsp *http.Response) (*GetSimpleExplodeO
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2411,9 +2363,6 @@ func ParseGetSimpleNoExplodeArrayResponse(rsp *http.Response) (*GetSimpleNoExplo
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2430,9 +2379,6 @@ func ParseGetSimpleNoExplodeObjectResponse(rsp *http.Response) (*GetSimpleNoExpl
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -2449,9 +2395,6 @@ func ParseGetSimplePrimitiveResponse(rsp *http.Response) (*GetSimplePrimitiveRes
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go
index 985c1a3..8c884bf 100644
--- a/internal/test/schemas/schemas.gen.go
+++ b/internal/test/schemas/schemas.gen.go
@@ -941,9 +941,6 @@ func ParseIssue185Response(rsp *http.Response) (*Issue185Response, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -960,9 +957,6 @@ func ParseIssue209Response(rsp *http.Response) (*Issue209Response, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -979,9 +973,6 @@ func ParseIssue30Response(rsp *http.Response) (*Issue30Response, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -998,9 +989,6 @@ func ParseIssue41Response(rsp *http.Response) (*Issue41Response, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
@@ -1017,9 +1005,6 @@ func ParseIssue9Response(rsp *http.Response) (*Issue9Response, error) {
 		HTTPResponse: rsp,
 	}
 
-	switch {
-	}
-
 	return response, nil
 }
 
diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go
index 4857ee4..ceda7da 100644
--- a/pkg/codegen/utils_test.go
+++ b/pkg/codegen/utils_test.go
@@ -134,22 +134,79 @@ func TestSortedRequestBodyKeys(t *testing.T) {
 }
 
 func TestRefPathToGoType(t *testing.T) {
-	goType, err := RefPathToGoType("#/components/schemas/Foo")
-	assert.Equal(t, "Foo", goType)
-	assert.NoError(t, err, "Expecting no error")
-
-	goType, err = RefPathToGoType("#/components/parameters/foo_bar")
-	assert.Equal(t, "FooBar", goType)
-	assert.NoError(t, err, "Expecting no error")
-
-	_, err = RefPathToGoType("http://deepmap.com/doc.json#/components/parameters/foo_bar")
-	assert.Errorf(t, err, "Expected an error on URL reference")
+	old := importMapping
+	importMapping = constructImportMapping(map[string]string{
+		"doc.json":                    "externalref0",
+		"http://deepmap.com/doc.json": "externalref1",
+	})
+	defer func() { importMapping = old }()
+
+	tests := []struct {
+		name   string
+		path   string
+		goType string
+	}{
+		{
+			name:   "local-schemas",
+			path:   "#/components/schemas/Foo",
+			goType: "Foo",
+		},
+		{
+			name:   "local-parameters",
+			path:   "#/components/parameters/foo_bar",
+			goType: "FooBar",
+		},
+		{
+			name:   "local-responses",
+			path:   "#/components/responses/wibble",
+			goType: "Wibble",
+		},
+		{
+			name:   "remote-root",
+			path:   "doc.json#/foo",
+			goType: "externalRef0.Foo",
+		},
+		{
+			name:   "remote-pathed",
+			path:   "doc.json#/components/parameters/foo",
+			goType: "externalRef0.Foo",
+		},
+		{
+			name:   "url-root",
+			path:   "http://deepmap.com/doc.json#/foo_bar",
+			goType: "externalRef1.FooBar",
+		},
+		{
+			name:   "url-pathed",
+			path:   "http://deepmap.com/doc.json#/components/parameters/foo_bar",
+			goType: "externalRef1.FooBar",
+		},
+		{
+			name: "local-too-deep",
+			path: "#/components/parameters/foo/components/bar",
+		},
+		{
+			name: "remote-too-deep",
+			path: "doc.json#/components/parameters/foo/foo_bar",
+		},
+		{
+			name: "url-too-deep",
+			path: "http://deepmap.com/doc.json#/components/parameters/foo/foo_bar",
+		},
+	}
 
-	_, err = RefPathToGoType("doc.json#/components/parameters/foo_bar")
-	assert.Errorf(t, err, "Expected an error on remote reference")
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			goType, err := RefPathToGoType(tc.path)
+			if tc.goType == "" {
+				assert.Error(t, err)
+				return
+			}
 
-	_, err = RefPathToGoType("#/components/parameters/foo/components/bar")
-	assert.Errorf(t, err, "Expected an error on reference depth")
+			assert.NoError(t, err)
+			assert.Equal(t, tc.goType, goType)
+		})
+	}
 }
 
 func TestIsWholeDocumentReference(t *testing.T) {
diff --git a/pkg/runtime/bindparam_test.go b/pkg/runtime/bindparam_test.go
index 0fda0b1..c7991a4 100644
--- a/pkg/runtime/bindparam_test.go
+++ b/pkg/runtime/bindparam_test.go
@@ -16,6 +16,7 @@ package runtime
 import (
 	"encoding/json"
 	"fmt"
+	"math/big"
 	"net/url"
 	"testing"
 	"time"
@@ -447,3 +448,13 @@ func TestBindParamsToExplodedObject(t *testing.T) {
 	assert.EqualValues(t, expectedDate, nTDstDate)
 
 }
+
+func TestBindStyledParameterWithLocation(t *testing.T) {
+	expectedBig := big.NewInt(12345678910)
+
+	var dstBigNumber big.Int
+	err := BindStyledParameterWithLocation("simple", false, "id", ParamLocationUndefined,
+		"12345678910", &dstBigNumber)
+	assert.NoError(t, err)
+	assert.Equal(t, *expectedBig, dstBigNumber)
+}