Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic response error handling #570

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 3 additions & 18 deletions cloud/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ func (s *BoardService) GetAllBoards(ctx context.Context, opt *BoardListOptions)
boards := new(BoardsList)
resp, err := s.client.Do(req, boards)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
return nil, resp, err
}

return boards, resp, err
Expand All @@ -163,8 +162,7 @@ func (s *BoardService) GetBoard(ctx context.Context, boardID int) (*Board, *Resp
board := new(Board)
resp, err := s.client.Do(req, board)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
return nil, resp, err
}

return board, resp, nil
Expand All @@ -188,8 +186,7 @@ func (s *BoardService) CreateBoard(ctx context.Context, board *Board) (*Board, *
responseBoard := new(Board)
resp, err := s.client.Do(req, responseBoard)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
return nil, resp, err
}

return responseBoard, resp, nil
Expand All @@ -207,9 +204,6 @@ func (s *BoardService) DeleteBoard(ctx context.Context, boardID int) (*Board, *R
}

resp, err := s.client.Do(req, nil)
if err != nil {
err = NewJiraError(resp, err)
}
return nil, resp, err
}

Expand All @@ -230,10 +224,6 @@ func (s *BoardService) GetAllSprints(ctx context.Context, boardID int, options *

result := new(SprintsList)
resp, err := s.client.Do(req, result)
if err != nil {
err = NewJiraError(resp, err)
}

return result, resp, err
}

Expand All @@ -250,10 +240,5 @@ func (s *BoardService) GetBoardConfiguration(ctx context.Context, boardID int) (

result := new(BoardConfiguration)
resp, err := s.client.Do(req, result)
if err != nil {
err = NewJiraError(resp, err)
}

return result, resp, err

}
3 changes: 1 addition & 2 deletions cloud/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ func (s *ComponentService) Create(ctx context.Context, options *CreateComponentO

component := new(ProjectComponent)
resp, err := s.client.Do(req, component)

if err != nil {
return nil, resp, NewJiraError(resp, err)
return nil, resp, err
}

return component, resp, nil
Expand Down
2 changes: 1 addition & 1 deletion cloud/customer.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (c *CustomerService) Create(ctx context.Context, email, displayName string)
responseCustomer := new(Customer)
resp, err := c.client.Do(req, responseCustomer)
if err != nil {
return nil, resp, NewJiraError(resp, err)
return nil, resp, err
}

return responseCustomer, resp, nil
Expand Down
104 changes: 38 additions & 66 deletions cloud/error.go
Original file line number Diff line number Diff line change
@@ -1,87 +1,59 @@
package cloud

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"strings"
"net/http"
"net/url"
)

// Error message from Jira
// JiraRestError error message from Jira REST API
// See https://docs.atlassian.com/jira/REST/cloud/#error-responses
type Error struct {
HTTPError error
type JiraRestError struct {
ErrorMessages []string `json:"errorMessages"`
Errors map[string]string `json:"errors"`
}

// NewJiraError creates a new jira Error
func NewJiraError(resp *Response, httpError error) error {
if resp == nil {
return fmt.Errorf("no response returned: %w", httpError)
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("%s: %w", httpError.Error(), err)
}
jerr := Error{HTTPError: httpError}
contentType := resp.Header.Get("Content-Type")
if strings.HasPrefix(contentType, "application/json") {
err = json.Unmarshal(body, &jerr)
if err != nil {
return fmt.Errorf("%s: could not parse JSON: %w", httpError.Error(), err)
}
} else {
if httpError == nil {
return fmt.Errorf("got response status %s:%s", resp.Status, string(body))
}
return fmt.Errorf("%s: %s: %w", resp.Status, string(body), httpError)
}
var ErrValidation = &ResponseError{statusCode: http.StatusBadRequest}
var ErrUnauthorized = &ResponseError{statusCode: http.StatusUnauthorized}
var ErrNotFound = &ResponseError{statusCode: http.StatusNotFound}
var ErrUnknown = &ResponseError{statusCode: http.StatusInternalServerError}
var ErrNoBody = errors.New("no body in response error")

return &jerr
type ResponseError struct {
status string
statusCode int
url *url.URL
body []byte
}

// Error is a short string representing the error
func (e *Error) Error() string {
if len(e.ErrorMessages) > 0 {
// return fmt.Sprintf("%v", e.HTTPError)
return fmt.Sprintf("%s: %v", e.ErrorMessages[0], e.HTTPError)
}
if len(e.Errors) > 0 {
for key, value := range e.Errors {
return fmt.Sprintf("%s - %s: %v", key, value, e.HTTPError)
}
}
return e.HTTPError.Error()
func (r *ResponseError) Error() string {
return fmt.Sprintf("%s: %d %s", r.url.String(), r.statusCode, r.status)
}

// LongError is a full representation of the error as a string
func (e *Error) LongError() string {
var msg bytes.Buffer
if e.HTTPError != nil {
msg.WriteString("Original:\n")
msg.WriteString(e.HTTPError.Error())
msg.WriteString("\n")
}
if len(e.ErrorMessages) > 0 {
msg.WriteString("Messages:\n")
for _, v := range e.ErrorMessages {
msg.WriteString(" - ")
msg.WriteString(v)
msg.WriteString("\n")
}
// Body returns the response body if present
func (r *ResponseError) Body() []byte {
return r.body
}

func (r *ResponseError) JiraRESTError() (*JiraRestError, error) {
if r.body == nil {
return nil, ErrNoBody
}
if len(e.Errors) > 0 {
for key, value := range e.Errors {
msg.WriteString(" - ")
msg.WriteString(key)
msg.WriteString(" - ")
msg.WriteString(value)
msg.WriteString("\n")
}

var jiraErr JiraRestError
err := json.Unmarshal(r.body, &jiraErr)
return &jiraErr, err
}

// Is checks if the error status code is equal
func (r *ResponseError) Is(tgt error) bool {
target, ok := tgt.(*ResponseError)
if !ok {
return false
}
return msg.String()

return r.statusCode == target.statusCode
}