diff --git a/headers/headers_test.go.bk b/headers/headers_test.go.bk deleted file mode 100644 index 1c19838..0000000 --- a/headers/headers_test.go.bk +++ /dev/null @@ -1,194 +0,0 @@ -// headers/headers_test.go -package headers - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/deploymenttheory/go-api-http-client/authenticationhandler" - "github.com/deploymenttheory/go-api-http-client/version" - "github.com/stretchr/testify/assert" -) - -// TestSetAuthorization verifies that the SetAuthorization method correctly sets -// the "Authorization" header of the HTTP request. The header should be prefixed -// with "Bearer " followed by the token provided by the authTokenHandler. -func TestSetAuthorization(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - - token := "test-token" - authTokenHandler := &authenticationhandler.AuthTokenHandler{Token: token} - - // Create HeaderHandler without a mock logger since logging is not being tested - headerHandler := NewHeaderHandler(req, nil, nil, authTokenHandler) - headerHandler.SetAuthorization() - - expectedHeaderValue := "Bearer " + token - assert.Equal(t, expectedHeaderValue, req.Header.Get("Authorization"), "Authorization header should be correctly set") -} - -// TestSetContentType verifies that the SetContentType method correctly sets -// the "Content-Type" header of the HTTP request. This header should reflect -// the content type passed to the method. -func TestSetContentType(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - - contentType := "application/json" - // Create HeaderHandler without a mock logger since logging is not being tested - headerHandler := NewHeaderHandler(req, nil, nil, nil) - headerHandler.SetContentType(contentType) - - assert.Equal(t, contentType, req.Header.Get("Content-Type"), "Content-Type header should be correctly set") -} - -// TestSetAccept verifies that the SetAccept method correctly sets the "Accept" -// header of the HTTP request. This header indicates the media types that the -// client is willing to receive from the server. -func TestSetAccept(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - - acceptHeader := "application/json" - headerHandler := NewHeaderHandler(req, nil, nil, nil) - headerHandler.SetAccept(acceptHeader) - - assert.Equal(t, acceptHeader, req.Header.Get("Accept"), "Accept header should be correctly set") -} - -// TestSetUserAgent verifies that the SetUserAgent method correctly sets the -// "User-Agent" header of the HTTP request. This header should reflect the user -// agent string passed to the method. -func TestSetUserAgent(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - - userAgent := "MyCustomUserAgent/1.0" - headerHandler := NewHeaderHandler(req, nil, nil, nil) - headerHandler.SetUserAgent(userAgent) - - assert.Equal(t, userAgent, req.Header.Get("User-Agent"), "User-Agent header should be correctly set") -} - -// TestSetCacheControlHeader verifies that the SetCacheControlHeader function -// correctly sets the "Cache-Control" header of the HTTP request. This header -// contains directives for caching mechanisms in requests and responses. -func TestSetCacheControlHeader(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - - cacheControlValue := "no-cache" - SetCacheControlHeader(req, cacheControlValue) - - assert.Equal(t, cacheControlValue, req.Header.Get("Cache-Control"), "Cache-Control header should be correctly set") -} - -func TestSetConditionalHeaders(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - ifModifiedSince := "Wed, 21 Oct 2015 07:28:00 GMT" - ifNoneMatch := `"etag-value"` - - SetConditionalHeaders(req, ifModifiedSince, ifNoneMatch) - - assert.Equal(t, ifModifiedSince, req.Header.Get("If-Modified-Since"), "If-Modified-Since header should be correctly set") - assert.Equal(t, ifNoneMatch, req.Header.Get("If-None-Match"), "If-None-Match header should be correctly set") -} - -func TestSetAcceptEncodingHeader(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - acceptEncodingValue := "gzip, deflate" - - SetAcceptEncodingHeader(req, acceptEncodingValue) - - assert.Equal(t, acceptEncodingValue, req.Header.Get("Accept-Encoding"), "Accept-Encoding header should be correctly set") -} - -func TestSetRefererHeader(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - refererValue := "http://previous-page.com" - - SetRefererHeader(req, refererValue) - - assert.Equal(t, refererValue, req.Header.Get("Referer"), "Referer header should be correctly set") -} - -func TestSetXForwardedForHeader(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - xForwardedForValue := "123.45.67.89" - - SetXForwardedForHeader(req, xForwardedForValue) - - assert.Equal(t, xForwardedForValue, req.Header.Get("X-Forwarded-For"), "X-Forwarded-For header should be correctly set") -} - -func TestSetCustomHeader(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) - headerName := "X-Custom-Header" - headerValue := "CustomValue" - - SetCustomHeader(req, headerName, headerValue) - - assert.Equal(t, headerValue, req.Header.Get(headerName), "Custom header should be correctly set") -} - -// TestSetUserAgentHeader verifies that the SetUserAgentHeader function returns the expected user agent string -func TestSetUserAgentHeader(t *testing.T) { - expectedUserAgent := fmt.Sprintf("%s/%s", version.UserAgentBase, version.SDKVersion) - userAgent := SetUserAgentHeader() - - assert.Equal(t, expectedUserAgent, userAgent, "User agent string should match expected format") -} - -// TestSetRequestHeaders verifies that standard headers, including a custom Authorization header, -// are set correctly on the HTTP request based on headers provided by a mock APIHandler. -// TODO need to implement MockAPIHandler -// func TestSetRequestHeaders(t *testing.T) { -// req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) -// mockAPIHandler := new(MockAPIHandler) // Assume you've implemented MockAPIHandler - -// // Mock APIHandler to return a set of standard headers -// standardHeaders := map[string]string{ -// "Content-Type": "application/json", -// "Custom-Header": "custom-value", -// } -// mockAPIHandler.On("GetAPIRequestHeaders", "test-endpoint").Return(standardHeaders) - -// authTokenHandler := &authenticationhandler.AuthTokenHandler{Token: "test-token"} -// headerHandler := NewHeaderHandler(req, nil, nil, authTokenHandler) -// headerHandler.SetRequestHeaders("test-endpoint") - -// assert.Equal(t, "Bearer test-token", req.Header.Get("Authorization"), "Authorization header should be correctly set with Bearer token") -// assert.Equal(t, "application/json", req.Header.Get("Content-Type"), "Content-Type header should be set to application/json") -// assert.Equal(t, "custom-value", req.Header.Get("Custom-Header"), "Custom-Header should be set to custom-value") - -// mockAPIHandler.AssertExpectations(t) -// } - -// TestHeadersToString verifies that the HeadersToString function correctly formats -// HTTP headers into a string, with each header on a new line. -func TestHeadersToString(t *testing.T) { - headers := http.Header{ - "Content-Type": []string{"application/json"}, - "Accept": []string{"application/xml"}, - } - - expected := "Content-Type: application/json\nAccept: application/xml" - result := HeadersToString(headers) - - assert.Equal(t, expected, result, "Headers should be correctly formatted into a string") -} - -// TestCheckDeprecationHeader verifies that the CheckDeprecationHeader function -// can detect the presence of a Deprecation header in the HTTP response. -// TODO need to implement MockLogger -// func TestCheckDeprecationHeader(t *testing.T) { -// resp := &http.Response{ -// Header: make(http.Header), -// } -// deprecationDate := "Fri, 01 Jan 2100 00:00:00 GMT" -// resp.Header.Set("Deprecation", deprecationDate) - -// // Normally, you would check for a log entry here, but we're skipping logging. -// // This test will simply ensure the function can run without error. -// CheckDeprecationHeader(resp, nil) // Passing nil as logger since we're not testing logging - -// assert.Equal(t, deprecationDate, resp.Header.Get("Deprecation"), "Deprecation header should be detected") -// } diff --git a/headers/redact/redact.go b/headers/redact/redact.go deleted file mode 100644 index 9130020..0000000 --- a/headers/redact/redact.go +++ /dev/null @@ -1,18 +0,0 @@ -// headers/redact/redact.go -package redact - -// RedactSensitiveHeaderData redacts sensitive data based on the hideSensitiveData flag. -func RedactSensitiveHeaderData(hideSensitiveData bool, key, value string) string { - if hideSensitiveData { - // Define sensitive data keys that should be redacted. - sensitiveKeys := map[string]bool{ - "AccessToken": true, - "Authorization": true, - } - - if _, found := sensitiveKeys[key]; found { - return "REDACTED" - } - } - return value -} diff --git a/headers/redact/redact_test.go b/headers/redact/redact_test.go deleted file mode 100644 index f64b0ee..0000000 --- a/headers/redact/redact_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// headers/redact/redact_test.go -package redact - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// TestRedactSensitiveHeaderData tests the RedactSensitiveHeaderData function to ensure it correctly redacts sensitive data. -func TestRedactSensitiveHeaderData(t *testing.T) { - cases := []struct { - name string - hideSensitiveData bool - key string - value string - expected string - }{ - {"Sensitive Key With Redaction", true, "AccessToken", "some-sensitive-token", "REDACTED"}, - {"Sensitive Key Without Redaction", false, "AccessToken", "some-sensitive-token", "some-sensitive-token"}, - {"Non-Sensitive Key With Redaction", true, "User-Agent", "MyCustomAgent", "MyCustomAgent"}, - {"Non-Sensitive Key Without Redaction", false, "User-Agent", "MyCustomAgent", "MyCustomAgent"}, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - result := RedactSensitiveHeaderData(tc.hideSensitiveData, tc.key, tc.value) - assert.Equal(t, tc.expected, result, "Redacted value should match the expected outcome") - }) - } -} diff --git a/helpers/helpers_test.go.bk b/helpers/helpers_test.go.bk deleted file mode 100644 index c2f5e41..0000000 --- a/helpers/helpers_test.go.bk +++ /dev/null @@ -1,46 +0,0 @@ -// helpers/helpers.go -package helpers - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -// TestParseISO8601Date tests the ParseISO8601Date function with various date strings -func TestParseISO8601Date(t *testing.T) { - tests := []struct { - dateStr string - expectErr bool - expectedTime time.Time // Add an expectedTime field for successful parsing - }{ - { - dateStr: "2023-01-02T15:04:05Z", - expectErr: false, - expectedTime: time.Date(2023, time.January, 2, 15, 4, 5, 0, time.UTC), - }, - { - dateStr: "2023-01-02T15:04:05-07:00", - expectErr: false, - expectedTime: time.Date(2023, time.January, 2, 15, 4, 5, 0, time.FixedZone("", -7*3600)), - }, - { - dateStr: "invalid-date", - expectErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.dateStr, func(t *testing.T) { - result, err := ParseISO8601Date(tt.dateStr) - - if tt.expectErr { - assert.Error(t, err, "Expected an error for date string: "+tt.dateStr) - } else { - assert.NoError(t, err, "Did not expect an error for date string: "+tt.dateStr) - assert.True(t, result.Equal(tt.expectedTime), "Parsed time should match expected time") - } - }) - } -} diff --git a/httpclient/httpmethod.go b/httpclient/methods.go similarity index 100% rename from httpclient/httpmethod.go rename to httpclient/methods.go diff --git a/httpclient/request.go b/httpclient/request.go index 1c751d1..7eda666 100644 --- a/httpclient/request.go +++ b/httpclient/request.go @@ -10,7 +10,6 @@ import ( "github.com/deploymenttheory/go-api-http-client/ratehandler" "github.com/deploymenttheory/go-api-http-client/response" - "github.com/deploymenttheory/go-api-http-client/status" "go.uber.org/zap" ) @@ -71,7 +70,7 @@ func (c *Client) DoRequest(method, endpoint string, body, out interface{}) (*htt } if IsIdempotentHTTPMethod(method) { - return c.executeRequestWithRetries(method, endpoint, body, out) + return c.executeRequestWithRetries(method, endpoint, body) } else if !IsIdempotentHTTPMethod(method) { return c.executeRequest(method, endpoint, body, out) } else { @@ -113,8 +112,7 @@ func (c *Client) DoRequest(method, endpoint string, body, out interface{}) (*htt // operations. // - The retry mechanism employs exponential backoff with jitter to mitigate the impact of retries on the server. // endregion -func (c *Client) executeRequestWithRetries(method, endpoint string, body, out interface{}) (*http.Response, error) { - // TODO review refactor executeRequestWithRetries +func (c *Client) executeRequestWithRetries(method, endpoint string, body interface{}) (*http.Response, error) { ctx := context.Background() totalRetryDeadline := time.Now().Add(c.config.TotalRetryDuration) @@ -124,28 +122,38 @@ func (c *Client) executeRequestWithRetries(method, endpoint string, body, out in c.Sugar.Debug("Executing request with retries", zap.String("method", method), zap.String("endpoint", endpoint)) + // TODO removed the blocked comments + // Timer for time.Now().Before(totalRetryDeadline) { - res, requestErr := c.doRequest(ctx, method, endpoint, body) + + // Resp + resp, requestErr := c.doRequest(ctx, method, endpoint, body) if requestErr != nil { return nil, requestErr } - resp = res + // Success if resp.StatusCode >= 200 && resp.StatusCode < 400 { if resp.StatusCode >= 300 { c.Sugar.Warn("Redirect response received", zap.Int("status_code", resp.StatusCode), zap.String("location", resp.Header.Get("Location"))) } - return resp, response.HandleAPISuccessResponse(resp, out, c.Sugar) + c.Sugar.Infof("%s request successful at %v", resp.Request.Method, resp.Request.URL) + return resp, nil } - statusMessage := status.TranslateStatusCode(resp) + statusMessage := http.StatusText(resp.StatusCode) + if statusMessage == "" { + statusMessage = "unknown response code" + } - if resp != nil && status.IsNonRetryableStatusCode(resp) { + // Non Retry + if IsNonRetryableStatusCode(resp) { c.Sugar.Warn("Non-retryable error received", zap.Int("status_code", resp.StatusCode), zap.String("status_message", statusMessage)) return resp, response.HandleAPIErrorResponse(resp, c.Sugar) } - if status.IsRateLimitError(resp) { + // Rate limited + if resp.StatusCode == http.StatusTooManyRequests { waitDuration := ratehandler.ParseRateLimitHeaders(resp, c.Sugar) if waitDuration > 0 { c.Sugar.Warn("Rate limit encountered, waiting before retrying", zap.Duration("waitDuration", waitDuration)) @@ -154,7 +162,8 @@ func (c *Client) executeRequestWithRetries(method, endpoint string, body, out in } } - if status.IsTransientError(resp) { + // Transient + if IsTransientError(resp) { retryCount++ if retryCount > c.config.MaxRetryAttempts { c.Sugar.Warn("Max retry attempts reached", zap.String("method", method), zap.String("endpoint", endpoint)) @@ -166,12 +175,14 @@ func (c *Client) executeRequestWithRetries(method, endpoint string, body, out in continue } - if !status.IsRetryableStatusCode(resp.StatusCode) { + // Retryable + if !IsRetryableStatusCode(resp.StatusCode) { if apiErr := response.HandleAPIErrorResponse(resp, c.Sugar); apiErr != nil { err = apiErr } break } + } if err != nil { diff --git a/httpclient/status.go b/httpclient/status.go new file mode 100644 index 0000000..98848ac --- /dev/null +++ b/httpclient/status.go @@ -0,0 +1,62 @@ +package httpclient + +import "net/http" + +// IsNonRetryableStatusCode checks if the provided response indicates a non-retryable error. +func IsNonRetryableStatusCode(resp *http.Response) bool { + nonRetryableStatusCodes := map[int]bool{ + http.StatusBadRequest: true, // 400 - Bad Request + http.StatusUnauthorized: true, // 401 - Unauthorized + http.StatusPaymentRequired: true, // 402 - Payment Required + http.StatusForbidden: true, // 403 - Forbidden + http.StatusNotFound: true, // 404 - Not Found + http.StatusMethodNotAllowed: true, // 405 - Method Not Allowed + http.StatusNotAcceptable: true, // 406 - Not Acceptable + http.StatusProxyAuthRequired: true, // 407 - Proxy Authentication Required + http.StatusConflict: true, // 409 - Conflict + http.StatusGone: true, // 410 - Gone + http.StatusLengthRequired: true, // 411 - Length Required + http.StatusPreconditionFailed: true, // 412 - Precondition Failed + http.StatusRequestEntityTooLarge: true, // 413 - Request Entity Too Large + http.StatusRequestURITooLong: true, // 414 - Request-URI Too Long + http.StatusUnsupportedMediaType: true, // 415 - Unsupported Media Type + http.StatusRequestedRangeNotSatisfiable: true, // 416 - Requested Range Not Satisfiable + http.StatusExpectationFailed: true, // 417 - Expectation Failed + http.StatusUnprocessableEntity: true, // 422 - Unprocessable Entity + http.StatusLocked: true, // 423 - Locked + http.StatusFailedDependency: true, // 424 - Failed Dependency + http.StatusUpgradeRequired: true, // 426 - Upgrade Required + http.StatusPreconditionRequired: true, // 428 - Precondition Required + http.StatusRequestHeaderFieldsTooLarge: true, // 431 - Request Header Fields Too Large + http.StatusUnavailableForLegalReasons: true, // 451 - Unavailable For Legal Reasons + } + + _, isNonRetryable := nonRetryableStatusCodes[resp.StatusCode] + return isNonRetryable +} + +// IsTransientError checks if an error or HTTP response indicates a transient error. +func IsTransientError(resp *http.Response) bool { + transientStatusCodes := map[int]bool{ + http.StatusInternalServerError: true, // 500 Internal Server Error + http.StatusBadGateway: true, // 502 Bad Gateway + http.StatusServiceUnavailable: true, // 503 Service Unavailable + http.StatusGatewayTimeout: true, // 504 - Gateway Timeout + } + return resp != nil && transientStatusCodes[resp.StatusCode] +} + +// IsRetryableStatusCode checks if the provided HTTP status code is considered retryable. +func IsRetryableStatusCode(statusCode int) bool { + retryableStatusCodes := map[int]bool{ + http.StatusRequestTimeout: true, // 408 - Request Timeout + http.StatusTooManyRequests: true, // 429 + http.StatusInternalServerError: true, // 500 + http.StatusBadGateway: true, // 502 + http.StatusServiceUnavailable: true, // 503 + http.StatusGatewayTimeout: true, // 504 + } + + _, retryable := retryableStatusCodes[statusCode] + return retryable +} diff --git a/status/status.go b/status/status.go index c357c50..79c0846 100644 --- a/status/status.go +++ b/status/status.go @@ -3,76 +3,9 @@ package status import ( - "fmt" "net/http" ) -// TranslateStatusCode provides a human-readable message for HTTP status codes. -func TranslateStatusCode(resp *http.Response) string { - - if resp == nil { - return "No status code received, possible network or connection error." - } - - messages := map[int]string{ - // Successful responses (200-299) - http.StatusOK: "Request successful.", - http.StatusCreated: "Request to create or update resource successful.", - http.StatusAccepted: "The request was accepted for processing, but the processing has not completed.", - http.StatusNoContent: "Request successful. No content to send for this request.", - - // Redirect status codes (300-399) - http.StatusMovedPermanently: "Moved Permanently. The requested resource has been assigned a new permanent URI. Future references should use the returned URI.", - http.StatusFound: "Found. The requested resource resides temporarily under a different URI. The client should use the Request-URI for future requests.", - http.StatusSeeOther: "See Other. The response to the request can be found under a different URI. A GET method should be used to retrieve the resource.", - http.StatusTemporaryRedirect: "Temporary Redirect. The requested resource resides temporarily under a different URI. The request method should not change.", - http.StatusPermanentRedirect: "Permanent Redirect. The requested resource has been permanently moved to a new URI. The request method should not change.", - - // Client error responses (400-499) - http.StatusBadRequest: "Bad request. Verify the syntax of the request.", - http.StatusUnauthorized: "Authentication failed. Verify the credentials being used for the request.", - http.StatusPaymentRequired: "Payment required. Access to the requested resource requires payment.", - http.StatusForbidden: "Invalid permissions. Verify the account has the proper permissions for the resource.", - http.StatusNotFound: "Resource not found. Verify the URL path is correct.", - http.StatusMethodNotAllowed: "Method not allowed. The method specified is not allowed for the resource.", - http.StatusNotAcceptable: "Not acceptable. The server cannot produce a response matching the list of acceptable values.", - http.StatusProxyAuthRequired: "Proxy authentication required. You must authenticate with a proxy server before this request can be served.", - http.StatusRequestTimeout: "Request timeout. The server timed out waiting for the request.", - http.StatusConflict: "Conflict. The request could not be processed because of conflict in the request.", - http.StatusGone: "Gone. The resource requested is no longer available and will not be available again.", - http.StatusLengthRequired: "Length required. The request did not specify the length of its content, which is required by the requested resource.", - http.StatusPreconditionFailed: "Precondition failed. The server does not meet one of the preconditions specified in the request.", - http.StatusRequestEntityTooLarge: "Payload too large. The request is larger than the server is willing or able to process.", - http.StatusRequestURITooLong: "Request-URI too long. The URI provided was too long for the server to process.", - http.StatusUnsupportedMediaType: "Unsupported media type. The request entity has a media type which the server or resource does not support.", - http.StatusRequestedRangeNotSatisfiable: "Requested range not satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion.", - http.StatusExpectationFailed: "Expectation failed. The server cannot meet the requirements of the Expect request-header field.", - http.StatusUnprocessableEntity: "Unprocessable entity. The server understands the content type and syntax of the request but was unable to process the contained instructions.", - http.StatusLocked: "Locked. The resource that is being accessed is locked.", - http.StatusFailedDependency: "Failed dependency. The request failed because it depended on another request and that request failed.", - http.StatusUpgradeRequired: "Upgrade required. The client should switch to a different protocol.", - http.StatusPreconditionRequired: "Precondition required. The server requires that the request be conditional.", - http.StatusTooManyRequests: "Too many requests. The user has sent too many requests in a given amount of time.", - http.StatusRequestHeaderFieldsTooLarge: "Request header fields too large. The server is unwilling to process the request because its header fields are too large.", - http.StatusUnavailableForLegalReasons: "Unavailable for legal reasons. The server is denying access to the resource as a consequence of a legal demand.", - - // Server error responses (500-599) - http.StatusInternalServerError: "Internal server error. The server encountered an unexpected condition that prevented it from fulfilling the request.", - http.StatusNotImplemented: "Not implemented. The server does not support the functionality required to fulfill the request.", - http.StatusBadGateway: "Bad gateway. The server received an invalid response from the upstream server while trying to fulfill the request.", - http.StatusServiceUnavailable: "Service unavailable. The server is currently unable to handle the request due to temporary overloading or maintenance.", - http.StatusGatewayTimeout: "Gateway timeout. The server did not receive a timely response from the upstream server.", - http.StatusHTTPVersionNotSupported: "HTTP version not supported. The server does not support the HTTP protocol version used in the request.", - http.StatusNetworkAuthenticationRequired: "Network authentication required. The client needs to authenticate to gain network access.", - } - - // Lookup and return the message for the given status code - if message, exists := messages[resp.StatusCode]; exists { - return message - } - return fmt.Sprintf("Unknown status code: %d", resp.StatusCode) -} - // IsRedirectStatusCode checks if the provided HTTP status code is one of the redirect codes. // Redirect status codes instruct the client to make a new request to a different URI, as defined in the response's Location header. // @@ -109,7 +42,6 @@ func IsPermanentRedirect(statusCode int) bool { // IsNonRetryableStatusCode checks if the provided response indicates a non-retryable error. func IsNonRetryableStatusCode(resp *http.Response) bool { - // Expanded list of non-retryable HTTP status codes nonRetryableStatusCodes := map[int]bool{ http.StatusBadRequest: true, // 400 - Bad Request http.StatusUnauthorized: true, // 401 - Unauthorized @@ -141,15 +73,6 @@ func IsNonRetryableStatusCode(resp *http.Response) bool { return isNonRetryable } -// IsRateLimitError checks if the provided response indicates a rate limit error. -func IsRateLimitError(resp *http.Response) bool { - if resp == nil { - // If the response is nil, it cannot be a rate limit error. - return false - } - return resp.StatusCode == http.StatusTooManyRequests -} - // IsTransientError checks if an error or HTTP response indicates a transient error. func IsTransientError(resp *http.Response) bool { transientStatusCodes := map[int]bool{ diff --git a/version/version.go b/version/version.go deleted file mode 100644 index 97e366e..0000000 --- a/version/version.go +++ /dev/null @@ -1,7 +0,0 @@ -// version.go -package version - -const ( - SDKVersion = "0.1.21" - UserAgentBase = "go-api-http-client" -)