From 20449aefbdef256e0f07d025babd7f91081b8ead Mon Sep 17 00:00:00 2001 From: David Bond Date: Wed, 2 Mar 2022 21:07:07 +0000 Subject: [PATCH] Add Data field to api errors (#6) Closes #5 This commit adds the `Data` field to the `APIError` type and provides a helper function `ErrorData` to obtain the slice of errors from the response. This will allow us to expose errors in ACL tests. Signed-off-by: David Bond --- tailscale/client.go | 20 +++++++++++++++++++- tailscale/client_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/tailscale/client.go b/tailscale/client.go index 7a081b6..d91b18c 100644 --- a/tailscale/client.go +++ b/tailscale/client.go @@ -25,10 +25,17 @@ type ( // The APIError type describes an error as returned by the Tailscale API. APIError struct { - Message string `json:"message"` + Message string `json:"message"` + Data []APIErrorData `json:"data"` status int } + // The APIErrorData type describes elements of the data field within errors returned by the Tailscale API. + APIErrorData struct { + User string `json:"user"` + Errors []string `json:"errors"` + } + // The ClientOption type is a function that is used to modify a Client. ClientOption func(c *Client) error ) @@ -485,3 +492,14 @@ func IsNotFound(err error) bool { return false } + +// ErrorData returns the contents of the APIError.Data field from the provided error if it is of type APIError. Returns +// a nil slice if the given error is not of type APIError. +func ErrorData(err error) []APIErrorData { + var apiErr APIError + if errors.As(err, &apiErr) { + return apiErr.Data + } + + return nil +} diff --git a/tailscale/client_test.go b/tailscale/client_test.go index 7ff9953..841fa9f 100644 --- a/tailscale/client_test.go +++ b/tailscale/client_test.go @@ -4,6 +4,7 @@ import ( "context" _ "embed" "encoding/json" + "io" "net/http" "testing" "time" @@ -537,3 +538,27 @@ func TestClient_SetDeviceTags(t *testing.T) { assert.NoError(t, json.Unmarshal(server.Body.Bytes(), &body)) assert.EqualValues(t, tags, body["tags"]) } + +func TestErrorData(t *testing.T) { + t.Parallel() + + t.Run("It should return the data element from a valid error", func(t *testing.T) { + expected := tailscale.APIError{ + Data: []tailscale.APIErrorData{ + { + User: "user1@example.com", + Errors: []string{ + "address \"user2@example.com:400\": want: Accept, got: Drop", + }, + }, + }, + } + + actual := tailscale.ErrorData(expected) + assert.EqualValues(t, expected.Data, actual) + }) + + t.Run("It should return an empty slice for any other error", func(t *testing.T) { + assert.Empty(t, tailscale.ErrorData(io.EOF)) + }) +}