Repository: swaggest/openapi-go
Pull request #12 (2020-09-08 11:11:22)

Add RequestBodyEnforcer
In some situations existing API is violating HTTP guidelines and uses GET or HEAD with request body.

This PR enables a workaround to allow such configurations by implementing empty `ForceRequestBody()` function on input structure.

codecov[bot]: # [Codecov](https://codecov.io/gh/swaggest/openapi-go/pull/12?src=pr&el=h1) Report
> Merging [#12](https://codecov.io/gh/swaggest/openapi-go/pull/12?src=pr&el=desc) into [master](https://codecov.io/gh/swaggest/openapi-go/commit/c774271bcc09e9272c43d282f310425fb94069e4?el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff             @@
##           master      #12      +/-   ##
==========================================
+ Coverage   12.05%   12.08%   +0.02%     
==========================================
  Files           5        5              
  Lines        4088     4089       +1     
==========================================
+ Hits          493      494       +1     
  Misses       3538     3538              
  Partials       57       57              
```


| [Impacted Files](https://codecov.io/gh/swaggest/openapi-go/pull/12?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [openapi3/reflect.go](https://codecov.io/gh/swaggest/openapi-go/pull/12/diff?src=pr&el=tree#diff-b3BlbmFwaTMvcmVmbGVjdC5nbw==) | `84.39% <100.00%> (+0.09%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/swaggest/openapi-go/pull/12?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/12?src=pr&el=footer). Last update [c774271...b86f953](https://codecov.io/gh/swaggest/openapi-go/pull/12?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.golangci.yml b/.golangci.yml
index a046fbd..43b7716 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -34,4 +34,5 @@ issues:
     - linters:
         - gomnd
         - goconst
+        - dupl
       path: "_test.go"
diff --git a/openapi3/reflect.go b/openapi3/reflect.go
index 3078a8b..9b8e6c2 100644
--- a/openapi3/reflect.go
+++ b/openapi3/reflect.go
@@ -69,11 +69,20 @@ const (
 	mimeMultipart      = "multipart/form-data"
 )
 
+// RequestBodyEnforcer enables request body for GET and HEAD methods.
+//
+// Should be implemented on input structure, function body can be empty.
+// Forcing request body is not recommended and should only be used for backwards compatibility.
+type RequestBodyEnforcer interface {
+	ForceRequestBody()
+}
+
 func (r *Reflector) parseRequestBody(o *Operation, input interface{}, tag, mime string, httpMethod string) error {
 	httpMethod = strings.ToUpper(httpMethod)
+	_, forceRequestBody := input.(RequestBodyEnforcer)
 
 	// GET and HEAD requests should not have body.
-	if httpMethod == http.MethodGet || httpMethod == http.MethodHead {
+	if (httpMethod == http.MethodGet || httpMethod == http.MethodHead) && !forceRequestBody {
 		return nil
 	}
 


Test patch:
diff --git a/openapi3/reflect_test.go b/openapi3/reflect_test.go
index 63739b5..9fa36bf 100644
--- a/openapi3/reflect_test.go
+++ b/openapi3/reflect_test.go
@@ -320,3 +320,70 @@ func TestReflector_SetRequest_pathParamAndBody(t *testing.T) {
 
 	assertjson.Equal(t, expected, b, string(b))
 }
+
+type WithReqBody PathParamAndBody
+
+func (*WithReqBody) ForceRequestBody() {}
+
+func TestRequestBodyEnforcer(t *testing.T) {
+	reflector := openapi3.Reflector{}
+	op := openapi3.Operation{}
+
+	err := reflector.SetRequest(&op, new(WithReqBody), http.MethodGet)
+	assert.NoError(t, err)
+
+	s := reflector.SpecEns()
+	s.Info.Title = apiName
+	s.Info.Version = apiVersion
+	assert.NoError(t, s.AddOperation(http.MethodGet, "/somewhere/{id}", op))
+
+	b, err := json.MarshalIndent(s, "", " ")
+	assert.NoError(t, err)
+
+	expected := []byte(`{
+        	            	 "openapi": "3.0.2",
+        	            	 "info": {
+        	            	  "title": "SampleAPI",
+        	            	  "version": "1.2.3"
+        	            	 },
+        	            	 "paths": {
+        	            	  "/somewhere/{id}": {
+        	            	   "get": {
+        	            	    "parameters": [
+        	            	     {
+        	            	      "name": "id",
+        	            	      "in": "path",
+        	            	      "required": true,
+        	            	      "schema": {
+        	            	       "type": "string"
+        	            	      }
+        	            	     }
+        	            	    ],
+        	            	    "requestBody": {
+        	            	     "content": {
+        	            	      "application/json": {
+        	            	       "schema": {
+        	            	        "$ref": "#/components/schemas/Openapi3TestWithReqBody"
+        	            	       }
+        	            	      }
+        	            	     }
+        	            	    },
+        	            	    "responses": {}
+        	            	   }
+        	            	  }
+        	            	 },
+        	            	 "components": {
+        	            	  "schemas": {
+        	            	   "Openapi3TestWithReqBody": {
+        	            	    "type": "array",
+        	            	    "items": {
+        	            	     "type": "string"
+        	            	    },
+        	            	    "nullable": true
+        	            	   }
+        	            	  }
+        	            	 }
+        	            	}`)
+
+	assertjson.Equal(t, expected, b, string(b))
+}