Skip to content

Commit

Permalink
Warnings indicating ignored and replaced parameters (#14962)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
vishalnayak authored Apr 11, 2022
1 parent f258885 commit fd73653
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 4 deletions.
2 changes: 1 addition & 1 deletion builtin/logical/database/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ func TestBackend_connectionCrud(t *testing.T) {
if err != nil || (resp != nil && resp.IsError()) {
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)
}

Expand Down
6 changes: 6 additions & 0 deletions changelog/14962.txt
Original file line number Diff line number Diff line change
@@ -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.
```
3 changes: 1 addition & 2 deletions http/logical_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,6 @@ func TestLogical_CreateToken(t *testing.T) {
})

var actual map[string]interface{}
var nilWarnings interface{}
expected := map[string]interface{}{
"lease_id": "",
"renewable": false,
Expand All @@ -231,12 +230,12 @@ func TestLogical_CreateToken(t *testing.T) {
"mfa_requirement": nil,
"num_uses": json.Number("0"),
},
"warnings": nilWarnings,
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
delete(actual["auth"].(map[string]interface{}), "client_token")
delete(actual["auth"].(map[string]interface{}), "accessor")
delete(actual, "warnings")
expected["request_id"] = actual["request_id"]
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad:\nexpected:\n%#v\nactual:\n%#v", expected, actual)
Expand Down
33 changes: 32 additions & 1 deletion sdk/framework/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
// for the fields over the PUT data.
raw := make(map[string]interface{}, len(path.Fields))
var ignored []string
for k, v := range req.Data {
raw[k] = v
if path.Fields[k] == nil {
ignored = append(ignored, k)
}
}

var replaced []string
for k, v := range captures {
if raw[k] != nil {
replaced = append(replaced, k)
}
raw[k] = v
}

Expand Down Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions sdk/framework/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package framework

import (
"context"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/stretchr/testify/require"
"net/http"
"reflect"
"strings"
Expand Down Expand Up @@ -45,6 +47,52 @@ func TestBackend_impl(t *testing.T) {
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) {
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
return &logical.Response{
Expand Down

0 comments on commit fd73653

Please sign in to comment.