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

     1  package papi
     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 papi error interface
    15  	Error struct {
    16  		Type          string          `json:"type"`
    17  		Title         string          `json:"title,omitempty"`
    18  		Detail        string          `json:"detail"`
    19  		Instance      string          `json:"instance,omitempty"`
    20  		BehaviorName  string          `json:"behaviorName,omitempty"`
    21  		ErrorLocation string          `json:"errorLocation,omitempty"`
    22  		StatusCode    int             `json:"statusCode,omitempty"`
    23  		Errors        json.RawMessage `json:"errors,omitempty"`
    24  		Warnings      json.RawMessage `json:"warnings,omitempty"`
    25  		LimitKey      string          `json:"limitKey,omitempty"`
    26  		Limit         *int            `json:"limit,omitempty"`
    27  		Remaining     *int            `json:"remaining,omitempty"`
    28  	}
    29  
    30  	// ActivationError represents errors returned in validation objects in include activation response
    31  	ActivationError struct {
    32  		Type      string                   `json:"type"`
    33  		Title     string                   `json:"title"`
    34  		Instance  string                   `json:"instance"`
    35  		Status    int                      `json:"status"`
    36  		Errors    []ActivationErrorMessage `json:"errors"`
    37  		MessageID string                   `json:"messageId"`
    38  		Result    string                   `json:"result"`
    39  	}
    40  
    41  	// ActivationErrorMessage represents detailed information about validation errors
    42  	ActivationErrorMessage struct {
    43  		Type   string `json:"type"`
    44  		Title  string `json:"title"`
    45  		Detail string `json:"detail"`
    46  	}
    47  )
    48  
    49  // Error parses an error from the response
    50  func (p *papi) Error(r *http.Response) error {
    51  	var e Error
    52  
    53  	var body []byte
    54  
    55  	body, err := ioutil.ReadAll(r.Body)
    56  	if err != nil {
    57  		p.Log(r.Request.Context()).Errorf("reading error response body: %s", err)
    58  		e.StatusCode = r.StatusCode
    59  		e.Title = fmt.Sprintf("Failed to read error body")
    60  		e.Detail = err.Error()
    61  		return &e
    62  	}
    63  
    64  	if err := json.Unmarshal(body, &e); err != nil {
    65  		p.Log(r.Request.Context()).Errorf("could not unmarshal API error: %s", err)
    66  		e.Title = fmt.Sprintf("Failed to unmarshal error body. PAPI API failed. Check details for more information.")
    67  		e.Detail = errs.UnescapeContent(string(body))
    68  	}
    69  
    70  	e.StatusCode = r.StatusCode
    71  
    72  	return &e
    73  }
    74  
    75  func (e *Error) Error() string {
    76  	msg, err := json.MarshalIndent(e, "", "\t")
    77  	if err != nil {
    78  		return fmt.Sprintf("error marshaling API error: %s", err)
    79  	}
    80  	return fmt.Sprintf("API error: \n%s", msg)
    81  }
    82  
    83  func (e *ActivationError) Error() string {
    84  	msg, err := json.MarshalIndent(e, "", "\t")
    85  	if err != nil {
    86  		return fmt.Sprintf("error marshaling API error: %s", err)
    87  	}
    88  	return fmt.Sprintf("API error: \n%s", msg)
    89  }
    90  
    91  // Is handles error comparisons
    92  func (e *Error) Is(target error) bool {
    93  	if errors.Is(target, ErrSBDNotEnabled) {
    94  		return e.isErrSBDNotEnabled()
    95  	}
    96  	if errors.Is(target, ErrDefaultCertLimitReached) {
    97  		return e.isErrDefaultCertLimitReached()
    98  	}
    99  	if errors.Is(target, ErrNotFound) {
   100  		return e.isErrNotFound()
   101  	}
   102  
   103  	var t *Error
   104  	if !errors.As(target, &t) {
   105  		return false
   106  	}
   107  
   108  	if e == t {
   109  		return true
   110  	}
   111  
   112  	if e.StatusCode != t.StatusCode {
   113  		return false
   114  	}
   115  
   116  	return e.Error() == t.Error()
   117  }
   118  
   119  // Is handles error comparisons for ActivationError type
   120  func (e *ActivationError) Is(target error) bool {
   121  	if errors.Is(target, ErrMissingComplianceRecord) {
   122  		return e.MessageID == "missing_compliance_record"
   123  	}
   124  
   125  	var t *ActivationError
   126  	if !errors.As(target, &t) {
   127  		return false
   128  	}
   129  
   130  	if e == t {
   131  		return true
   132  	}
   133  
   134  	if e.Status != t.Status {
   135  		return false
   136  	}
   137  
   138  	return e.Error() == t.Error()
   139  }
   140  
   141  func (e *Error) isErrSBDNotEnabled() bool {
   142  	return e.StatusCode == http.StatusForbidden && e.Type == "https://problems.luna.akamaiapis.net/papi/v0/property-version-hostname/default-cert-provisioning-unavailable"
   143  }
   144  
   145  func (e *Error) isErrDefaultCertLimitReached() bool {
   146  	return e.StatusCode == http.StatusTooManyRequests && e.LimitKey == "DEFAULT_CERTS_PER_CONTRACT" && e.Remaining != nil && *e.Remaining == 0
   147  }
   148  
   149  func (e *Error) isErrNotFound() bool {
   150  	return e.StatusCode == http.StatusNotFound
   151  }