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 records and other fixes #197

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

pandomic
Copy link

@pandomic pandomic commented Sep 7, 2021

Summary

  • Fix "list" endpoint for subscription items (3e99357)
  • Add usage record endpoints (ab721fa)

@H--o-l
Copy link
Collaborator

H--o-l commented Sep 7, 2021

Hey @pandomic!

About the commit fix(javascript): Be less pedantic about already modified DOM can you describe why you need it? What is your use case? What issue does it solve for you?
Also the commit change two lines that I'm not sure are related. With an explanation inside the commit message, it would be easier for me to understand what you are doing.

About chore(gitignore): Add .venv and .idea to gitignore and chore(Dockerfile): Add Dockerfile , localstripe doesn't need that, it's specific to your environment, and we don't want to maintain it. Can you remove them?

About the last two commits, they look great!
We will try to review them in-deep soon.

@pandomic pandomic force-pushed the usage-records-and-other-fixes branch from 3b62a25 to 2e3ff02 Compare September 7, 2021 17:10
@pandomic
Copy link
Author

pandomic commented Sep 7, 2021

Hi @H--o-l, I left only subscription items and usage records-related changes. Looking forward to getting them merged.

Going back to your question, js-fix was there because of the mounting issue we had with React but leaving it out of scope for now.

I hope in the future we will see a containerized version as well ;)

Copy link
Collaborator

@H--o-l H--o-l left a comment

Choose a reason for hiding this comment

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

Hey @pandomic !

Again thanks for your work on this PR!

Here is the first series of requests, I mostly check the first commit.

li = List(url, limit=limit, starting_after=starting_after)
li._list = [value for key, value in store.items()
if key.startswith(cls.object + ':')
and value.subscription == subscription]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Plop!

Looking at implementation of _api_list_all in a few other object, I think here we should use something more like this:

        li = super(SubscriptionItem, cls)._api_list_all(
            url, limit=limit, starting_after=starting_after)
        li._list = [obj for obj in li._list
                    if value.subscription == subscription]

What it does it's that it re-uses the code of the parent class instead of accessing the store directly.
Do you think it would work?
(I didn't test it, maybe it needs improvement).

except AssertionError:
raise UserError(400, 'Bad request')

# TODO: implement ending_before
Copy link
Collaborator

@H--o-l H--o-l Sep 8, 2021

Choose a reason for hiding this comment

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

There is a few TODO in the existing code, but they always suggest a better way for something already existing.
If we were to TODO all missing arguments of the API, the file would be 50% TODOs.
Instead, I propose to remove the missing feature from the function SubscriptionItem._api_list_all() prototype.
Like that, a motivated contributor could realize that it's missing and open a PR.

@@ -447,6 +449,10 @@ curl -sSfg -u $SK: $HOST/v1/invoices/upcoming?customer=$cus\&subscription_items[

curl -sSfg -u $SK: $HOST/v1/invoices/upcoming?customer=$cus\&subscription=$sub\&subscription_items[0][id]=si_RBrVStcKDimMnp\&subscription_items[0][plan]=basique-annuel\&subscription_proration_date=1504182686\&subscription_tax_percent=20

si=$(curl -sSfg -u $SK: $HOST/v1/subscription_items?subscription=$sub \
| grep -oE 'si_\w+')
[ -n "$si" ]
Copy link
Collaborator

@H--o-l H--o-l Sep 8, 2021

Choose a reason for hiding this comment

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

In this first commit, si is not a subscription item, it's two subscription items.
So maybe there could be a first res that stores them, a check of how many items there is in res and then, in the second commit a si=$(echo "$res" | head -n 1).
It would be more readable for future readers of this code.
What do you think?

Copy link
Author

Choose a reason for hiding this comment

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

👍 split into proposed steps

@@ -6,6 +6,8 @@ set -eux
HOST=http://localhost:8420
SK=sk_test_12345

curl -X DELETE $HOST/_config/data

Copy link
Collaborator

Choose a reason for hiding this comment

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

Because it's not related to a localstripe feature, but it's more a CI change, this change needs to be inside another PR.
Like that we would be able to discuss it, as long as needed, without blocking this PR.

Copy link
Author

Choose a reason for hiding this comment

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

👍 removed this line

try:
assert _type(subscription) is str
assert subscription.startswith('sub_')
Subscription._api_retrieve(subscription)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Great to add this line!
But can it be moved outside the try/catch, I think there is no reason for this line to be in it.
Also can you add a small comment like # to return 404 if not existant on this line?

* override _api_list_all for subscription items
* add `subscription` argument
* filter subscription items by subscription id
* add a test to verify endpoint works
* add api to record usage
* add api to retrieve usage record summaries
* add tests for usage records and summaries
@pandomic
Copy link
Author

Hi @H--o-l, thanks for the feedback, I added proposed changes and force-pushed them to the current branch

Copy link
Collaborator

@H--o-l H--o-l left a comment

Choose a reason for hiding this comment

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

Hey @pandomic!

Here is a few new comments, I let you see, and don't hesitate if you need me.

@@ -165,6 +165,7 @@ def _api_list_all(cls, url, limit=None, starting_after=None, **kwargs):
if kwargs:
raise UserError(400, 'Unexpected ' + ', '.join(kwargs.keys()))

# TODO: implement ending_before
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hey!
Please remove this TODO.

@@ -3144,6 +3144,30 @@ def _calculate_amount_in_tier(self, quantity, index):
t = self.plan.tiers[index]
return int(t['unit_amount']) * quantity + int(t['flat_amount'])

@classmethod
def _api_list_all(cls, url, subscription=None, limit=None,
starting_after=None, ending_before=None, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove ending_before=None here, so users know it's not implemented.

raise UserError(400, 'Unexpected ' + ', '.join(kwargs.keys()))

# to return 404 if not existant
Subscription._api_retrieve(subscription)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please move these two lines after the try/except like in the other interfaces.

)

li._list = [obj for obj in li._list
if obj.subscription == subscription]
Copy link
Collaborator

Choose a reason for hiding this comment

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

A quick diff proposition here:

  • don't add a new line before ) so we save one line
  • don't add a new line after ) so we save one line, and it's more similar to the other API
--- a/localstripe/resources.py
+++ b/localstripe/resources.py
@@ -3160,9 +3160,7 @@ class SubscriptionItem(StripeObject):
             raise UserError(400, 'Bad request')
 
         li = super(SubscriptionItem, cls)._api_list_all(
-            url, limit=limit, starting_after=starting_after
-        )
-
+            url, limit=limit, starting_after=starting_after)
         li._list = [obj for obj in li._list
                     if obj.subscription == subscription]

timestamp=None):
self.quantity = quantity
self.subscription_item = subscription_item_id
self.timestamp = timestamp
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add check of kwargs and assert of parameters like in the other ressources __init__ function?
Assert should follow the Stripe API documentation on what is mandatory and what's not.

self.subscription_item = subscription_item_id
self.timestamp = timestamp

super().__init__()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a small # All exceptions must be raised before this point. above this line like in the other ressources __init__?


usage_records = [value for key, value in store.items()
if key.startswith(cls.object + ':')
and value.subscription_item == subscription_item_id]
Copy link
Collaborator

Choose a reason for hiding this comment

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

When I see this operation, and the routes below (/v1/subscription_items/{subscription_item_id}/usage_records, /v1/subscription_items/{subscription_item_id}/usage_record_summaries) I think it would be simpler and more logical to have usage record stored direcly inside the corresponding subscription item.

Also I'm OK with a new object class UsageRecord() but I think I'm NOK with a class UsageRecord(StripeObject), because usage record are never used with the standard APIs that all other StripeObject implement, ie api_create, api_retrieve, api_update, api_delete, api_list_all and associated HTTP route and method (GET object/id, POST object/id etc).

So I think that routes /usage_records and /usage_record_summaries should be extensions of the object subscription item.

What do you think?

curl -sSfg -u $SK: $HOST/v1/subscription_items/$si/usage_record_summaries \
| grep -oP '"total_usage": \K([0-9]+)')
[ "$total_usage" -eq 115 ]

Copy link
Collaborator

Choose a reason for hiding this comment

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

❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants