From 6ec9ec9a976c83974b0b70d6fedb47de555bfe45 Mon Sep 17 00:00:00 2001 From: Hirokazu Miyaji Date: Sun, 27 Jun 2021 17:54:53 +0900 Subject: [PATCH] feat: introduce context for observability --- attribution.go | 6 ++++-- attribution_test.go | 3 ++- client.go | 21 ++++++++++++++++----- google.go | 18 +++++++++++++----- google_test.go | 5 +++-- offerings.go | 10 ++++++---- offerings_test.go | 5 +++-- promotions.go | 19 ++++++++++++++----- promotions_test.go | 5 +++-- purchases.go | 11 +++++++++-- purchases_test.go | 5 +++-- subscribers.go | 21 +++++++++++++-------- subscribers_test.go | 9 +++++---- 13 files changed, 94 insertions(+), 44 deletions(-) diff --git a/attribution.go b/attribution.go index 88b4e27..cba98d1 100644 --- a/attribution.go +++ b/attribution.go @@ -1,5 +1,7 @@ package revenuecat +import "context" + // Network represents a predefined attribution channel. type Network int @@ -21,7 +23,7 @@ type AttributionData struct { // AddUserAttribution attaches attribution data to a subscriber from specific supported networks. // https://docs.revenuecat.com/reference#subscribersattribution -func (c *Client) AddUserAttribution(userID string, network Network, data AttributionData) error { +func (c *Client) AddUserAttribution(ctx context.Context, userID string, network Network, data AttributionData) error { var resp struct { Subscriber Subscriber `json:"subscriber"` } @@ -34,5 +36,5 @@ func (c *Client) AddUserAttribution(userID string, network Network, data Attribu Network: network, } - return c.call("POST", "subscribers/"+userID+"/attribution", req, "", &resp) + return c.call(ctx, "POST", "subscribers/"+userID+"/attribution", req, "", &resp) } diff --git a/attribution_test.go b/attribution_test.go index e2672bf..f194dd0 100644 --- a/attribution_test.go +++ b/attribution_test.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "testing" ) @@ -9,7 +10,7 @@ func TestAddUserAttribution(t *testing.T) { rc := New("apikey") rc.http = cl - err := rc.AddUserAttribution("123", Facebook, AttributionData{IDFA: "test"}) + err := rc.AddUserAttribution(context.Background(), "123", Facebook, AttributionData{IDFA: "test"}) if err != nil { t.Errorf("error: %v", err) } diff --git a/client.go b/client.go index 2bb8c42..57a224a 100644 --- a/client.go +++ b/client.go @@ -2,6 +2,7 @@ package revenuecat import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -23,17 +24,26 @@ type doer interface { // New returns a new *Client for the provided API key. // For more information on authentication, see https://docs.revenuecat.com/docs/authentication. func New(apiKey string) *Client { - return &Client{ - apiKey: apiKey, - apiURL: "https://api.revenuecat.com/v1/", - http: &http.Client{ + return NewWithClient( + apiKey, + &http.Client{ // Set a long timeout here since calls to Apple are probably invloved. Timeout: 10 * time.Second, }, + ) +} + +// NewWithClient returns a new *Client for the provided API key and http.Client. +// For more information on authentication, see https://docs.revenuecat.com/docs/authentication. +func NewWithClient(apiKey string, client *http.Client) *Client { + return &Client{ + apiKey: apiKey, + apiURL: "https://api.revenuecat.com/v1/", + http: client, } } -func (c *Client) call(method, path string, reqBody interface{}, platform string, respBody interface{}) error { +func (c *Client) call(ctx context.Context, method, path string, reqBody interface{}, platform string, respBody interface{}) error { var reqBodyJSON io.Reader if reqBody != nil { js, err := json.Marshal(reqBody) @@ -51,6 +61,7 @@ func (c *Client) call(method, path string, reqBody interface{}, platform string, if platform != "" { req.Header.Add("X-Platform", platform) } + req = req.WithContext(ctx) resp, err := c.http.Do(req) if err != nil { diff --git a/google.go b/google.go index cb26d37..b6599de 100644 --- a/google.go +++ b/google.go @@ -1,21 +1,29 @@ package revenuecat -import "time" +import ( + "context" + "time" +) // RefundGoogleSubscription immediately revokes access to a Google Subscription and issues a refund for the last purchase. // https://docs.revenuecat.com/reference#revoke-a-google-subscription -func (c *Client) RefundGoogleSubscription(userID string, id string) (Subscriber, error) { +func (c *Client) RefundGoogleSubscription(ctx context.Context, userID string, id string) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } - err := c.call("POST", "subscribers/"+userID+"/subscriptions/"+id+"/revoke", nil, "", &resp) + err := c.call(ctx, "POST", "subscribers/"+userID+"/subscriptions/"+id+"/revoke", nil, "", &resp) return resp.Subscriber, err } // DeferGoogleSubscription defers the purchase of a Google Subscription to a later date. // https://docs.revenuecat.com/reference#defer-a-google-subscription -func (c *Client) DeferGoogleSubscription(userID string, id string, nextExpiry time.Time) (Subscriber, error) { +func (c *Client) DeferGoogleSubscription( + ctx context.Context, + userID string, + id string, + nextExpiry time.Time, +) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } @@ -26,6 +34,6 @@ func (c *Client) DeferGoogleSubscription(userID string, id string, nextExpiry ti ExpiryTime: toMilliseconds(nextExpiry), } - err := c.call("POST", "subscribers/"+userID+"/subscriptions/"+id+"/defer", req, "", &resp) + err := c.call(ctx, "POST", "subscribers/"+userID+"/subscriptions/"+id+"/defer", req, "", &resp) return resp.Subscriber, err } diff --git a/google_test.go b/google_test.go index 984247a..f3e45ee 100644 --- a/google_test.go +++ b/google_test.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "testing" ) @@ -9,7 +10,7 @@ func TestRefundGoogleSubscription(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.RefundGoogleSubscription("123", "sub") + _, err := rc.RefundGoogleSubscription(context.Background(), "123", "sub") if err != nil { t.Errorf("error: %v", err) } @@ -23,7 +24,7 @@ func TestDeferGoogleSubscription(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.DeferGoogleSubscription("123", "sub", staticTime(t, "2020-01-15 23:54:17")) + _, err := rc.DeferGoogleSubscription(context.Background(), "123", "sub", staticTime(t, "2020-01-15 23:54:17")) if err != nil { t.Errorf("error: %v", err) } diff --git a/offerings.go b/offerings.go index eebab5f..21d29a0 100644 --- a/offerings.go +++ b/offerings.go @@ -1,21 +1,23 @@ package revenuecat +import "context" + // OverrideOffering overrides the current Offering for a specific user. // https://docs.revenuecat.com/reference#override-offering -func (c *Client) OverrideOffering(userID string, offeringUUID string) (Subscriber, error) { +func (c *Client) OverrideOffering(ctx context.Context, userID string, offeringUUID string) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } - err := c.call("POST", "subscribers/"+userID+"/offerings/"+offeringUUID+"/override", nil, "", &resp) + err := c.call(ctx, "POST", "subscribers/"+userID+"/offerings/"+offeringUUID+"/override", nil, "", &resp) return resp.Subscriber, err } // DeleteOfferingOverride reset the offering overrides back to the current offering for a specific user. // https://docs.revenuecat.com/reference#delete-offering-override -func (c *Client) DeleteOfferingOverride(userID string) (Subscriber, error) { +func (c *Client) DeleteOfferingOverride(ctx context.Context, userID string) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } - err := c.call("DELETE", "subscribers/"+userID+"/offerings/override", nil, "", &resp) + err := c.call(ctx, "DELETE", "subscribers/"+userID+"/offerings/override", nil, "", &resp) return resp.Subscriber, err } diff --git a/offerings_test.go b/offerings_test.go index ced5449..8598688 100644 --- a/offerings_test.go +++ b/offerings_test.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "testing" ) @@ -9,7 +10,7 @@ func TestOverrideOffering(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.OverrideOffering("123", "testUUID") + _, err := rc.OverrideOffering(context.Background(), "123", "testUUID") if err != nil { t.Errorf("error: %v", err) } @@ -23,7 +24,7 @@ func TestDeleteOfferingOverride(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.DeleteOfferingOverride("123") + _, err := rc.DeleteOfferingOverride(context.Background(), "123") if err != nil { t.Errorf("error: %v", err) } diff --git a/promotions.go b/promotions.go index fd9723c..1998f83 100644 --- a/promotions.go +++ b/promotions.go @@ -1,10 +1,19 @@ package revenuecat -import "time" +import ( + "context" + "time" +) // GrantEntitlement grants a user a promotional entitlement. // https://docs.revenuecat.com/reference#grant-a-promotional-entitlement -func (c *Client) GrantEntitlement(userID string, id string, duration Duration, startTime time.Time) (Subscriber, error) { +func (c *Client) GrantEntitlement( + ctx context.Context, + userID string, + id string, + duration Duration, + startTime time.Time, +) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } @@ -20,17 +29,17 @@ func (c *Client) GrantEntitlement(userID string, id string, duration Duration, s req.StartTime = toMilliseconds(startTime) } - err := c.call("POST", "subscribers/"+userID+"/entitlements/"+id+"/promotional", req, "", &resp) + err := c.call(ctx, "POST", "subscribers/"+userID+"/entitlements/"+id+"/promotional", req, "", &resp) return resp.Subscriber, err } // RevokeEntitlement revokes all promotional entitlements for a given entitlement identifier and app user ID. // https://docs.revenuecat.com/reference#revoke-promotional-entitlements -func (c *Client) RevokeEntitlement(userID string, id string) (Subscriber, error) { +func (c *Client) RevokeEntitlement(ctx context.Context, userID string, id string) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } - err := c.call("POST", "subscribers/"+userID+"/entitlements/"+id+"/revoke_promotionals", nil, "", &resp) + err := c.call(ctx, "POST", "subscribers/"+userID+"/entitlements/"+id+"/revoke_promotionals", nil, "", &resp) return resp.Subscriber, err } diff --git a/promotions_test.go b/promotions_test.go index 338119b..669b754 100644 --- a/promotions_test.go +++ b/promotions_test.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "testing" ) @@ -9,7 +10,7 @@ func TestGrantEntitlement(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.GrantEntitlement("123", "all", ThreeMonth, staticTime(t, "2020-01-15 23:54:17")) + _, err := rc.GrantEntitlement(context.Background(), "123", "all", ThreeMonth, staticTime(t, "2020-01-15 23:54:17")) if err != nil { t.Errorf("error: %v", err) } @@ -24,7 +25,7 @@ func TestRevokeEntitlement(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.RevokeEntitlement("123", "all") + _, err := rc.RevokeEntitlement(context.Background(), "123", "all") if err != nil { t.Errorf("error: %v", err) } diff --git a/purchases.go b/purchases.go index d6e3ce6..bf8908c 100644 --- a/purchases.go +++ b/purchases.go @@ -1,5 +1,7 @@ package revenuecat +import "context" + // CreatePurchaseOptions holds the optional values for creating a purchase. // https://docs.revenuecat.com/reference#receipts type CreatePurchaseOptions struct { @@ -16,7 +18,12 @@ type CreatePurchaseOptions struct { // CreatePurchase records a purchase for a user from iOS, Android, or Stripe and will create a user if they don't already exist. // https://docs.revenuecat.com/reference#receipts -func (c *Client) CreatePurchase(userID string, receipt string, opt *CreatePurchaseOptions) (Subscriber, error) { +func (c *Client) CreatePurchase( + ctx context.Context, + userID string, + receipt string, + opt *CreatePurchaseOptions, +) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } @@ -36,6 +43,6 @@ func (c *Client) CreatePurchase(userID string, receipt string, opt *CreatePurcha platform = opt.Platform } - err := c.call("POST", "receipts", req, platform, &resp) + err := c.call(ctx, "POST", "receipts", req, platform, &resp) return resp.Subscriber, err } diff --git a/purchases_test.go b/purchases_test.go index 5f49d1e..85a73e8 100644 --- a/purchases_test.go +++ b/purchases_test.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "testing" ) @@ -9,7 +10,7 @@ func TestCreatePurchaseWithoutOpts(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.CreatePurchase("123", "testreceipt", nil) + _, err := rc.CreatePurchase(context.Background(), "123", "testreceipt", nil) if err != nil { t.Errorf("error: %v", err) } @@ -32,7 +33,7 @@ func TestCreatePurchaseWithOpts(t *testing.T) { IsRestore: true, } - _, err := rc.CreatePurchase("123", "testreceipt", opt) + _, err := rc.CreatePurchase(context.Background(), "123", "testreceipt", opt) if err != nil { t.Errorf("error: %v", err) } diff --git a/subscribers.go b/subscribers.go index 9d317ad..4f543f9 100644 --- a/subscribers.go +++ b/subscribers.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "encoding/json" "time" ) @@ -83,36 +84,40 @@ func (s Subscriber) IsEntitledTo(entitlement string) bool { // GetSubscriber gets the latest subscriber info or creates one if it doesn't exist. // https://docs.revenuecat.com/reference#subscribers -func (c *Client) GetSubscriber(userID string) (Subscriber, error) { - return c.GetSubscriberWithPlatform(userID, "") +func (c *Client) GetSubscriber(ctx context.Context, userID string) (Subscriber, error) { + return c.GetSubscriberWithPlatform(ctx, userID, "") } // GetSubscriberWithPlatform gets the latest subscriber info or creates one if it doesn't exist, updating the subscriber record's last_seen // value for the platform provided. // https://docs.revenuecat.com/reference#subscribers -func (c *Client) GetSubscriberWithPlatform(userID string, platform string) (Subscriber, error) { +func (c *Client) GetSubscriberWithPlatform(ctx context.Context, userID string, platform string) (Subscriber, error) { var resp struct { Subscriber Subscriber `json:"subscriber"` } - err := c.call("GET", "subscribers/"+userID, nil, platform, &resp) + err := c.call(ctx, "GET", "subscribers/"+userID, nil, platform, &resp) return resp.Subscriber, err } // UpdateSubscriberAttributes updates subscriber attributes for a user. // https://docs.revenuecat.com/reference#update-subscriber-attributes -func (c *Client) UpdateSubscriberAttributes(userID string, attributes map[string]SubscriberAttribute) error { +func (c *Client) UpdateSubscriberAttributes( + ctx context.Context, + userID string, + attributes map[string]SubscriberAttribute, +) error { req := struct { Attributes map[string]SubscriberAttribute `json:"attributes"` }{ Attributes: attributes, } - return c.call("POST", "subscribers/"+userID+"/attributes", req, "", nil) + return c.call(ctx, "POST", "subscribers/"+userID+"/attributes", req, "", nil) } // DeleteSubscriber permanently deletes a subscriber. // https://docs.revenuecat.com/reference#subscribersapp_user_id -func (c *Client) DeleteSubscriber(userID string) error { - return c.call("DELETE", "subscribers/"+userID, nil, "", nil) +func (c *Client) DeleteSubscriber(ctx context.Context, userID string) error { + return c.call(ctx, "DELETE", "subscribers/"+userID, nil, "", nil) } func (attr SubscriberAttribute) MarshalJSON() ([]byte, error) { diff --git a/subscribers_test.go b/subscribers_test.go index 35f616f..b95d527 100644 --- a/subscribers_test.go +++ b/subscribers_test.go @@ -1,6 +1,7 @@ package revenuecat import ( + "context" "encoding/json" "testing" "time" @@ -11,7 +12,7 @@ func TestGetSubscriber(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.GetSubscriber("123") + _, err := rc.GetSubscriber(context.Background(), "123") if err != nil { t.Errorf("error: %v", err) } @@ -25,7 +26,7 @@ func TestGetSubscriberWithPlatform(t *testing.T) { rc := New("apikey") rc.http = cl - _, err := rc.GetSubscriberWithPlatform("123", "ios") + _, err := rc.GetSubscriberWithPlatform(context.Background(), "123", "ios") if err != nil { t.Errorf("error: %v", err) } @@ -47,7 +48,7 @@ func TestUpdateSubscriberAttributes(t *testing.T) { UpdatedAt: staticTime(t, "2020-01-15 23:54:17"), }, } - err := rc.UpdateSubscriberAttributes("123", attrs) + err := rc.UpdateSubscriberAttributes(context.Background(), "123", attrs) if err != nil { t.Errorf("error: %v", err) } @@ -62,7 +63,7 @@ func TestDeleteSubscriber(t *testing.T) { rc := New("apikey") rc.http = cl - err := rc.DeleteSubscriber("123") + err := rc.DeleteSubscriber(context.Background(), "123") if err != nil { t.Errorf("error: %v", err) }