Skip to content

Commit

Permalink
stripe-go v71 (#1055)
Browse files Browse the repository at this point in the history
* Make API response accessible on returned API structs (#1054)

* Make API response accessible on returned API structs

Makes an API response struct containing niceties like the raw response
body, status, and request ID accessible via API resource structs
returned from client functions. For example:

    customer, err := customer.New(params)
    fmt.Printf("request ID = %s\n", customer.LastResponse.RequestID)

This is a feature that already exists in other language API libraries
and which is requested occasionally here, usually for various situations
involving more complex usage or desire for better observability.

-- Implementation

We introduce a few new types to make this work:

* `APIResponse`: Represents a response from the Stripe API and includes
  things like request ID, status, and headers. I elected to create my
  own object instead of reusing `http.Response` because it gives us a
  little more flexibility, and hides many of myriad of fields exposed by
  the `http` version, which will hopefully give us a little more API
  stability/forward compatibility.

* `APIResource`: A struct that contains `LastResponse` and is meant to
  represent any type that can we returned from a Stripe API endpoint. A
  coupon is an `APIResource` and so is a list object. This struct is
  embedded in response structs where appropriate across the whole API
  surface area (e.g. `Coupon`, `ListMeta`, etc.).

* `LastResponseGetter`: A very basic interface to an object that looks
  like an `APIResource`. This isn't strictly necessary, but gives us
  slightly more flexibility around the API and makes backward
  compatibility a little bit better for non-standard use cases (see the
  section on that below).

`stripe.Do` and other backend calls all start taking objects which are
`LastResponseGetter` instead of `interface{}`. This provides us with some
type safety around forgetting to include an embedded `APIResource` on
structs that should have it by making the compiler balk.

As `stripe.Do` finishes running a request, it generates an `APIResponse`
object and sets it onto the API resource type it's deserializing and
returning (e.g. a `Coupon`).

Errors also embed `APIResource` and similarly get access to the same set
of fields as response resources, although in their case some of the
fields provided in `APIResponse` are duplicates of what they had
already (see "Caveats" below).

-- Backwards compatibility

This is a minor breaking change in that backend implementations methods
like `Do` now take `LastResponseGetter` instead of `interface{}`, which
is more strict.

The good news though is that:

* Very few users should be using any of these even if they're
  technically public. The resource-specific clients packages tend to do
  all the work.

* Users who are broken should have a very easy time updating code.
  Mostly this will just involve adding `APIResource` to structs that were
  being passed in.

-- Naming

* `APIResponse`: Went with this instead of `StripeResponse` as we see in
  some other libraries because the linter will complain that it
  "stutters" when used outside of the package (meaning, uses the same
  word twice in a row), i.e. `stripe.StripeResponse`. `APIResponse`
  sorts nicely with `APIResource` though, so I think it's okay.

* `LastResponse`: Copied the "last" convention from other API libraries
  like stripe-python.

* `LastResponseGetter`: Given an "-er" name per Go convention around
  small interfaces that are basically one liners -- e.g. `Reader`,
  `Writer, `Formatter`, `CloseNotifier`, etc. I can see the argument
  that this maybe should just be `APIResourceInterface` or something
  like that in case we start adding new things, but I figure at that
  point we can either rename it, or create a parent interface that
  encapsulates it:

    ``` go
    type APIResourceInterface interface {
        LastResponseGetter
    }
    ```

-- Caveats

* We only set the last response for top-level returned objects. For
  example, an `InvoiceItem` is an API resource, but if it's returned
  under an `Invoice`, only `Invoice` has a non-nil `LastResponse`. The
  same applies for all resources under list objects. I figure that doing
  it this way is more performant and makes a little bit more intuitive
  sense. Users should be able to work around it if they need to.

* There is some duplication between `LastResponse` and some other fields
  that already existed on `stripe.Error` because the latter was already
  exposing some of this information, e.g. `RequestID`. I figure this is
  okay: it's nice that `stripe.Error` is a `LastResponseGetter` for
  consistency with other API resources. The duplication is a little
  unfortunate, but not that big of a deal.

* Rename `LastResponseGetter` to `LastResponseSetter` and remove a function

* Update stripe.go

Co-Authored-By: Olivier Bellone <ob@stripe.com>

* Move `APIResource` onto individual list structs instead of having it in `ListMeta`

Co-authored-by: Brandur <brandur@stripe.com>
Co-authored-by: Olivier Bellone <ob@stripe.com>

* Remove all beta features from Issuing APIs

* Multiple breaking API changes

* `PaymentIntent` is now expandable on `Charge`
* `Percentage` was removed as a filter when listing `TaxRate`
* Removed `RenewalInterval` on `SubscriptionSchedule`
* Removed `Country` and `RoutingNumber` from `ChargePaymentMethodDetailsAcssDebit`

* Start using Go Modules

Similar to the original implementation for Go Modules in #712, here we
add a `go.mod` and `go.sum`, then proceed to use Go Modules style
import paths everywhere that include the current major revision.

Unfortunately, this may still cause trouble for Dep users who are trying
to upgrade stripe-go, but the project's now had two years to help its
users with basic Go Module awareness, and has chosen not to do so. It's
received no commits of any kind since August 2019, and therefore would
be considered unmaintained by most definitions. Elsewhere, Go Modules
now seem to be the only and obvious way forward, so we're likely to see
more and more users on them.

`scripts/check_api_clients/main.go` is also updated to be smarter about
breaking down package paths which may now include the major.

[1] golang/dep#1963

* Change v71 back to v70

Move back down to current major version so that we can test that our
release script will bump it to v71 properly when the time comes.

Co-authored-by: Brandur <brandur@stripe.com>
Co-authored-by: Olivier Bellone <ob@stripe.com>
Co-authored-by: Remi Jannel <remi@stripe.com>
  • Loading branch information
3 people committed Apr 17, 2020
2 parents e78d6d6 + f7797f4 commit d5e092a
Show file tree
Hide file tree
Showing 256 changed files with 1,091 additions and 1,228 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ cache:
env:
global:
# If changing this number, please also change it in `testing/testing.go`.
- STRIPE_MOCK_VERSION=0.86.0
- STRIPE_MOCK_VERSION=0.87.0

go:
- "1.9.x"
Expand Down
78 changes: 24 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,8 @@ Then, import it using:

``` go
import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/customer"
)
```

### Go Module Support

The library currently *does not* ship with first-class support for Go
modules. We put in support for it before, but ran into compatibility problems
for existing installations using Dep (see discussion in [closer to the bottom
of this thread][gomodvsdep]), and [reverted support][gomodrevert]. Our current
plan is to wait for better module compatibility in Dep (see a [preliminary
patch here][depgomodsupport]), give the release a little grace time to become
more widely distributed, then bring support back.

For now, require stripe-go in `go.mod` with a version but without a *version
suffix* in the path like so:

``` go
module github.com/my/package

require (
github.com/stripe/stripe-go v70.15.0
)
```

And use the same style of import paths as above:

``` go
import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/customer"
"github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/customer"
)
```

Expand Down Expand Up @@ -139,8 +109,8 @@ To use a key, pass it to `API`'s `Init` function:
```go

import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/client"
"github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/client"
)

stripe := &client.API{}
Expand All @@ -161,8 +131,8 @@ import (
"google.golang.org/appengine"
"google.golang.org/appengine/urlfetch"

"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/client"
"github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/client"
)

func handler(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -196,8 +166,8 @@ client.

```go
import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/$resource$"
"github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/$resource$"
)

// Setup
Expand Down Expand Up @@ -236,8 +206,8 @@ individual key.

```go
import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/client"
"github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/client"
)

// Setup
Expand Down Expand Up @@ -267,19 +237,24 @@ if err := i.Err(); err != nil {
}
```

### Configuring Automatic Retries
### Automatic Retries

The library automatically retries requests on intermittent failures like on a
connection error, timeout, or on certain API responses like a status `409
Conflict`. [Idempotency keys][idempotency-keys] are always added to requests to
make any such subsequent retries safe.

You can enable automatic retries on requests that fail due to a transient
problem by configuring the maximum number of retries:
By default, it will perform up to two retries. That number can be configured
with `MaxNetworkRetries`:

```go
import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/client"
"github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/client"
)

config := &stripe.BackendConfig{
MaxNetworkRetries: 2,
MaxNetworkRetries: stripe.Int64(0), // Zero retries
}

sc := &client.API{}
Expand All @@ -291,15 +266,10 @@ sc.Init("sk_key", &stripe.Backends{
coupon, err := sc.Coupons.New(...)
```

Various errors can trigger a retry, like a connection error or a timeout, and
also certain API responses like HTTP status `409 Conflict`.

[Idempotency keys][idempotency-keys] are added to requests to guarantee that
retries are safe.

### Configuring Logging

Configure logging using the global `DefaultLeveledLogger` variable:
By default, the library logs error messages only (which are sent to `stderr`).
Configure default logging using the global `DefaultLeveledLogger` variable:

```go
stripe.DefaultLeveledLogger = &stripe.LeveledLogger{
Expand Down Expand Up @@ -360,7 +330,7 @@ You can disable this behavior if you prefer:

```go
config := &stripe.BackendConfig{
EnableTelemetry: false,
EnableTelemetry: stripe.Bool(false),
}
```

Expand Down
5 changes: 4 additions & 1 deletion account.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package stripe
import (
"encoding/json"

"github.com/stripe/stripe-go/form"
"github.com/stripe/stripe-go/v70/form"
)

// AccountType is the type of an account.
Expand Down Expand Up @@ -461,6 +461,7 @@ type AccountTOSAcceptance struct {
// Account is the resource representing your Stripe account.
// For more details see https://stripe.com/docs/api/#account.
type Account struct {
APIResource
BusinessProfile *AccountBusinessProfile `json:"business_profile"`
BusinessType AccountBusinessType `json:"business_type"`
Capabilities *AccountCapabilities `json:"capabilities"`
Expand Down Expand Up @@ -505,13 +506,15 @@ func (a *Account) UnmarshalJSON(data []byte) error {

// AccountList is a list of accounts as returned from a list endpoint.
type AccountList struct {
APIResource
ListMeta
Data []*Account `json:"data"`
}

// ExternalAccountList is a list of external accounts that may be either bank
// accounts or cards.
type ExternalAccountList struct {
APIResource
ListMeta

// Values contains any external accounts (bank accounts and/or cards)
Expand Down
4 changes: 2 additions & 2 deletions account/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ package account
import (
"net/http"

stripe "github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/form"
stripe "github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/form"
)

// Client is used to invoke APIs related to accounts.
Expand Down
4 changes: 2 additions & 2 deletions account/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
stripe "github.com/stripe/stripe-go"
_ "github.com/stripe/stripe-go/testing"
stripe "github.com/stripe/stripe-go/v70"
_ "github.com/stripe/stripe-go/v70/testing"
)

func TestAccountDel(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
"github.com/stripe/stripe-go/form"
"github.com/stripe/stripe-go/v70/form"
)

func TestAccountExternalAccountParams_AppendTo(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions accountlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type AccountLinkParams struct {
// AccountLink is the resource representing an account link.
// For more details see https://stripe.com/docs/api/#account_links.
type AccountLink struct {
APIResource
Created int64 `json:"created"`
ExpiresAt int64 `json:"expires_at"`
Object string `json:"object"`
Expand Down
2 changes: 1 addition & 1 deletion accountlink/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package accountlink
import (
"net/http"

stripe "github.com/stripe/stripe-go"
stripe "github.com/stripe/stripe-go/v70"
)

// Client is used to invoke APIs related to account links.
Expand Down
4 changes: 2 additions & 2 deletions accountlink/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
stripe "github.com/stripe/stripe-go"
_ "github.com/stripe/stripe-go/testing"
stripe "github.com/stripe/stripe-go/v70"
_ "github.com/stripe/stripe-go/v70/testing"
)

func TestAccountLinkNew(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions applepaydomain.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type ApplePayDomainParams struct {

// ApplePayDomain is the resource representing a Stripe ApplePayDomain object
type ApplePayDomain struct {
APIResource
Created int64 `json:"created"`
Deleted bool `json:"deleted"`
DomainName string `json:"domain_name"`
Expand All @@ -22,6 +23,7 @@ type ApplePayDomainListParams struct {

// ApplePayDomainList is a list of ApplePayDomains as returned from a list endpoint.
type ApplePayDomainList struct {
APIResource
ListMeta
Data []*ApplePayDomain `json:"data"`
}
4 changes: 2 additions & 2 deletions applepaydomain/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ package applepaydomain
import (
"net/http"

stripe "github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/form"
stripe "github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/form"
)

// Client is used to invoke /apple_pay/domains and Apple Pay domain-related APIs.
Expand Down
4 changes: 2 additions & 2 deletions applepaydomain/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
stripe "github.com/stripe/stripe-go"
_ "github.com/stripe/stripe-go/testing"
stripe "github.com/stripe/stripe-go/v70"
_ "github.com/stripe/stripe-go/v70/testing"
)

func TestApplePayDomainDel(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type BalanceParams struct {
// Balance is the resource representing your Stripe balance.
// For more details see https://stripe.com/docs/api/#balance.
type Balance struct {
APIResource
Available []*Amount `json:"available"`
ConnectReserved []*Amount `json:"connect_reserved"`
Livemode bool `json:"livemode"`
Expand Down
2 changes: 1 addition & 1 deletion balance/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package balance
import (
"net/http"

stripe "github.com/stripe/stripe-go"
stripe "github.com/stripe/stripe-go/v70"
)

// Client is used to invoke /balance and transaction-related APIs.
Expand Down
2 changes: 1 addition & 1 deletion balance/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
_ "github.com/stripe/stripe-go/testing"
_ "github.com/stripe/stripe-go/v70/testing"
)

func TestBalanceGet(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions balancetransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ type BalanceTransactionListParams struct {
// BalanceTransaction is the resource representing the balance transaction.
// For more details see https://stripe.com/docs/api/#balance.
type BalanceTransaction struct {
APIResource
Amount int64 `json:"amount"`
AvailableOn int64 `json:"available_on"`
Created int64 `json:"created"`
Expand All @@ -145,6 +146,7 @@ type BalanceTransaction struct {

// BalanceTransactionList is a list of transactions as returned from a list endpoint.
type BalanceTransactionList struct {
APIResource
ListMeta
Data []*BalanceTransaction `json:"data"`
}
Expand Down
4 changes: 2 additions & 2 deletions balancetransaction/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ package balancetransaction
import (
"net/http"

stripe "github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/form"
stripe "github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/form"
)

// Client is used to invoke /balance_transactions APIs.
Expand Down
4 changes: 2 additions & 2 deletions balancetransaction/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
stripe "github.com/stripe/stripe-go"
_ "github.com/stripe/stripe-go/testing"
stripe "github.com/stripe/stripe-go/v70"
_ "github.com/stripe/stripe-go/v70/testing"
)

func TestBalanceTransactionGet(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion bankaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"strconv"

"github.com/stripe/stripe-go/form"
"github.com/stripe/stripe-go/v70/form"
)

// BankAccountStatus is the list of allowed values for the bank account's status.
Expand Down Expand Up @@ -141,6 +141,7 @@ func (p *BankAccountListParams) AppendTo(body *form.Values, keyParts []string) {

// BankAccount represents a Stripe bank account.
type BankAccount struct {
APIResource
Account *Account `json:"account"`
AccountHolderName string `json:"account_holder_name"`
AccountHolderType BankAccountAccountHolderType `json:"account_holder_type"`
Expand All @@ -160,6 +161,7 @@ type BankAccount struct {

// BankAccountList is a list object for bank accounts.
type BankAccountList struct {
APIResource
ListMeta
Data []*BankAccount `json:"data"`
}
Expand Down
4 changes: 2 additions & 2 deletions bankaccount/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"errors"
"net/http"

stripe "github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/form"
stripe "github.com/stripe/stripe-go/v70"
"github.com/stripe/stripe-go/v70/form"
)

// Client is used to invoke /bank_accounts APIs.
Expand Down
4 changes: 2 additions & 2 deletions bankaccount/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"

assert "github.com/stretchr/testify/require"
stripe "github.com/stripe/stripe-go"
_ "github.com/stripe/stripe-go/testing"
stripe "github.com/stripe/stripe-go/v70"
_ "github.com/stripe/stripe-go/v70/testing"
)

func TestBankAccountDel_ByAccount(t *testing.T) {
Expand Down
Loading

0 comments on commit d5e092a

Please sign in to comment.