Repository: swaggest/openapi-go
Pull request #11 (2020-08-25 13:43:49)

Fix embedded types handling

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

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

```diff
@@            Coverage Diff             @@
##           master      #11      +/-   ##
==========================================
+ Coverage   12.03%   12.05%   +0.02%     
==========================================
  Files           5        5              
  Lines        4087     4088       +1     
==========================================
+ Hits          492      493       +1     
  Misses       3538     3538              
  Partials       57       57              
```


| [Impacted Files](https://codecov.io/gh/swaggest/openapi-go/pull/11?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [openapi3/helper.go](https://codecov.io/gh/swaggest/openapi-go/pull/11/diff?src=pr&el=tree#diff-b3BlbmFwaTMvaGVscGVyLmdv) | `85.18% <ø> (ø)` | |
| [openapi3/jsonschema.go](https://codecov.io/gh/swaggest/openapi-go/pull/11/diff?src=pr&el=tree#diff-b3BlbmFwaTMvanNvbnNjaGVtYS5nbw==) | `61.85% <ø> (ø)` | |
| [openapi3/yaml.go](https://codecov.io/gh/swaggest/openapi-go/pull/11/diff?src=pr&el=tree#diff-b3BlbmFwaTMveWFtbC5nbw==) | `42.68% <ø> (ø)` | |
| [openapi3/reflect.go](https://codecov.io/gh/swaggest/openapi-go/pull/11/diff?src=pr&el=tree#diff-b3BlbmFwaTMvcmVmbGVjdC5nbw==) | `84.30% <83.33%> (+0.09%)` | :arrow_up: |

------

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


Patch:
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index 4ff2337..b44c93a 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -13,10 +13,10 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@v0.2.0
+        uses: golangci/golangci-lint-action@v2.2.0
         with:
           # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
-          version: v1.26
+          version: v1.30
 
           # Optional: golangci-lint command line arguments.
           # args: ./the-only-dir-to-analyze/...
diff --git a/Makefile b/Makefile
index 90dfdfa..e321419 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-GOLANGCI_LINT_VERSION := "v1.27.0"
+GOLANGCI_LINT_VERSION := "v1.30.0"
 
 gen:
 	cd resources/schema/ && json-cli gen-go openapi3.json --output ../../openapi3/entities.go --package-name openapi3 --with-zero-values --fluent-setters --root-name Spec
@@ -6,4 +6,4 @@ gen:
 
 lint:
 	@test -s $(GOPATH)/bin/golangci-lint-$(GOLANGCI_LINT_VERSION) || (curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /tmp $(GOLANGCI_LINT_VERSION) && mv /tmp/golangci-lint $(GOPATH)/bin/golangci-lint-$(GOLANGCI_LINT_VERSION))
-	@$(GOPATH)/bin/golangci-lint-$(GOLANGCI_LINT_VERSION) run ./...
+	@$(GOPATH)/bin/golangci-lint-$(GOLANGCI_LINT_VERSION) run ./... --fix
diff --git a/go.mod b/go.mod
index 610de29..e68e077 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.13
 require (
 	github.com/stretchr/testify v1.5.1
 	github.com/swaggest/assertjson v1.0.0
-	github.com/swaggest/jsonschema-go v0.3.5
+	github.com/swaggest/jsonschema-go v0.3.7
 	github.com/swaggest/refl v0.1.3
 	github.com/swaggest/swgen v0.6.23
 	gopkg.in/yaml.v2 v2.3.0
diff --git a/go.sum b/go.sum
index eafdf62..ba481dc 100644
--- a/go.sum
+++ b/go.sum
@@ -56,6 +56,10 @@ github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVt
 github.com/swaggest/jsonschema-go v0.2.4/go.mod h1:m4VV88Gbi7lCrt9ckJzigK1rMlEeFjdZUkJr1o5MnDE=
 github.com/swaggest/jsonschema-go v0.3.5 h1:zOUyPaLfoqgoPB/HnaM3rApLD/nT/BrzZhDeZj9yVUY=
 github.com/swaggest/jsonschema-go v0.3.5/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
+github.com/swaggest/jsonschema-go v0.3.6 h1:fqRQuCX377UnEfAaTwwhaDYchW7y1SnXU1SCihetfKI=
+github.com/swaggest/jsonschema-go v0.3.6/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
+github.com/swaggest/jsonschema-go v0.3.7 h1:EgSPKzzjRrMKGdq7PVF/ziMilQag8HUD8XYCZoii8S8=
+github.com/swaggest/jsonschema-go v0.3.7/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
 github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
 github.com/swaggest/refl v0.1.0 h1:mz3skba8ewcPmxDryM9rETv4fSuAX4GqNxTuDfPyUM0=
 github.com/swaggest/refl v0.1.0/go.mod h1:kmYWhxNEvjfRDdMRqpaR/vLULk/SotJs9HFUCIVMK8o=
diff --git a/openapi3/helper.go b/openapi3/helper.go
index a6cc97e..6b3a5eb 100644
--- a/openapi3/helper.go
+++ b/openapi3/helper.go
@@ -68,11 +68,13 @@ func (s *Spec) SetupOperation(method, path string, setup ...func(*Operation) err
 
 		if found := paramIndex[p.Parameter.Name+string(p.Parameter.In)]; found {
 			errs = append(errs, "duplicate parameter in "+string(p.Parameter.In)+": "+p.Parameter.Name)
+
 			continue
 		}
 
 		if found := pathParams[p.Parameter.Name]; !found && p.Parameter.In == ParameterInPath {
 			errs = append(errs, "missing path parameter placeholder in url: "+p.Parameter.Name)
+
 			continue
 		}
 
@@ -109,6 +111,7 @@ func (s *Spec) AddOperation(method, path string, operation Operation) error {
 
 	return s.SetupOperation(method, path, func(op *Operation) error {
 		*op = operation
+
 		return nil
 	})
 }
diff --git a/openapi3/jsonschema.go b/openapi3/jsonschema.go
index 280e9ff..5c9629e 100644
--- a/openapi3/jsonschema.go
+++ b/openapi3/jsonschema.go
@@ -179,12 +179,14 @@ func (s *SchemaOrRef) toJSONSchema(ctx toJSONSchemaContext) jsonschema.SchemaOrB
 func (s *SchemaOrRef) FromJSONSchema(schema jsonschema.SchemaOrBool) {
 	if schema.TypeBoolean != nil {
 		s.fromBool(*schema.TypeBoolean)
+
 		return
 	}
 
 	js := schema.TypeObject
 	if js.Ref != nil {
 		s.SchemaReference = &SchemaReference{Ref: *js.Ref}
+
 		return
 	}
 
diff --git a/openapi3/reflect.go b/openapi3/reflect.go
index 303abc5..3078a8b 100644
--- a/openapi3/reflect.go
+++ b/openapi3/reflect.go
@@ -85,19 +85,19 @@ func (r *Reflector) parseRequestBody(o *Operation, input interface{}, tag, mime
 	}
 
 	// JSON can be a map or array without field tags.
-	if !hasTaggedFields && !refl.IsSliceOrMap(input) {
+	if !hasTaggedFields && !refl.IsSliceOrMap(input) && refl.FindEmbeddedSliceOrMap(input) == nil {
 		return nil
 	}
 
 	hasFileUpload := false
-	definitionPefix := ""
+	definitionPrefix := ""
 
 	if tag != tagJSON {
-		definitionPefix += strings.Title(tag)
+		definitionPrefix += strings.Title(tag)
 	}
 
 	schema, err := r.Reflect(input,
-		jsonschema.DefinitionsPrefix("#/components/schemas/"+definitionPefix),
+		jsonschema.DefinitionsPrefix("#/components/schemas/"+definitionPrefix),
 		jsonschema.RootRef,
 		jsonschema.PropertyNameTag(tag),
 		jsonschema.InterceptType(func(v reflect.Value, s *jsonschema.Schema) (bool, error) {
@@ -121,6 +121,7 @@ func (r *Reflector) parseRequestBody(o *Operation, input interface{}, tag, mime
 				s.WithFormat("binary")
 
 				hasFileUpload = true
+
 				return true, nil
 			}
 
@@ -144,7 +145,7 @@ func (r *Reflector) parseRequestBody(o *Operation, input interface{}, tag, mime
 
 		s.FromJSONSchema(def)
 
-		r.SpecEns().ComponentsEns().SchemasEns().WithMapOfSchemaOrRefValuesItem(definitionPefix+name, s)
+		r.SpecEns().ComponentsEns().SchemasEns().WithMapOfSchemaOrRefValuesItem(definitionPrefix+name, s)
 	}
 
 	if mime == mimeFormUrlencoded && hasFileUpload {
@@ -165,6 +166,7 @@ func (r *Reflector) parseParametersIn(o *Operation, input interface{}, in Parame
 		jsonschema.DefinitionsPrefix("#/components/schemas/"),
 		jsonschema.CollectDefinitions(r.collectDefinition),
 		jsonschema.PropertyNameTag(string(in)),
+		jsonschema.SkipEmbeddedMapsSlices,
 		jsonschema.InterceptProperty(func(name string, field reflect.StructField, propertySchema *jsonschema.Schema) error {
 			s := SchemaOrRef{}
 			s.FromJSONSchema(propertySchema.ToSchemaOrBool())
diff --git a/openapi3/yaml.go b/openapi3/yaml.go
index 06429b9..7040619 100644
--- a/openapi3/yaml.go
+++ b/openapi3/yaml.go
@@ -30,7 +30,7 @@ func (s *Spec) UnmarshalYAML(data []byte) error {
 
 // MarshalYAML produces YAML bytes.
 func (s *Spec) MarshalYAML() ([]byte, error) {
-	//return ya.Marshal(s)
+	// return ya.Marshal(s)
 	jsonData, err := s.MarshalJSON()
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3f3efc1..8b42212 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,7 +12,7 @@ jobs:
   test:
     strategy:
       matrix:
-        go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x]
+        go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x]
     runs-on: ubuntu-latest
     steps:
       - name: Install Go
@@ -32,5 +32,5 @@ jobs:
       - name: Test
         run: go test -mod=vendor -v -cover -race -coverprofile=coverage.txt -covermode=atomic ./...
       - name: Upload code coverage
-        if: matrix.go-version == '1.14.x'
+        if: matrix.go-version == '1.15.x'
         uses: codecov/codecov-action@v1
\ No newline at end of file
diff --git a/openapi3/reflect_test.go b/openapi3/reflect_test.go
index b9bfac8..63739b5 100644
--- a/openapi3/reflect_test.go
+++ b/openapi3/reflect_test.go
@@ -70,6 +70,7 @@ var _ jsonschema.Preparer = &Resp{}
 
 func (r *Resp) PrepareJSONSchema(s *jsonschema.Schema) error {
 	s.WithExtraPropertiesItem("x-foo", "bar")
+
 	return nil
 }
 
@@ -245,3 +246,77 @@ func TestReflector_SetJSONResponse(t *testing.T) {
 
 	assertjson.Equal(t, expected, b)
 }
+
+type Identity struct {
+	ID string `path:"id"`
+}
+
+type Data []string
+
+type PathParamAndBody struct {
+	Identity
+	Data
+}
+
+func TestReflector_SetRequest_pathParamAndBody(t *testing.T) {
+	reflector := openapi3.Reflector{}
+	op := openapi3.Operation{}
+
+	err := reflector.SetRequest(&op, new(PathParamAndBody), http.MethodPost)
+	assert.NoError(t, err)
+
+	s := reflector.SpecEns()
+	s.Info.Title = apiName
+	s.Info.Version = apiVersion
+	assert.NoError(t, s.AddOperation(http.MethodPost, "/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}": {
+        	            	   "post": {
+        	            	    "parameters": [
+        	            	     {
+        	            	      "name": "id",
+        	            	      "in": "path",
+        	            	      "required": true,
+        	            	      "schema": {
+        	            	       "type": "string"
+        	            	      }
+        	            	     }
+        	            	    ],
+        	            	    "requestBody": {
+        	            	     "content": {
+        	            	      "application/json": {
+        	            	       "schema": {
+        	            	        "$ref": "#/components/schemas/Openapi3TestPathParamAndBody"
+        	            	       }
+        	            	      }
+        	            	     }
+        	            	    },
+        	            	    "responses": {}
+        	            	   }
+        	            	  }
+        	            	 },
+        	            	 "components": {
+        	            	  "schemas": {
+        	            	   "Openapi3TestPathParamAndBody": {
+        	            	    "type": "array",
+        	            	    "items": {
+        	            	     "type": "string"
+        	            	    },
+        	            	    "nullable": true
+        	            	   }
+        	            	  }
+        	            	 }
+        	            	}`)
+
+	assertjson.Equal(t, expected, b, string(b))
+}