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

[usage] Use stripe clients rather than a singleton in the usage controller #10854

Merged
merged 3 commits into from
Jun 27, 2022

Conversation

andrew-farries
Copy link
Contributor

@andrew-farries andrew-farries commented Jun 23, 2022

Description

Change the usage component's Stripe package to return client instances rather than using a Stripe singleton.

Related Issue(s)

#10754 (comment)

How to test

The usual test steps to set up usage based billing and generate a test invoice.

See eg #10801

Release Notes

NONE

Documentation

Werft options:

  • /werft with-preview
  • /werft with-payment

/hold

@andrew-farries andrew-farries requested a review from a team June 23, 2022 08:05
@github-actions github-actions bot added the team: webapp Issue belongs to the WebApp team label Jun 23, 2022
Copy link
Member

@easyCZ easyCZ left a comment

Choose a reason for hiding this comment

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

How are we going to mock the Stripe client so that we can test what requests we send to Stripe?

type stripeKeys struct {
PublishableKey string `json:"publishableKey"`
SecretKey string `json:"secretKey"`
}

// Authenticate authenticates the Stripe client using a provided file containing a Stripe secret key.
func Authenticate(apiKeyFile string) error {
func Authenticate(apiKeyFile string) (*Client, error) {
Copy link
Member

Choose a reason for hiding this comment

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

I'd recommend passing in a struct with the credentials, instead of the Path. The path can be resolved during component initialization. For example, unit tests should need a path, just data.

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
func Authenticate(apiKeyFile string) (*Client, error) {
func New(apiKeyFile string) (*Client, error) {

So that you can do stripe.New(..)

bytes, err := os.ReadFile(apiKeyFile)
if err != nil {
return err
return nil, err
}

var stripeKeys stripeKeys
Copy link
Member

Choose a reason for hiding this comment

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

name clashes with type

}

stripe.Key = stripeKeys.SecretKey
return nil
sc := &client.API{}
Copy link
Member

Choose a reason for hiding this comment

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

Would client.New(key, backends) be more appropriate here?

@werft-gitpod-dev-com
Copy link

started the job as gitpod-build-af-instantiate-stripe-client-instance.6 because the annotations in the pull request description changed
(with .werft/ from main)

@andrew-farries
Copy link
Contributor Author

andrew-farries commented Jun 23, 2022

How are we going to mock the Stripe client so that we can test what requests we send to Stripe?

I think we have a few options:

  1. Use stripe-mock.
  2. Create a real stripe account and use test mode keys to interact with it from tests. We could:
  • Set up a product, prices, customer and subscription in test setup.
  • Run the stripe client.
  • Assert on the state of the stripe customer's subscription.
  1. Write our own stripe mock (not keen on this) that handles just the subset of the Stripe API used by this client.

@easyCZ
Copy link
Member

easyCZ commented Jun 24, 2022

I think using stripe-mock but directly in code, rather than starting another process is our best option for now. The only other alternative is to expose a (small) interface which matches Stripe for the API methods we need and stub these out for our purposes.

Using a real account is not really an option for unit tests, they will run possibly in parallel (from multiple PRs) and we'd have a hard time ensuring they are not flaky.

@andrew-farries
Copy link
Contributor Author

andrew-farries commented Jun 24, 2022

Thanks 👍 We'll add tests for the Stripe integration in a later PR. Could you approve if you are happy with these changes 🙏 .

/hold

@easyCZ
Copy link
Member

easyCZ commented Jun 24, 2022

I was waiting for the parent PR to land, otherwise it would've merged this one into the parent PR

@andrew-farries
Copy link
Contributor Author

Thanks. I put hold on it for that reason so I think it can be safely approved.

}

// UpdateUsage updates teams' Stripe subscriptions with usage data
// `usageForTeam` is a map from team name to total workspace seconds used within a billing period.
func UpdateUsage(usageForTeam map[string]int64) error {
func (c *Client) UpdateUsage(usageForTeam map[string]int64) error {
Copy link
Member

Choose a reason for hiding this comment

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

For follow-up, I'd recommend returnig a struct which contains a summary of the succesful and failed updates. That way, you can also better test that the right number of records got updated. For an example, see the Usage Reconciler

Base automatically changed from af/usage-make-payment-integration-optional to main June 27, 2022 06:16
@roboquat roboquat added size/L and removed size/M labels Jun 27, 2022
@andrew-farries andrew-farries force-pushed the af/instantiate-stripe-client-instance branch from f71a674 to a71fc50 Compare June 27, 2022 07:52
@roboquat roboquat added size/M and removed size/L labels Jun 27, 2022
@andrew-farries
Copy link
Contributor Author

/unhold

Andrew Farries added 3 commits June 27, 2022 07:58
`Authenticate` now returns a Client object rather than acting as a
singleton. Change the `UpdateUsage` function to be a method on the
client type.

See:
https://github.com/stripe/stripe-go#with-a-client
Give an instance of the stripe client to the stripe billing controller.
* Rename the function to `New`.
* Lift config file unmarshaling out of the package and into the
  consumer.
@andrew-farries andrew-farries force-pushed the af/instantiate-stripe-client-instance branch from a71fc50 to 5f9e5b6 Compare June 27, 2022 07:58
@roboquat roboquat merged commit db870a5 into main Jun 27, 2022
@roboquat roboquat deleted the af/instantiate-stripe-client-instance branch June 27, 2022 08:15
@roboquat roboquat added deployed: webapp Meta team change is running in production deployed Change is completely running in production labels Jun 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
deployed: webapp Meta team change is running in production deployed Change is completely running in production release-note-none size/M team: webapp Issue belongs to the WebApp team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants