Repository: getkin/kin-openapi
Pull request #483 (2022-02-01 03:12:20)

openapi2conv: Convert response headers
v2 <-> v3 conversion of the Response.Headers objects


Patch:
diff --git a/openapi2/openapi2.go b/openapi2/openapi2.go
index 0d8c072..de9247f 100644
--- a/openapi2/openapi2.go
+++ b/openapi2/openapi2.go
@@ -232,10 +232,7 @@ func (response *Response) UnmarshalJSON(data []byte) error {
 }
 
 type Header struct {
-	openapi3.ExtensionProps
-	Ref         string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
-	Description string `json:"description,omitempty" yaml:"description,omitempty"`
-	Type        string `json:"type,omitempty" yaml:"type,omitempty"`
+	Parameter
 }
 
 func (header *Header) MarshalJSON() ([]byte, error) {
diff --git a/openapi2conv/openapi2_conv.go b/openapi2conv/openapi2_conv.go
index 04cb18d..4177f1f 100644
--- a/openapi2conv/openapi2_conv.go
+++ b/openapi2conv/openapi2_conv.go
@@ -424,9 +424,29 @@ func ToV3Response(response *openapi2.Response) (*openapi3.ResponseRef, error) {
 	if schemaRef := response.Schema; schemaRef != nil {
 		result.WithJSONSchemaRef(ToV3SchemaRef(schemaRef))
 	}
+	if headers := response.Headers; len(headers) > 0 {
+		result.Headers = ToV3Headers(headers)
+	}
 	return &openapi3.ResponseRef{Value: result}, nil
 }
 
+func ToV3Headers(defs map[string]*openapi2.Header) openapi3.Headers {
+	headers := make(openapi3.Headers, len(defs))
+	for name, header := range defs {
+		header.In = ""
+		header.Name = ""
+		if ref := header.Ref; ref != "" {
+			headers[name] = &openapi3.HeaderRef{Ref: ToV3Ref(ref)}
+		} else {
+			parameter, _, _, _ := ToV3Parameter(nil, &header.Parameter, nil)
+			headers[name] = &openapi3.HeaderRef{Value: &openapi3.Header{
+				Parameter: *parameter.Value,
+			}}
+		}
+	}
+	return headers
+}
+
 func ToV3Schemas(defs map[string]*openapi3.SchemaRef) map[string]*openapi3.SchemaRef {
 	schemas := make(map[string]*openapi3.SchemaRef, len(defs))
 	for name, schema := range defs {
@@ -654,6 +674,7 @@ func FromV3(doc3 *openapi3.T) (*openapi2.T, error) {
 		doc2.SecurityDefinitions = doc2SecuritySchemes
 	}
 	doc2.Security = FromV3SecurityRequirements(doc3.Security)
+
 	return doc2, nil
 }
 
@@ -1048,9 +1069,30 @@ func FromV3Response(ref *openapi3.ResponseRef, components *openapi3.Components)
 			result.Schema, _ = FromV3SchemaRef(ct.Schema, components)
 		}
 	}
+	if headers := response.Headers; len(headers) > 0 {
+		var err error
+		if result.Headers, err = FromV3Headers(headers, components); err != nil {
+			return nil, err
+		}
+	}
 	return result, nil
 }
 
+func FromV3Headers(defs openapi3.Headers, components *openapi3.Components) (map[string]*openapi2.Header, error) {
+	headers := make(map[string]*openapi2.Header, len(defs))
+	for name, header := range defs {
+		ref := openapi3.ParameterRef{Ref: header.Ref, Value: &header.Value.Parameter}
+		parameter, err := FromV3Parameter(&ref, components)
+		if err != nil {
+			return nil, err
+		}
+		parameter.In = ""
+		parameter.Name = ""
+		headers[name] = &openapi2.Header{Parameter: *parameter}
+	}
+	return headers, nil
+}
+
 func FromV3SecurityScheme(doc3 *openapi3.T, ref *openapi3.SecuritySchemeRef) (*openapi2.SecurityScheme, error) {
 	securityScheme := ref.Value
 	if securityScheme == nil {


Test patch:
diff --git a/openapi2conv/openapi2_conv_test.go b/openapi2conv/openapi2_conv_test.go
index f8f2878..24ead56 100644
--- a/openapi2conv/openapi2_conv_test.go
+++ b/openapi2conv/openapi2_conv_test.go
@@ -5,9 +5,10 @@ import (
 	"encoding/json"
 	"testing"
 
+	"github.com/stretchr/testify/require"
+
 	"github.com/getkin/kin-openapi/openapi2"
 	"github.com/getkin/kin-openapi/openapi3"
-	"github.com/stretchr/testify/require"
 )
 
 func TestConvOpenAPIV3ToV2(t *testing.T) {
@@ -179,6 +180,13 @@ const exampleV2 = `
 								"$ref": "#/definitions/Item"
 							},
 							"type": "array"
+						},
+						"headers": {
+							"ETag": {
+								"description": "The ETag (or entity tag) HTTP response header is an identifier for a specific version of a resource.",
+								"type": "string",
+								"maxLength": 64
+							}
 						}
 					},
 					"404": {
@@ -543,7 +551,16 @@ const exampleV3 = `
 								}
 							}
 						},
-						"description": "ok"
+						"description": "ok",
+						"headers": {
+							"ETag": {
+								"description": "The ETag (or entity tag) HTTP response header is an identifier for a specific version of a resource.",
+								"schema": {
+									"type": "string",
+									"maxLength": 64
+								}
+							}
+						}
 					},
 					"404": {
 						"description": "404 response"