Skip to content

Commit

Permalink
Add details to APIError
Browse files Browse the repository at this point in the history
Add Details field to APIError to contain the details of the errors returned by the API.
Also add a method to return the details of type ErrorInfo.
  • Loading branch information
hectorcast-db committed Sep 15, 2023
1 parent ad0cb9e commit 97c8d7d
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 2 deletions.
37 changes: 35 additions & 2 deletions apierr/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ var (
}
)

const (
errorInfoType string = "type.googleapis.com/google.rpc.ErrorInfo"
)

// APIErrorBody maps "proper" databricks rest api errors to a struct
type APIErrorBody struct {
ErrorCode string `json:"error_code,omitempty"`
Message string `json:"message,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
Message string `json:"message,omitempty"`
Details []ErrorDetail `json:"details,omitempty"`
// The following two are for scim api only
// for RFC 7644 Section 3.7.3 https://tools.ietf.org/html/rfc7644#section-3.7.3
ScimDetail string `json:"detail,omitempty"`
Expand All @@ -41,11 +46,19 @@ type APIErrorBody struct {
API12Error string `json:"error,omitempty"`
}

type ErrorDetail struct {
Type string `json:"@type,omitempty"`
Reason string `json:"reason,omitempty"`
Domain string `json:"domain,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}

// APIError is a generic struct for an api error on databricks
type APIError struct {
ErrorCode string
Message string
StatusCode int
Details []ErrorDetail
}

// Error returns error message string instead of
Expand All @@ -62,6 +75,25 @@ func IsMissing(err error) bool {
return false
}

// GetErrorInfo returns all entries in the list of error details of type `ErrorInfo`.
func GetErrorInfo(err error) []ErrorDetail {
return getDetailsByType(err, errorInfoType)
}

func getDetailsByType(err error, errorDetailType string) []ErrorDetail {
var apiError *APIError
if !errors.As(err, &apiError) {
return nil
}
filteredDetails := []ErrorDetail{}
for _, detail := range apiError.Details {
if errorDetailType == detail.Type {
filteredDetails = append(filteredDetails, detail)
}
}
return filteredDetails
}

// IsMissing tells if it is missing resource
func (apiError *APIError) IsMissing() bool {
return apiError.StatusCode == http.StatusNotFound
Expand Down Expand Up @@ -165,6 +197,7 @@ func parseErrorFromResponse(resp *http.Response, body []byte) *APIError {
Message: errorBody.Message,
ErrorCode: errorBody.ErrorCode,
StatusCode: resp.StatusCode,
Details: errorBody.Details,
}
}

Expand Down
54 changes: 54 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,60 @@ func TestSimpleRequestFailsAPIError(t *testing.T) {
assert.EqualError(t, err, "nope")
}

func TestETag(t *testing.T) {
reason := "some_reason"
domain := "a_domain"
eTag := "sample_etag"
c := &DatabricksClient{
Config: config.NewMockConfig(func(r *http.Request) error {
return nil
}),
httpClient: hc(func(r *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: 400,
Request: r,
Body: io.NopCloser(strings.NewReader(fmt.Sprintf(`{
"error_code": "RESOURCE_CONFLICT",
"message": "test_public_workspace_setting",
"stack_trace": "java.io.PrintWriter@329e4ed3",
"details": [
{
"@type": "%s",
"reason": "%s",
"domain": "%s",
"metadata": {
"etag": "%s"
}
},
{
"@type": "anotherType",
"reason": "",
"domain": "",
"metadata": {
"etag": "anotherTag"
}
}
]
}`, "type.googleapis.com/google.rpc.ErrorInfo", reason, domain, eTag))),
}, nil
}),
rateLimiter: rate.NewLimiter(rate.Inf, 1),
}
err := c.Do(context.Background(), "GET", "/a/b", map[string]string{
"e": "f",
}, map[string]string{
"c": "d",
}, nil)
details := apierr.GetErrorInfo(err)
assert.Equal(t, 1, len(details))
errorDetails := details[0]
assert.Equal(t, reason, errorDetails.Reason)
assert.Equal(t, domain, errorDetails.Domain)
assert.Equal(t, map[string]string{
"etag": eTag,
}, errorDetails.Metadata)
}

func TestSimpleRequestSucceeds(t *testing.T) {
type Dummy struct {
Foo int `json:"foo"`
Expand Down

0 comments on commit 97c8d7d

Please sign in to comment.