Repository: deepmap/oapi-codegen
Pull request #308 (2021-03-02 06:53:20)

Fix: Allow to define type fields with whole document or remote reference
Fix: Allow to define type fields with whole document or remote reference

It fixes the following case:
```
$ cat main.yml:
  ...
      elements:
        type: array
        items:
          $ref: './schemas/items.yml'

$ cat ./schemas/items.yml
type: object
properties:
  id:
    type: integer
    format: int64
  name:
    type: string
```


Patch:
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 7eb137f..cf648bc 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -163,7 +163,7 @@ func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterD
 		// If this is a reference to a predefined type, simply use the reference
 		// name as the type. $ref: "#/components/schemas/custom_type" becomes
 		// "CustomType".
-		if paramOrRef.Ref != "" {
+		if IsGoTypeReference(paramOrRef.Ref) {
 			goType, err := RefPathToGoType(paramOrRef.Ref)
 			if err != nil {
 				return nil, fmt.Errorf("error dereferencing (%s) for param (%s): %s",
@@ -297,7 +297,7 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]TypeDefinition, er
 						Schema:       responseSchema,
 						ResponseName: responseName,
 					}
-					if contentType.Schema.Ref != "" {
+					if IsGoTypeReference(contentType.Schema.Ref) {
 						refType, err := RefPathToGoType(contentType.Schema.Ref)
 						if err != nil {
 							return nil, errors.Wrap(err, "error dereferencing response Ref")
@@ -517,7 +517,7 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody
 		}
 
 		// If the body is a pre-defined type
-		if bodyOrRef.Ref != "" {
+		if IsGoTypeReference(bodyOrRef.Ref) {
 			// Convert the reference path to Go type
 			refType, err := RefPathToGoType(bodyOrRef.Ref)
 			if err != nil {
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index 9ed3d8b..11c869a 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -111,7 +111,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 
 	schema := sref.Value
 
-	if sref.Ref != "" {
+	if IsGoTypeReference(sref.Ref) {
 		var err error
 		// Convert the reference path to Go type
 		refType, err = RefPathToGoType(sref.Ref)
@@ -380,7 +380,7 @@ func MergeSchemas(allOf []*openapi3.SchemaRef, path []string) (Schema, error) {
 
 		var refType string
 		var err error
-		if ref != "" {
+		if IsGoTypeReference(ref) {
 			refType, err = RefPathToGoType(ref)
 			if err != nil {
 				return Schema{}, errors.Wrap(err, "error converting reference path to a go type")
@@ -433,7 +433,7 @@ func GenStructFromAllOf(allOf []*openapi3.SchemaRef, path []string) (string, err
 	objectParts := []string{"struct {"}
 	for _, schemaOrRef := range allOf {
 		ref := schemaOrRef.Ref
-		if ref != "" {
+		if IsGoTypeReference(ref) {
 			// We have a referenced type, we will generate an inlined struct
 			// member.
 			// struct {
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index 1431815..96bf339 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -224,6 +224,27 @@ func RefPathToGoType(refPath string) (string, error) {
 	}
 }
 
+// This function takes a $ref value and checks if it has link to go type.
+// #/components/schemas/Foo                     -> true
+// ./local/file.yml#/components/parameters/Bar  -> true
+// ./local/file.yml                             -> false
+// The function can be used to check whether RefPathToGoType($ref) is possible.
+//
+func IsGoTypeReference(ref string) bool {
+	return ref != "" && !IsWholeDocumentReference(ref)
+}
+
+// This function takes a $ref value and checks if it is whole document reference.
+// #/components/schemas/Foo                             -> false
+// ./local/file.yml#/components/parameters/Bar          -> false
+// ./local/file.yml                                     -> true
+// http://deepmap.com/schemas/document.json             -> true
+// http://deepmap.com/schemas/document.json#/Foo        -> false
+//
+func IsWholeDocumentReference(ref string) bool {
+	return ref != "" && !strings.ContainsAny(ref, "#")
+}
+
 // This function converts a swagger style path URI with parameters to a
 // Echo compatible path URI. We need to replace all of Swagger parameters with
 // ":param". Valid input parameters are:


Test patch:
diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go
index 5892832..da2c78f 100644
--- a/pkg/codegen/utils_test.go
+++ b/pkg/codegen/utils_test.go
@@ -152,6 +152,26 @@ func TestRefPathToGoType(t *testing.T) {
 	assert.Errorf(t, err, "Expected an error on reference depth")
 }
 
+func TestIsWholeDocumentReference(t *testing.T) {
+	assert.Equal(t, false, IsWholeDocumentReference(""))
+	assert.Equal(t, false, IsWholeDocumentReference("#/components/schemas/Foo"))
+	assert.Equal(t, false, IsWholeDocumentReference("doc.json#/components/schemas/Foo"))
+	assert.Equal(t, true, IsWholeDocumentReference("doc.json"))
+	assert.Equal(t, true, IsWholeDocumentReference("../doc.json"))
+	assert.Equal(t, false, IsWholeDocumentReference("http://deepmap.com/doc.json#/components/parameters/foo_bar"))
+	assert.Equal(t, true, IsWholeDocumentReference("http://deepmap.com/doc.json"))
+}
+
+func TestIsGoTypeReference(t *testing.T) {
+	assert.Equal(t, false, IsGoTypeReference(""))
+	assert.Equal(t, true, IsGoTypeReference("#/components/schemas/Foo"))
+	assert.Equal(t, true, IsGoTypeReference("doc.json#/components/schemas/Foo"))
+	assert.Equal(t, false, IsGoTypeReference("doc.json"))
+	assert.Equal(t, false, IsGoTypeReference("../doc.json"))
+	assert.Equal(t, true, IsGoTypeReference("http://deepmap.com/doc.json#/components/parameters/foo_bar"))
+	assert.Equal(t, false, IsGoTypeReference("http://deepmap.com/doc.json"))
+}
+
 func TestSwaggerUriToEchoUri(t *testing.T) {
 	assert.Equal(t, "/path", SwaggerUriToEchoUri("/path"))
 	assert.Equal(t, "/path/:arg", SwaggerUriToEchoUri("/path/{arg}"))