Skip to content

Commit

Permalink
Merge pull request #274 from flimzy/RetryAfter
Browse files Browse the repository at this point in the history
Expose RetryAfter value in error
  • Loading branch information
strideynet authored Nov 28, 2024
2 parents 80fbf5a + beecb19 commit 2a7f41f
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
6 changes: 6 additions & 0 deletions spotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ type Error struct {
Message string `json:"message"`
// The HTTP status code.
Status int `json:"status"`
// RetryAfter contains the time before which client should not retry a
// rate-limited request, calculated from the Retry-After header, when present.
RetryAfter time.Time `json:"-"`
}

func (e Error) Error() string {
Expand Down Expand Up @@ -196,6 +199,9 @@ func decodeError(resp *http.Response) error {
e.E.Message = fmt.Sprintf("spotify: unexpected HTTP %d: %s (empty error)",
resp.StatusCode, http.StatusText(resp.StatusCode))
}
if retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After")); retryAfter != 0 {
e.E.RetryAfter = time.Now().Add(time.Duration(retryAfter) * time.Second)
}

return e.E
}
Expand Down
48 changes: 48 additions & 0 deletions spotify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package spotify

import (
"context"
"errors"
"io"
"net/http"
"net/http/httptest"
"os"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -105,6 +107,52 @@ func TestNewReleasesRateLimitExceeded(t *testing.T) {
}
}

func TestRateLimitExceededReportsRetryAfter(t *testing.T) {
t.Parallel()
const retryAfter = 2

handlers := []http.HandlerFunc{
// first attempt fails
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Retry-After", strconv.Itoa(retryAfter))
w.WriteHeader(rateLimitExceededStatusCode)

Check failure on line 118 in spotify_test.go

View workflow job for this annotation

GitHub Actions / unit

undefined: rateLimitExceededStatusCode
_, _ = io.WriteString(w, `{ "error": { "message": "slow down", "status": 429 } }`)
}),
// next attempt succeeds
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f, err := os.Open("test_data/new_releases.txt")
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, err = io.Copy(w, f)
if err != nil {
t.Fatal(err)
}
}),
}

i := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handlers[i](w, r)
i++
}))
defer server.Close()

client := &Client{http: http.DefaultClient, baseURL: server.URL + "/"}
_, err := client.NewReleases(context.Background())
if err == nil {
t.Fatal("expected an error")
}
var spotifyError Error
if !errors.As(err, &spotifyError) {
t.Fatalf("expected a spotify error, got %T", err)
}
if retryAfter*time.Second-time.Until(spotifyError.RetryAfter) > time.Second {
t.Error("expected RetryAfter value")
}
}

func TestClient_Token(t *testing.T) {
// oauth setup for valid test token
config := oauth2.Config{
Expand Down

0 comments on commit 2a7f41f

Please sign in to comment.