github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/cloudwrapper/errors.go (about)

     1  package cloudwrapper
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  
    10  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/errs"
    11  )
    12  
    13  type (
    14  	// Error is a cloudwrapper error implementation
    15  	// For details on possible error types, refer to: https://techdocs.akamai.com/cloud-wrapper/reference/errors
    16  	Error struct {
    17  		Type        string      `json:"type,omitempty"`
    18  		Title       string      `json:"title,omitempty"`
    19  		Instance    string      `json:"instance"`
    20  		Status      int         `json:"status"`
    21  		Detail      string      `json:"detail"`
    22  		Errors      []ErrorItem `json:"errors"`
    23  		Method      string      `json:"method"`
    24  		ServerIP    string      `json:"serverIp"`
    25  		ClientIP    string      `json:"clientIp"`
    26  		RequestID   string      `json:"requestId"`
    27  		RequestTime string      `json:"requestTime"`
    28  	}
    29  
    30  	// ErrorItem is a cloud wrapper error's item
    31  	ErrorItem struct {
    32  		Type             string `json:"type"`
    33  		Title            string `json:"title"`
    34  		Detail           string `json:"detail"`
    35  		IllegalValue     any    `json:"illegalValue"`
    36  		IllegalParameter string `json:"illegalParameter"`
    37  	}
    38  )
    39  
    40  const (
    41  	configurationNotFoundType = "/cloud-wrapper/error-types/not-found"
    42  	deletionNotAllowedType    = "/cloud-wrapper/error-types/forbidden"
    43  )
    44  
    45  var (
    46  	// ErrConfigurationNotFound is returned when configuration was not found
    47  	ErrConfigurationNotFound = errors.New("configuration not found")
    48  	// ErrDeletionNotAllowed is returned when user has insufficient permissions to delete configuration
    49  	ErrDeletionNotAllowed = errors.New("deletion not allowed")
    50  )
    51  
    52  // Error parses an error from the response
    53  func (c *cloudwrapper) Error(r *http.Response) error {
    54  	var result Error
    55  	var body []byte
    56  	body, err := ioutil.ReadAll(r.Body)
    57  	if err != nil {
    58  		c.Log(r.Request.Context()).Errorf("reading error response body: %s", err)
    59  		result.Status = r.StatusCode
    60  		result.Title = "Failed to read error body"
    61  		result.Detail = err.Error()
    62  		return &result
    63  	}
    64  
    65  	if err = json.Unmarshal(body, &result); err != nil {
    66  		c.Log(r.Request.Context()).Errorf("could not unmarshal API error: %s", err)
    67  		result.Title = "Failed to unmarshal error body. Cloud Wrapper API failed. Check details for more information."
    68  		result.Detail = errs.UnescapeContent(string(body))
    69  		result.Status = r.StatusCode
    70  	}
    71  	return &result
    72  }
    73  
    74  func (e *Error) Error() string {
    75  	msg, err := json.MarshalIndent(e, "", "\t")
    76  	if err != nil {
    77  		return fmt.Sprintf("error marshaling API error: %s", err)
    78  	}
    79  	return fmt.Sprintf("API error: \n%s", msg)
    80  }
    81  
    82  // Is handles error comparisons
    83  func (e *Error) Is(target error) bool {
    84  	if errors.Is(target, ErrConfigurationNotFound) {
    85  		return e.Status == http.StatusNotFound && e.Type == configurationNotFoundType
    86  	}
    87  	if errors.Is(target, ErrDeletionNotAllowed) {
    88  		return e.Status == http.StatusForbidden && e.Type == deletionNotAllowedType
    89  	}
    90  
    91  	var t *Error
    92  	if !errors.As(target, &t) {
    93  		return false
    94  	}
    95  
    96  	if e == t {
    97  		return true
    98  	}
    99  
   100  	if e.Status != t.Status {
   101  		return false
   102  	}
   103  
   104  	return e.Error() == t.Error()
   105  }