Skip to content

Commit

Permalink
custom_method decorator for defining static methods for custom API …
Browse files Browse the repository at this point in the history
…requests
  • Loading branch information
ob-stripe committed Apr 3, 2019
1 parent bd2d74d commit 2eb4116
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 0 deletions.
2 changes: 2 additions & 0 deletions stripe/api_resources/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
)
from stripe.api_resources.abstract.verify_mixin import VerifyMixin

from stripe.api_resources.abstract.custom_method import custom_method

from stripe.api_resources.abstract.nested_resource_class_methods import (
nested_resource_class_methods,
)
43 changes: 43 additions & 0 deletions stripe/api_resources/abstract/custom_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from __future__ import absolute_import, division, print_function

from stripe import util
from stripe.six.moves.urllib.parse import quote_plus


def custom_method(name, http_verb, http_path=None):
if http_verb not in ["get", "post", "delete"]:
raise ValueError(
"Invalid http_verb: %s. Must be one of 'get', 'post' or 'delete'"
% http_verb
)
if http_path is None:
http_path = name

def wrapper(cls):
def custom_method_request(cls, sid, **params):
url = "%s/%s/%s" % (
cls.class_url(),
quote_plus(util.utf8(sid)),
http_path,
)
return cls._static_request(http_verb, url, **params)

existing_method = getattr(cls, name, None)
if existing_method is None:
setattr(cls, name, classmethod(custom_method_request))
else:
# If a method with the same name we want to use already exists on
# the class, we assume it's an instance method. In this case, the
# new class method is prefixed with `_cls_`, and the original
# instance method is decorated with `util.class_method_variant` so
# that the new class method is called when the original method is
# called as a class method.
setattr(cls, "_cls_" + name, classmethod(custom_method_request))
instance_method = util.class_method_variant("_cls_" + name)(
existing_method
)
setattr(cls, name, instance_method)

return cls

return wrapper
2 changes: 2 additions & 0 deletions stripe/api_resources/charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from stripe.api_resources.abstract import CreateableAPIResource
from stripe.api_resources.abstract import UpdateableAPIResource
from stripe.api_resources.abstract import ListableAPIResource
from stripe.api_resources.abstract import custom_method


@custom_method("capture", "post")
class Charge(
CreateableAPIResource, ListableAPIResource, UpdateableAPIResource
):
Expand Down
6 changes: 6 additions & 0 deletions stripe/api_resources/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
from stripe.api_resources.abstract import DeletableAPIResource
from stripe.api_resources.abstract import UpdateableAPIResource
from stripe.api_resources.abstract import ListableAPIResource
from stripe.api_resources.abstract import custom_method


@custom_method("finalize_invoice", "post", http_path="finalize")
@custom_method("mark_uncollectible", "post")
@custom_method("pay", "post")
@custom_method("send_invoice", "post", http_path="send")
@custom_method("void_invoice", "post", http_path="void")
class Invoice(
CreateableAPIResource,
UpdateableAPIResource,
Expand Down
7 changes: 7 additions & 0 deletions tests/api_resources/test_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ def test_is_capturable(self, request_mock):
)
assert isinstance(resource, stripe.Charge)

def test_can_capture(self, request_mock):
resource = stripe.Charge.capture(TEST_RESOURCE_ID)
request_mock.assert_requested(
"post", "/v1/charges/%s/capture" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Charge)

def test_can_update_dispute(self, request_mock):
charge = stripe.Charge.retrieve(TEST_RESOURCE_ID)
resource = charge.update_dispute()
Expand Down
35 changes: 35 additions & 0 deletions tests/api_resources/test_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ def test_can_finalize_invoice(self, request_mock):
)
assert isinstance(resource, stripe.Invoice)

def test_can_finalize_invoice_classmethod(self, request_mock):
resource = stripe.Invoice.finalize_invoice(TEST_RESOURCE_ID)
request_mock.assert_requested(
"post", "/v1/invoices/%s/finalize" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Invoice)

def test_can_mark_uncollectible(self, request_mock):
resource = stripe.Invoice.retrieve(TEST_RESOURCE_ID)
resource = resource.mark_uncollectible()
Expand All @@ -73,6 +80,13 @@ def test_can_mark_uncollectible(self, request_mock):
)
assert isinstance(resource, stripe.Invoice)

def test_can_mark_uncollectible_classmethod(self, request_mock):
resource = stripe.Invoice.mark_uncollectible(TEST_RESOURCE_ID)
request_mock.assert_requested(
"post", "/v1/invoices/%s/mark_uncollectible" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Invoice)

def test_can_pay(self, request_mock):
resource = stripe.Invoice.retrieve(TEST_RESOURCE_ID)
resource = resource.pay()
Expand All @@ -81,6 +95,13 @@ def test_can_pay(self, request_mock):
)
assert isinstance(resource, stripe.Invoice)

def test_can_pay_classmethod(self, request_mock):
resource = stripe.Invoice.pay(TEST_RESOURCE_ID)
request_mock.assert_requested(
"post", "/v1/invoices/%s/pay" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Invoice)

def test_can_send_invoice(self, request_mock):
resource = stripe.Invoice.retrieve(TEST_RESOURCE_ID)
resource = resource.send_invoice()
Expand All @@ -89,6 +110,13 @@ def test_can_send_invoice(self, request_mock):
)
assert isinstance(resource, stripe.Invoice)

def test_can_send_invoice_classmethod(self, request_mock):
resource = stripe.Invoice.send_invoice(TEST_RESOURCE_ID)
request_mock.assert_requested(
"post", "/v1/invoices/%s/send" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Invoice)

def test_can_upcoming(self, request_mock):
resource = stripe.Invoice.upcoming(customer="cus_123")
request_mock.assert_requested("get", "/v1/invoices/upcoming")
Expand All @@ -101,3 +129,10 @@ def test_can_void_invoice(self, request_mock):
"post", "/v1/invoices/%s/void" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Invoice)

def test_can_void_invoice_classmethod(self, request_mock):
resource = stripe.Invoice.void_invoice(TEST_RESOURCE_ID)
request_mock.assert_requested(
"post", "/v1/invoices/%s/void" % TEST_RESOURCE_ID
)
assert isinstance(resource, stripe.Invoice)

0 comments on commit 2eb4116

Please sign in to comment.