Skip to content
3 changes: 3 additions & 0 deletions chartmogul/api/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ class Account(Resource):
_path = "/account"

class _Schema(Schema):
id = fields.String(allow_none=True)
name = fields.String()
currency = fields.String()
time_zone = fields.String()
week_start_on = fields.String()
churn_recognition = fields.String(allow_none=True)
churn_when_zero_mrr = fields.Raw(allow_none=True)

@post_load
def make(self, data, **kwargs):
Expand Down
3 changes: 3 additions & 0 deletions chartmogul/api/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class _Schema(Schema):
account_code = fields.String(allow_none=True)
description = fields.String(allow_none=True)
event_order = fields.Int(allow_none=True)
errors = fields.Dict(allow_none=True)

@post_load
def make(self, data, **kwargs):
Expand Down Expand Up @@ -97,3 +98,5 @@ def all(cls, config, **kwargs):
"/data_sources{/data_source_uuid}/customers{/customer_uuid}/invoices",
)
Invoice.retrieve = Invoice._method("retrieve", "get", "/invoices{/uuid}")
Invoice.update_status = Invoice._method("modify", "patch", "/invoices{/uuid}")
Invoice.disable = Invoice._method("disable", "patch", "/invoices{/uuid}/disable")
41 changes: 41 additions & 0 deletions chartmogul/api/subscription_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,54 @@ class _Schema(Schema):
retracted_event_id = fields.String(allow_none=True)
external_id = fields.String(allow_none=True)
event_order = fields.Int(allow_none=True)
disabled = fields.Bool(allow_none=True)

@post_load
def make(self, data, **kwargs):
return SubscriptionEvent(**data)

_schema = _Schema(unknown=EXCLUDE)

@classmethod
def destroy(cls, config, **kwargs):
"""Accept flat params and wrap in subscription_event envelope for the API."""
data = dict(kwargs.get("data", {}))
if "subscription_event" not in data:
data = {"subscription_event": data}
return cls.destroy_with_params(config, data=data)

@classmethod
def modify(cls, config, **kwargs):
"""Accept flat params and wrap in subscription_event envelope for the API."""
data = dict(kwargs.get("data", {}))
if "subscription_event" not in data:
data = {"subscription_event": data}
return cls.modify_with_params(config, data=data)

@classmethod
def disable(cls, config, **kwargs):
"""Disable a subscription event by setting disabled to true."""
data = dict(kwargs.get("data", {}))
if "subscription_event" in data:
data = {"subscription_event": dict(data["subscription_event"])}
data["subscription_event"]["disabled"] = True
else:
data["disabled"] = True
data = {"subscription_event": data}
return cls.modify_with_params(config, data=data)

@classmethod
def enable(cls, config, **kwargs):
"""Enable a subscription event by setting disabled to false."""
data = dict(kwargs.get("data", {}))
if "subscription_event" in data:
data = {"subscription_event": dict(data["subscription_event"])}
data["subscription_event"]["disabled"] = False
else:
data["disabled"] = False
data = {"subscription_event": data}
return cls.modify_with_params(config, data=data)


SubscriptionEvent.all = SubscriptionEvent._method("all", "get", "/subscription_events")
SubscriptionEvent.destroy_with_params = SubscriptionEvent._method(
Expand Down
1 change: 1 addition & 0 deletions chartmogul/api/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class _Schema(Schema):
result = fields.String()
amount_in_cents = fields.Int(allow_none=True)
transaction_fees_in_cents = fields.Int(allow_none=True)
errors = fields.Dict(allow_none=True)

@post_load
def make(self, data, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion chartmogul/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def _expandPath(cls, path, kwargs):
def _validate_arguments(cls, method, kwargs):
# This enforces user to pass argument, otherwise we could call
# wrong URL.
if method in ["destroy", "cancel", "retrieve", "modify", "update"] and "uuid" not in kwargs:
if method in ["destroy", "cancel", "retrieve", "modify", "update", "disable"] and "uuid" not in kwargs:
raise ArgumentMissingError("Please pass 'uuid' parameter")
if method in ["create", "modify"] and "data" not in kwargs:
raise ArgumentMissingError("Please pass 'data' parameter")
Expand Down
100 changes: 98 additions & 2 deletions test/api/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@
from chartmogul import Account, Config, APIError


jsonResponse = {
base_response = {
"name": "Example Test Company",
"currency": "EUR",
"time_zone": "Europe/Berlin",
"week_start_on": "sunday",
}

response_with_id = {
**base_response,
"id": "acct_a1b2c3d4",
}

response_with_include = {
**response_with_id,
"churn_recognition": "immediate",
"churn_when_zero_mrr": "ignore",
}


class AccountTestCase(unittest.TestCase):
"""
Expand All @@ -25,7 +36,7 @@ def test_retrieve(self, mock_requests):
"https://api.chartmogul.com/v1/account",
request_headers={"Authorization": "Basic dG9rZW46"},
status_code=200,
json=jsonResponse,
json=base_response,
)

config = Config("token") # is actually checked in mock
Expand All @@ -35,3 +46,88 @@ def test_retrieve(self, mock_requests):
self.assertEqual(account.currency, "EUR")
self.assertEqual(account.time_zone, "Europe/Berlin")
self.assertEqual(account.week_start_on, "sunday")

@requests_mock.mock()
def test_retrieve_with_id(self, mock_requests):
mock_requests.register_uri(
"GET",
"https://api.chartmogul.com/v1/account",
request_headers={"Authorization": "Basic dG9rZW46"},
status_code=200,
json=response_with_id,
)

config = Config("token")
account = Account.retrieve(config).get()
self.assertTrue(isinstance(account, Account))
self.assertEqual(account.id, "acct_a1b2c3d4")

@requests_mock.mock()
def test_retrieve_with_include(self, mock_requests):
mock_requests.register_uri(
"GET",
"https://api.chartmogul.com/v1/account?include=churn_recognition,churn_when_zero_mrr",
request_headers={"Authorization": "Basic dG9rZW46"},
headers={"Content-Type": "application/json"},
status_code=200,
json=response_with_include,
)

config = Config("token")
account = Account.retrieve(
config,
include="churn_recognition,churn_when_zero_mrr"
).get()
self.assertTrue(isinstance(account, Account))
self.assertEqual(account.id, "acct_a1b2c3d4")
self.assertEqual(account.churn_recognition, "immediate")
self.assertEqual(account.churn_when_zero_mrr, "ignore")
self.assertEqual(
mock_requests.last_request.qs,
{"include": ["churn_recognition,churn_when_zero_mrr"]},
)

@requests_mock.mock()
def test_retrieve_without_id_field(self, mock_requests):
"""Old API responses without id field should not break deserialization."""
mock_requests.register_uri(
"GET",
"https://api.chartmogul.com/v1/account",
request_headers={"Authorization": "Basic dG9rZW46"},
status_code=200,
json=base_response,
)

config = Config("token")
account = Account.retrieve(config).get()
self.assertTrue(isinstance(account, Account))
self.assertFalse(hasattr(account, "id"))

@requests_mock.mock()
def test_retrieve_with_single_include(self, mock_requests):
single_include_response = {
**response_with_id,
"churn_recognition": "immediate",
}

mock_requests.register_uri(
"GET",
"https://api.chartmogul.com/v1/account?include=churn_recognition",
request_headers={"Authorization": "Basic dG9rZW46"},
headers={"Content-Type": "application/json"},
status_code=200,
json=single_include_response,
)

config = Config("token")
account = Account.retrieve(
config,
include="churn_recognition"
).get()
self.assertTrue(isinstance(account, Account))
self.assertEqual(account.churn_recognition, "immediate")
self.assertFalse(hasattr(account, "churn_when_zero_mrr"))
self.assertEqual(
mock_requests.last_request.qs,
{"include": ["churn_recognition"]},
)
Loading
Loading