1package cloudflare
2
3import (
4	"fmt"
5	"net/http"
6	"strings"
7)
8
9// Error messages
10const (
11	errEmptyCredentials          = "invalid credentials: key & email must not be empty"
12	errEmptyAPIToken             = "invalid credentials: API Token must not be empty"
13	errMakeRequestError          = "error from makeRequest"
14	errUnmarshalError            = "error unmarshalling the JSON response"
15	errUnmarshalErrorBody        = "error unmarshalling the JSON response error body"
16	errRequestNotSuccessful      = "error reported by API"
17	errMissingAccountID          = "account ID is empty and must be provided"
18	errOperationStillRunning     = "bulk operation did not finish before timeout"
19	errOperationUnexpectedStatus = "bulk operation returned an unexpected status"
20	errResultInfo                = "incorrect pagination info (result_info) in responses"
21	errManualPagination          = "unexpected pagination options passed to functions that handle pagination automatically"
22)
23
24// APIRequestError is a type of error raised by API calls made by this library.
25type APIRequestError struct {
26	StatusCode int
27	Errors     []ResponseInfo
28}
29
30func (e APIRequestError) Error() string {
31	errString := ""
32	errString += fmt.Sprintf("HTTP status %d", e.StatusCode)
33
34	if len(e.Errors) > 0 {
35		errString += ": "
36	}
37
38	errMessages := []string{}
39	for _, err := range e.Errors {
40		m := ""
41		if err.Message != "" {
42			m += err.Message
43		}
44
45		if err.Code != 0 {
46			m += fmt.Sprintf(" (%d)", err.Code)
47		}
48
49		errMessages = append(errMessages, m)
50	}
51
52	return errString + strings.Join(errMessages, ", ")
53}
54
55// HTTPStatusCode exposes the HTTP status from the error response encountered.
56func (e APIRequestError) HTTPStatusCode() int {
57	return e.StatusCode
58}
59
60// ErrorMessages exposes the error messages as a slice of strings from the error
61// response encountered.
62func (e *APIRequestError) ErrorMessages() []string {
63	messages := []string{}
64
65	for _, e := range e.Errors {
66		messages = append(messages, e.Message)
67	}
68
69	return messages
70}
71
72// InternalErrorCodes exposes the internal error codes as a slice of int from
73// the error response encountered.
74func (e *APIRequestError) InternalErrorCodes() []int {
75	ec := []int{}
76
77	for _, e := range e.Errors {
78		ec = append(ec, e.Code)
79	}
80
81	return ec
82}
83
84// ServiceError returns a boolean whether or not the raised error was caused by
85// an internal service.
86func (e *APIRequestError) ServiceError() bool {
87	return e.StatusCode >= http.StatusInternalServerError &&
88		e.StatusCode < 600
89}
90
91// ClientError returns a boolean whether or not the raised error was caused by
92// something client side.
93func (e *APIRequestError) ClientError() bool {
94	return e.StatusCode >= http.StatusBadRequest &&
95		e.StatusCode < http.StatusInternalServerError
96}
97
98// ClientRateLimited returns a boolean whether or not the raised error was
99// caused by too many requests from the client.
100func (e *APIRequestError) ClientRateLimited() bool {
101	return e.StatusCode == http.StatusTooManyRequests
102}
103
104// InternalErrorCodeIs returns a boolean whether or not the desired internal
105// error code is present in `e.InternalErrorCodes`.
106func (e *APIRequestError) InternalErrorCodeIs(code int) bool {
107	for _, errCode := range e.InternalErrorCodes() {
108		if errCode == code {
109			return true
110		}
111	}
112
113	return false
114}
115
116// ErrorMessageContains returns a boolean whether or not a substring exists in
117// any of the `e.ErrorMessages` slice entries.
118func (e *APIRequestError) ErrorMessageContains(s string) bool {
119	for _, errMsg := range e.ErrorMessages() {
120		if strings.Contains(errMsg, s) {
121			return true
122		}
123	}
124	return false
125}
126