Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Context on API requests #477

Merged
merged 3 commits into from
Oct 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions params.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stripe

import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
Expand All @@ -23,6 +24,16 @@ type Params struct {
// Please use StripeAccount instead.
Account string `form:"-"` // Passed as header

// Context used for request. It may carry deadlines, cancelation signals,
// and other request-scoped values across API boundaries and between
// processes.
//
// Note that a cancelled or timed out context does not provide any
// guarantee whether the operation was or was not completed on Stripe's API
// servers. For certainty, you must either retry with the same idempotency
// key or query the state of the API.
Context context.Context `form:"-"`

Exp []string `form:"expand"`
Extra *ExtraValues `form:"*"`

Expand Down Expand Up @@ -58,6 +69,16 @@ func (v ExtraValues) AppendTo(body *form.Values, keyParts []string) {
// ListParams is the structure that contains the common properties
// of any *ListParams structure.
type ListParams struct {
// Context used for request. It may carry deadlines, cancelation signals,
// and other request-scoped values across API boundaries and between
// processes.
//
// Note that a cancelled or timed out context does not provide any
// guarantee whether the operation was or was not completed on Stripe's API
// servers. For certainty, you must either retry with the same idempotency
// key or query the state of the API.
Context context.Context `form:"-"`

End string `form:"ending_before"`
Exp []string `form:"expand"`
Filters Filters `form:"*"`
Expand Down Expand Up @@ -190,6 +211,7 @@ func (p *ListParams) Expand(f string) {
// ListParams is only used to build a set of parameters.
func (p *ListParams) ToParams() *Params {
return &Params{
Context: p.Context,
StripeAccount: p.StripeAccount,
}
}
13 changes: 7 additions & 6 deletions params_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stripe_test

import (
"context"
"testing"

assert "github.com/stretchr/testify/require"
Expand Down Expand Up @@ -213,13 +214,13 @@ func TestListParams_Expand(t *testing.T) {
}

func TestListParams_ToParams(t *testing.T) {
listParams := &stripe.ListParams{StripeAccount: TestMerchantID}
params := listParams.ToParams()

if params.StripeAccount != TestMerchantID {
t.Fatalf("Expected StripeAccount of %v but got %v.",
TestMerchantID, params.StripeAccount)
listParams := &stripe.ListParams{
Context: context.Background(),
StripeAccount: TestMerchantID,
}
params := listParams.ToParams()
assert.Equal(t, listParams.Context, params.Context)
assert.Equal(t, listParams.StripeAccount, params.StripeAccount)
}

func TestParams_SetAccount(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions stripe.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func GetBackend(backend SupportedBackend) Backend {
defer backends.mu.Unlock()
backends.API = &BackendConfiguration{backend, apiURL, httpClient}
return backends.API

case UploadsBackend:
backends.mu.RLock()
ret := backends.Uploads
Expand Down Expand Up @@ -272,6 +273,10 @@ func (s *BackendConfiguration) NewRequest(method, path, key, contentType string,
req.Header.Add("X-Stripe-Client-User-Agent", encodedStripeUserAgent)

if params != nil {
if params.Context != nil {
req = req.WithContext(params.Context)
}

if idempotency := strings.TrimSpace(params.IdempotencyKey); idempotency != "" {
if len(idempotency) > 255 {
return nil, errors.New("Cannot use an IdempotencyKey longer than 255 characters long.")
Expand Down
47 changes: 46 additions & 1 deletion stripe_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stripe_test

import (
"context"
"encoding/json"
"net/http"
"regexp"
Expand All @@ -13,7 +14,7 @@ import (
. "github.com/stripe/stripe-go/testing"
)

func TestCheckinUseBearerAuth(t *testing.T) {
func TestBearerAuth(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opportunistic fix here. "Checkin"'s been stripped everywhere else.

c := &stripe.BackendConfiguration{URL: stripe.APIURL}
key := "apiKey"

Expand All @@ -23,6 +24,50 @@ func TestCheckinUseBearerAuth(t *testing.T) {
assert.Equal(t, "Bearer "+key, req.Header.Get("Authorization"))
}

func TestContext(t *testing.T) {
c := &stripe.BackendConfiguration{URL: stripe.APIURL}
p := &stripe.Params{Context: context.Background()}

req, err := c.NewRequest("", "", "", "", nil, p)
assert.NoError(t, err)

assert.Equal(t, p.Context, req.Context())
}

func TestContext_Cancel(t *testing.T) {
c := &stripe.BackendConfiguration{
HTTPClient: &http.Client{},
URL: stripe.APIURL,
}
ctx, cancel := context.WithCancel(context.Background())
p := &stripe.Params{Context: ctx}

req, err := c.NewRequest("", "", "", "", nil, p)
assert.NoError(t, err)

assert.Equal(t, ctx, req.Context())

// Cancel the context before we even try to start the request. This will
// cause it to immediately return an error and also avoid any kind of race
// condition.
cancel()

var v interface{}
err = c.Do(req, &v)

// Go 1.7 will produce an error message like:
//
// Get https://api.stripe.com/v1/: net/http: request canceled while waiting for connection
//
// 1.8 and later produce something like:
//
// Get https://api.stripe.com/v1/: context canceled
//
// When we drop support for 1.7 we can remove the first case of this
// expression.
assert.Regexp(t, regexp.MustCompile(`(request canceled|context canceled\z)`), err.Error())
}

// TestMultipleAPICalls will fail the test run if a race condition is thrown while running multiple NewRequest calls.
func TestMultipleAPICalls(t *testing.T) {
wg := &sync.WaitGroup{}
Expand Down