Warnings indicating ignored and replaced parameters (#14962)
* Warnings indicating ignored and replaced parameters * Avoid additional var creation * Add warnings only if the response is non-nil * Return the response even when error is non-nil * Fix tests * Rearrange comments * Print warning in the log * Fix another test * Add CL
This commit is contained in:
parent
660fefe25b
commit
ad3bf3173c
|
@ -689,7 +689,7 @@ func TestBackend_connectionCrud(t *testing.T) {
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
||||||
}
|
}
|
||||||
if len(resp.Warnings) != 1 {
|
if len(resp.Warnings) == 0 {
|
||||||
t.Fatalf("expected warning about password in url %s, resp:%#v\n", connURL, resp)
|
t.Fatalf("expected warning about password in url %s, resp:%#v\n", connURL, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
```release-note:improvement
|
||||||
|
api: If the parameters supplied over the API payload are ignored due to not
|
||||||
|
being what the endpoints were expecting, or if the parameters supplied get
|
||||||
|
replaced by the values in the endpoint's path itself, warnings will be added to
|
||||||
|
the non-empty responses listing all the ignored and replaced parameters.
|
||||||
|
```
|
|
@ -212,7 +212,6 @@ func TestLogical_CreateToken(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
var actual map[string]interface{}
|
var actual map[string]interface{}
|
||||||
var nilWarnings interface{}
|
|
||||||
expected := map[string]interface{}{
|
expected := map[string]interface{}{
|
||||||
"lease_id": "",
|
"lease_id": "",
|
||||||
"renewable": false,
|
"renewable": false,
|
||||||
|
@ -231,12 +230,12 @@ func TestLogical_CreateToken(t *testing.T) {
|
||||||
"mfa_requirement": nil,
|
"mfa_requirement": nil,
|
||||||
"num_uses": json.Number("0"),
|
"num_uses": json.Number("0"),
|
||||||
},
|
},
|
||||||
"warnings": nilWarnings,
|
|
||||||
}
|
}
|
||||||
testResponseStatus(t, resp, 200)
|
testResponseStatus(t, resp, 200)
|
||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
delete(actual["auth"].(map[string]interface{}), "client_token")
|
delete(actual["auth"].(map[string]interface{}), "client_token")
|
||||||
delete(actual["auth"].(map[string]interface{}), "accessor")
|
delete(actual["auth"].(map[string]interface{}), "accessor")
|
||||||
|
delete(actual, "warnings")
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
t.Fatalf("bad:\nexpected:\n%#v\nactual:\n%#v", expected, actual)
|
t.Fatalf("bad:\nexpected:\n%#v\nactual:\n%#v", expected, actual)
|
||||||
|
|
|
@ -218,10 +218,19 @@ func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*log
|
||||||
// Build up the data for the route, with the URL taking priority
|
// Build up the data for the route, with the URL taking priority
|
||||||
// for the fields over the PUT data.
|
// for the fields over the PUT data.
|
||||||
raw := make(map[string]interface{}, len(path.Fields))
|
raw := make(map[string]interface{}, len(path.Fields))
|
||||||
|
var ignored []string
|
||||||
for k, v := range req.Data {
|
for k, v := range req.Data {
|
||||||
raw[k] = v
|
raw[k] = v
|
||||||
|
if path.Fields[k] == nil {
|
||||||
|
ignored = append(ignored, k)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var replaced []string
|
||||||
for k, v := range captures {
|
for k, v := range captures {
|
||||||
|
if raw[k] != nil {
|
||||||
|
replaced = append(replaced, k)
|
||||||
|
}
|
||||||
raw[k] = v
|
raw[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +284,29 @@ func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(ctx, req, &fd)
|
resp, err := callback(ctx, req, &fd)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch resp {
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
// If fields supplied in the request are not present in the field schema
|
||||||
|
// of the path, add a warning to the response indicating that those
|
||||||
|
// parameters will be ignored.
|
||||||
|
if len(ignored) != 0 {
|
||||||
|
resp.AddWarning(fmt.Sprintf("Endpoint ignored these unrecognized parameters: %v", ignored))
|
||||||
|
}
|
||||||
|
// If fields supplied in the request is being overwritten by the values
|
||||||
|
// supplied in the API request path, add a warning to the response
|
||||||
|
// indicating that those parameters will be replaced.
|
||||||
|
if len(replaced) != 0 {
|
||||||
|
resp.AddWarning(fmt.Sprintf("Endpoint replaced the value of these parameters with the values captured from the endpoint's path: %v", replaced))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlePatchOperation acts as an abstraction for performing JSON merge patch
|
// HandlePatchOperation acts as an abstraction for performing JSON merge patch
|
||||||
|
|
|
@ -2,6 +2,8 @@ package framework
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -45,6 +47,52 @@ func TestBackend_impl(t *testing.T) {
|
||||||
var _ logical.Backend = new(Backend)
|
var _ logical.Backend = new(Backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackendHandleRequestFieldWarnings(t *testing.T) {
|
||||||
|
handler := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
|
||||||
|
return &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"an_int": data.Get("an_int"),
|
||||||
|
"a_string": data.Get("a_string"),
|
||||||
|
"name": data.Get("name"),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
backend := &Backend{
|
||||||
|
Paths: []*Path{
|
||||||
|
{
|
||||||
|
Pattern: "foo/bar/(?P<name>.+)",
|
||||||
|
Fields: map[string]*FieldSchema{
|
||||||
|
"an_int": {Type: TypeInt},
|
||||||
|
"a_string": {Type: TypeString},
|
||||||
|
"name": {Type: TypeString},
|
||||||
|
},
|
||||||
|
Operations: map[logical.Operation]OperationHandler{
|
||||||
|
logical.UpdateOperation: &PathOperation{Callback: handler},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
resp, err := backend.HandleRequest(ctx, &logical.Request{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Path: "foo/bar/baz",
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"an_int": 10,
|
||||||
|
"a_string": "accepted",
|
||||||
|
"unrecognized1": "unrecognized",
|
||||||
|
"unrecognized2": 20.2,
|
||||||
|
"name": "noop",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
t.Log(resp.Warnings)
|
||||||
|
require.Len(t, resp.Warnings, 2)
|
||||||
|
require.True(t, strutil.StrListContains(resp.Warnings, "Endpoint ignored these unrecognized parameters: [unrecognized1 unrecognized2]"))
|
||||||
|
require.True(t, strutil.StrListContains(resp.Warnings, "Endpoint replaced the value of these parameters with the values captured from the endpoint's path: [name]"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestBackendHandleRequest(t *testing.T) {
|
func TestBackendHandleRequest(t *testing.T) {
|
||||||
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
|
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
|
||||||
return &logical.Response{
|
return &logical.Response{
|
||||||
|
|
Loading…
Reference in New Issue