Repository: getkin/kin-openapi (go)

Issue:
Duplicate error reason for request parameter when it is required
When I define a request parameter which is required I get the message `value is required but missing` twice when I call `openapi3filter.RequestError.Error()`.

Schema:
```yml
#...
parameters:
  - name: 'hostname'
    in: query
    description: '...'
    schema:
      type: string
    required: true
#...
```

Code for validation:
```go
if err := openapi3filter.ValidateRequest(c.Request.Context(), requestValidationInput); err != nil {
	c.String(400, err.Error())
	c.Abort()
	return
}
```
Message when the parameter is missing:
`parameter "hostname" in query has an error: value is required but missing: value is required but missing`

Pull request:
fix: duplicate error reason when parameter is required but not present
Fixes #452 

This PR removes the population of `Reason` in `RequestError` when a parameter is required but not present. This was one of the few places `Reason` was even set.

Patch:
diff --git a/openapi3filter/validate_request.go b/openapi3filter/validate_request.go
index 2f9a5f1..990b299 100644
--- a/openapi3filter/validate_request.go
+++ b/openapi3filter/validate_request.go
@@ -140,7 +140,7 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
 	// Validate a parameter's value.
 	if value == nil {
 		if parameter.Required {
-			return &RequestError{Input: input, Parameter: parameter, Reason: ErrInvalidRequired.Error(), Err: ErrInvalidRequired}
+			return &RequestError{Input: input, Parameter: parameter, Err: ErrInvalidRequired}
 		}
 		return nil
 	}
diff --git a/openapi3filter/validation_error_encoder.go b/openapi3filter/validation_error_encoder.go
index 707b22d..2051869 100644
--- a/openapi3filter/validation_error_encoder.go
+++ b/openapi3filter/validation_error_encoder.go
@@ -75,7 +75,7 @@ func convertBasicRequestError(e *RequestError) *ValidationError {
 }
 
 func convertErrInvalidRequired(e *RequestError) *ValidationError {
-	if e.Reason == ErrInvalidRequired.Error() && e.Parameter != nil {
+	if e.Err == ErrInvalidRequired && e.Parameter != nil {
 		return &ValidationError{
 			Status: http.StatusBadRequest,
 			Title:  fmt.Sprintf("parameter %q in %s is required", e.Parameter.Name, e.Parameter.In),


Test patch:
diff --git a/openapi3/loader_recursive_ref_test.go b/openapi3/loader_recursive_ref_test.go
index bd65903..5b7c150 100644
--- a/openapi3/loader_recursive_ref_test.go
+++ b/openapi3/loader_recursive_ref_test.go
@@ -11,7 +11,39 @@ func TestLoaderSupportsRecursiveReference(t *testing.T) {
 	loader.IsExternalRefsAllowed = true
 	doc, err := loader.LoadFromFile("testdata/recursiveRef/openapi.yml")
 	require.NoError(t, err)
-	require.NotNil(t, doc)
-	require.NoError(t, doc.Validate(loader.Context))
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
 	require.Equal(t, "bar", doc.Paths["/foo"].Get.Responses.Get(200).Value.Content.Get("application/json").Schema.Value.Properties["foo2"].Value.Properties["foo"].Value.Properties["bar"].Value.Example)
 }
+
+func TestIssue447(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData([]byte(`
+openapi: 3.0.1
+info:
+  title: Recursive refs example
+  version: "1.0"
+paths: {}
+components:
+  schemas:
+    Complex:
+      type: object
+      properties:
+        parent:
+          $ref: '#/components/schemas/Complex'
+`))
+	require.NoError(t, err)
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+	require.Equal(t, "object", doc.Components.
+		// Complex
+		Schemas["Complex"].
+		// parent
+		Value.Properties["parent"].
+		// parent
+		Value.Properties["parent"].
+		// parent
+		Value.Properties["parent"].
+		// type
+		Value.Type)
+}
diff --git a/openapi3filter/validation_error_test.go b/openapi3filter/validation_error_test.go
index d539c3f..6eadbd0 100644
--- a/openapi3filter/validation_error_test.go
+++ b/openapi3filter/validation_error_test.go
@@ -180,7 +180,7 @@ func getValidationTests(t *testing.T) []*validationTest {
 			},
 			wantErrParam:   "status",
 			wantErrParamIn: "query",
-			wantErrReason:  ErrInvalidRequired.Error(),
+			wantErrBody:    `parameter "status" in query has an error: value is required but missing`,
 			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
 				Title: `parameter "status" in query is required`},
 		},
@@ -424,7 +424,7 @@ func getValidationTests(t *testing.T) []*validationTest {
 			},
 			wantErrParam:   "petId",
 			wantErrParamIn: "path",
-			wantErrReason:  ErrInvalidRequired.Error(),
+			wantErrBody:    `parameter "petId" in path has an error: value is required but missing`,
 			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
 				Title: `parameter "petId" in path is required`},
 		},