Repository: swaggest/openapi-go
Pull request #15 (2020-10-06 23:02:55)

Add control of response content-type

codecov[bot]: # [Codecov](https://codecov.io/gh/swaggest/openapi-go/pull/15?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@6f69602`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/swaggest/openapi-go/pull/15/graphs/tree.svg?width=650&height=150&src=pr&token=3uETjeijpQ)](https://codecov.io/gh/swaggest/openapi-go/pull/15?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master      #15   +/-   ##
=========================================
  Coverage          ?   37.15%           
=========================================
  Files             ?        5           
  Lines             ?     3467           
  Branches          ?        0           
=========================================
  Hits              ?     1288           
  Misses            ?     1895           
  Partials          ?      284           
```

| Flag | Coverage Δ | |
|---|---|---|
| #unittests | `37.15% <100.00%> (?)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/swaggest/openapi-go/pull/15?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [openapi3/entities.go](https://codecov.io/gh/swaggest/openapi-go/pull/15/diff?src=pr&el=tree#diff-b3BlbmFwaTMvZW50aXRpZXMuZ28=) | `31.43% <ø> (ø)` | |
| [openapi3/reflect.go](https://codecov.io/gh/swaggest/openapi-go/pull/15/diff?src=pr&el=tree#diff-b3BlbmFwaTMvcmVmbGVjdC5nbw==) | `85.36% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/swaggest/openapi-go/pull/15?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/swaggest/openapi-go/pull/15?src=pr&el=footer). Last update [6f69602...c23a543](https://codecov.io/gh/swaggest/openapi-go/pull/15?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/openapi3/entities.go b/openapi3/entities.go
index 9eb21da..f1fda80 100644
--- a/openapi3/entities.go
+++ b/openapi3/entities.go
@@ -3257,10 +3257,8 @@ func (h *Header) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constHeader is unconditionally added to JSON.
-	constHeader = json.RawMessage(`{"style":"simple"}`)
-)
+// constHeader is unconditionally added to JSON.
+var constHeader = json.RawMessage(`{"style":"simple"}`)
 
 // MarshalJSON encodes JSON.
 func (h Header) MarshalJSON() ([]byte, error) {
@@ -3560,10 +3558,8 @@ func (p *PathParameter) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constPathParameter is unconditionally added to JSON.
-	constPathParameter = json.RawMessage(`{"in":"path","required":true}`)
-)
+// constPathParameter is unconditionally added to JSON.
+var constPathParameter = json.RawMessage(`{"in":"path","required":true}`)
 
 // MarshalJSON encodes JSON.
 func (p PathParameter) MarshalJSON() ([]byte, error) {
@@ -3616,10 +3612,8 @@ func (q *QueryParameter) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constQueryParameter is unconditionally added to JSON.
-	constQueryParameter = json.RawMessage(`{"in":"query"}`)
-)
+// constQueryParameter is unconditionally added to JSON.
+var constQueryParameter = json.RawMessage(`{"in":"query"}`)
 
 // MarshalJSON encodes JSON.
 func (q QueryParameter) MarshalJSON() ([]byte, error) {
@@ -3660,10 +3654,8 @@ func (h *HeaderParameter) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constHeaderParameter is unconditionally added to JSON.
-	constHeaderParameter = json.RawMessage(`{"in":"header","style":"simple"}`)
-)
+// constHeaderParameter is unconditionally added to JSON.
+var constHeaderParameter = json.RawMessage(`{"in":"header","style":"simple"}`)
 
 // MarshalJSON encodes JSON.
 func (h HeaderParameter) MarshalJSON() ([]byte, error) {
@@ -3704,10 +3696,8 @@ func (c *CookieParameter) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constCookieParameter is unconditionally added to JSON.
-	constCookieParameter = json.RawMessage(`{"in":"cookie","style":"form"}`)
-)
+// constCookieParameter is unconditionally added to JSON.
+var constCookieParameter = json.RawMessage(`{"in":"cookie","style":"form"}`)
 
 // MarshalJSON encodes JSON.
 func (c CookieParameter) MarshalJSON() ([]byte, error) {
@@ -6556,10 +6546,8 @@ func (a *APIKeySecurityScheme) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constAPIKeySecurityScheme is unconditionally added to JSON.
-	constAPIKeySecurityScheme = json.RawMessage(`{"type":"apiKey"}`)
-)
+// constAPIKeySecurityScheme is unconditionally added to JSON.
+var constAPIKeySecurityScheme = json.RawMessage(`{"type":"apiKey"}`)
 
 // MarshalJSON encodes JSON.
 func (a APIKeySecurityScheme) MarshalJSON() ([]byte, error) {
@@ -6752,10 +6740,8 @@ func (h *HTTPSecurityScheme) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constHTTPSecurityScheme is unconditionally added to JSON.
-	constHTTPSecurityScheme = json.RawMessage(`{"type":"http"}`)
-)
+// constHTTPSecurityScheme is unconditionally added to JSON.
+var constHTTPSecurityScheme = json.RawMessage(`{"type":"http"}`)
 
 // MarshalJSON encodes JSON.
 func (h HTTPSecurityScheme) MarshalJSON() ([]byte, error) {
@@ -6790,10 +6776,8 @@ func (b *Bearer) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constBearer is unconditionally added to JSON.
-	constBearer = json.RawMessage(`{"scheme":"bearer"}`)
-)
+// constBearer is unconditionally added to JSON.
+var constBearer = json.RawMessage(`{"scheme":"bearer"}`)
 
 // MarshalJSON encodes JSON.
 func (b Bearer) MarshalJSON() ([]byte, error) {
@@ -6938,10 +6922,8 @@ func (o *OAuth2SecurityScheme) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constOAuth2SecurityScheme is unconditionally added to JSON.
-	constOAuth2SecurityScheme = json.RawMessage(`{"type":"oauth2"}`)
-)
+// constOAuth2SecurityScheme is unconditionally added to JSON.
+var constOAuth2SecurityScheme = json.RawMessage(`{"type":"oauth2"}`)
 
 // MarshalJSON encodes JSON.
 func (o OAuth2SecurityScheme) MarshalJSON() ([]byte, error) {
@@ -7811,10 +7793,8 @@ func (o *OpenIDConnectSecurityScheme) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-var (
-	// constOpenIDConnectSecurityScheme is unconditionally added to JSON.
-	constOpenIDConnectSecurityScheme = json.RawMessage(`{"type":"openIdConnect"}`)
-)
+// constOpenIDConnectSecurityScheme is unconditionally added to JSON.
+var constOpenIDConnectSecurityScheme = json.RawMessage(`{"type":"openIdConnect"}`)
 
 // MarshalJSON encodes JSON.
 func (o OpenIDConnectSecurityScheme) MarshalJSON() ([]byte, error) {
diff --git a/openapi3/reflect.go b/openapi3/reflect.go
index fa6d3ce..2300d3c 100644
--- a/openapi3/reflect.go
+++ b/openapi3/reflect.go
@@ -58,6 +58,7 @@ type OperationContext struct {
 
 	Output            interface{}
 	HTTPStatus        int
+	RespContentType   string
 	RespHeaderMapping map[string]string
 }
 
@@ -345,7 +346,9 @@ func (r *Reflector) SetupResponse(oc OperationContext) error {
 	resp := Response{}
 
 	if oc.Output != nil {
-		err := r.parseJSONResponse(&resp, oc.Output)
+		oc.RespContentType = strings.Split(oc.RespContentType, ";")[0]
+
+		err := r.parseJSONResponse(&resp, oc.Output, oc.RespContentType)
 		if err != nil {
 			return err
 		}
@@ -354,6 +357,10 @@ func (r *Reflector) SetupResponse(oc OperationContext) error {
 		if err != nil {
 			return err
 		}
+
+		if oc.RespContentType != "" {
+			r.ensureResponseContentType(&resp, oc.RespContentType)
+		}
 	}
 
 	if resp.Description == "" {
@@ -367,7 +374,19 @@ func (r *Reflector) SetupResponse(oc OperationContext) error {
 	return nil
 }
 
-func (r *Reflector) parseJSONResponse(resp *Response, output interface{}) error {
+func (r *Reflector) ensureResponseContentType(resp *Response, contentType string) {
+	if _, ok := resp.Content[contentType]; !ok {
+		if resp.Content == nil {
+			resp.Content = map[string]MediaType{}
+		}
+
+		resp.Content[contentType] = MediaType{
+			Schema: &SchemaOrRef{Schema: &Schema{}},
+		}
+	}
+}
+
+func (r *Reflector) parseJSONResponse(resp *Response, output interface{}, contentType string) error {
 	// Check if output structure exposes meaningful schema.
 	if hasJSONBody, err := r.hasJSONBody(output); err == nil && !hasJSONBody {
 		return nil
@@ -393,7 +412,11 @@ func (r *Reflector) parseJSONResponse(resp *Response, output interface{}) error
 		resp.Content = map[string]MediaType{}
 	}
 
-	resp.Content[mimeJSON] = MediaType{
+	if contentType == "" {
+		contentType = mimeJSON
+	}
+
+	resp.Content[contentType] = MediaType{
 		Schema:        &oaiSchema,
 		Example:       nil,
 		Examples:      nil,


Test patch:
diff --git a/openapi3/reflect_test.go b/openapi3/reflect_test.go
index 51aab3f..7cb142e 100644
--- a/openapi3/reflect_test.go
+++ b/openapi3/reflect_test.go
@@ -339,8 +339,9 @@ func TestReflector_SetupResponse(t *testing.T) {
 	s.Info.Version = apiVersion
 
 	require.NoError(t, reflector.SetupResponse(openapi3.OperationContext{
-		Operation:  &op,
-		HTTPStatus: http.StatusNoContent,
+		Operation:       &op,
+		RespContentType: "text/csv; charset=utf-8",
+		HTTPStatus:      http.StatusNoContent,
 		Output: new(struct {
 			Val1 int
 			Val2 string
@@ -366,7 +367,8 @@ func TestReflector_SetupResponse(t *testing.T) {
 		  "headers":{
 		   "X-Value-1":{"style":"simple","schema":{"type":"integer"}},
 		   "X-Value-2":{"style":"simple","schema":{"type":"string"}}
-		  }
+		  },
+		  "content":{"text/csv":{"schema":{}}}
 		 }
 		}
 	   }