diff --git a/.flake8 b/.flake8 index 2adde4d..f02138b 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,4 @@ [flake8] ignore = E501,W503 exclude = __init__.py +max-line-length = 100 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c91637d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog], +and this project adheres to [Semantic Versioning]. + +[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ +[Semantic Versioning]: https://semver.org/spec/v2.0.0.html + +## [3.1.3] - 2023-09-27 + +### Added +- Support for cursor based pagination to `.all()` endpoints. +- Changelog. diff --git a/README.md b/README.md index 370d862..f197fff 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ chartmogul.DataSource.destroy(config, uuid='ds_5915ee5a-babd-406b-b8ce-d207133fb ```python chartmogul.Customer.create(config, data={}) -chartmogul.Customer.all(config, page=2, per_page=20) +chartmogul.Customer.all(config, cursor='cursor==', per_page=20) chartmogul.Customer.retrieve(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb') chartmogul.Customer.search(config, email='email@email.com') chartmogul.Customer.merge(config, data={ @@ -217,7 +217,7 @@ chartmogul.Plan.retrieve(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb' chartmogul.Plan.modify(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={ 'name': 'new name' }) -chartmogul.Plan.all(config, page=2, external_id='') +chartmogul.Plan.all(config, cursor='cursor==', external_id='') chartmogul.Plan.destroy(config, uuid='') ``` @@ -227,7 +227,7 @@ chartmogul.Plan.destroy(config, uuid='') chartmogul.PlanGroup.create(config, data={}) chartmogul.PlanGroup.retrieve(config, uuid='plg_5915ee5a-babd-406b-b8ce-d207133fb4cb') chartmogul.PlanGroup.modify(config, uuid='plg_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) -chartmogul.PlanGroup.all(config, page=2) +chartmogul.PlanGroup.all(config, cursor='cursor==') chartmogul.PlanGroup.all(config, uuid='plg_5915ee5a-babd-406b-b8ce-d207133fb4cb') chartmogul.PlanGroup.destroy(config, uuid='') ``` @@ -238,7 +238,7 @@ chartmogul.PlanGroup.destroy(config, uuid='') import chartmogul chartmogul.Invoice.create(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) -chartmogul.Invoice.all(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', page=2, per_page=10) +chartmogul.Invoice.all(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', cursor='cursor==', per_page=10) chartmogul.Invoice.all(config, customer_uuid='cus_f466e33d-ff2b-4a11-8f85-417eb02157a7', external_id='INV0001') chartmogul.Invoice.retrieve(config, uuid='inv_22910fc6-c931-48e7-ac12-90d2cb5f0059') ``` diff --git a/chartmogul/__init__.py b/chartmogul/__init__.py index f390ec7..186921a 100644 --- a/chartmogul/__init__.py +++ b/chartmogul/__init__.py @@ -37,8 +37,8 @@ :license: MIT, see LICENSE for more details. """ -__title__ = 'chartmogul' +__title__ = "chartmogul" __build__ = 0x000000 -__author__ = 'ChartMogul Ltd' -__license__ = 'MIT' -__copyright__ = 'Copyright 2023 ChartMogul Ltd' +__author__ = "ChartMogul Ltd" +__license__ = "MIT" +__copyright__ = "Copyright 2023 ChartMogul Ltd" diff --git a/chartmogul/api/__init__.py b/chartmogul/api/__init__.py index 0004094..3169f8d 100644 --- a/chartmogul/api/__init__.py +++ b/chartmogul/api/__init__.py @@ -1,2 +1 @@ - # api module is imported directly into chartmogul namespace for users' convenience. diff --git a/chartmogul/api/account.py b/chartmogul/api/account.py index 7b618dc..97d24bf 100644 --- a/chartmogul/api/account.py +++ b/chartmogul/api/account.py @@ -6,6 +6,7 @@ class Account(Resource): """ https://dev.chartmogul.com/v1.0/reference#account """ + _path = "/account" class _Schema(Schema): diff --git a/chartmogul/api/activities_export.py b/chartmogul/api/activities_export.py index 7a9c65c..0609b3a 100644 --- a/chartmogul/api/activities_export.py +++ b/chartmogul/api/activities_export.py @@ -5,7 +5,6 @@ class ExportParams(DataObject): - class _Schema(Schema): kind = fields.String() params = fields.Dict(allow_none=True) @@ -19,8 +18,9 @@ class ActivitiesExport(Resource): """ https://dev.chartmogul.com/v1.0/reference# """ + _path = "/activities_export" - _many = namedtuple('ActivitiesExport', ["id", "status", "file_url", "expires_at", "created_at"]) + _many = namedtuple("ActivitiesExport", ["id", "status", "file_url", "expires_at", "created_at"]) class _Schema(Schema): # Create @@ -45,11 +45,13 @@ def make(self, data, **kwargs): @classmethod def _validate_arguments(cls, method, kwargs): # This enforces user to pass correct argument - if method in ['retrieve'] and 'id' not in kwargs: - raise ArgumentMissingError("Please pass 'id' parameter to retrieve your export request status") - if method in ['create'] and 'data' not in kwargs: + if method in ["retrieve"] and "id" not in kwargs: + raise ArgumentMissingError( + "Please pass 'id' parameter to retrieve your export request status" + ) + if method in ["create"] and "data" not in kwargs: raise ArgumentMissingError("Please pass 'data' parameter") -ActivitiesExport.create = ActivitiesExport._method('create', 'post', '/activities_export') -ActivitiesExport.retrieve = ActivitiesExport._method('retrieve', 'get', '/activities_export/{id}') +ActivitiesExport.create = ActivitiesExport._method("create", "post", "/activities_export") +ActivitiesExport.retrieve = ActivitiesExport._method("retrieve", "get", "/activities_export/{id}") diff --git a/chartmogul/api/activity.py b/chartmogul/api/activity.py index 1e599a2..1e47ae1 100644 --- a/chartmogul/api/activity.py +++ b/chartmogul/api/activity.py @@ -5,27 +5,30 @@ class Activity(Resource): """ - https://dev.chartmogul.com/v1.0/reference#list-customer-subscriptions + https://dev.chartmogul.com/reference/list-activities """ + _path = "/activities" - _root_key = 'entries' - _many = namedtuple('Activities', [_root_key, "has_more", "per_page"]) + _root_key = "entries" + _many = namedtuple( + "Activities", [_root_key, "has_more", "per_page", "cursor"], defaults=[None, None, None] + ) class _Schema(Schema): - activity_arr = fields.Number(data_key='activity-arr') - activity_mrr = fields.Number(data_key='activity-mrr') - activity_mrr_movement = fields.Number(data_key='activity-mrr-movement') + activity_arr = fields.Number(data_key="activity-arr") + activity_mrr = fields.Number(data_key="activity-mrr") + activity_mrr_movement = fields.Number(data_key="activity-mrr-movement") currency = fields.String() date = fields.DateTime() description = fields.String() type = fields.String() - subscription_external_id = fields.String(data_key='subscription-external-id') - plan_external_id = fields.String(data_key='plan-external-id') - customer_name = fields.String(data_key='customer-name') - customer_uuid = fields.String(data_key='customer-uuid') - customer_external_id = fields.String(data_key='customer-external-id') - billing_connector_uuid = fields.String(data_key='billing-connector-uuid') - uuid = fields.String(data_key='uuid') + subscription_external_id = fields.String(data_key="subscription-external-id") + plan_external_id = fields.String(data_key="plan-external-id") + customer_name = fields.String(data_key="customer-name") + customer_uuid = fields.String(data_key="customer-uuid") + customer_external_id = fields.String(data_key="customer-external-id") + billing_connector_uuid = fields.String(data_key="billing-connector-uuid") + uuid = fields.String(data_key="uuid") @post_load def make(self, data, **kwargs): diff --git a/chartmogul/api/attributes.py b/chartmogul/api/attributes.py index 737016e..3824d8c 100644 --- a/chartmogul/api/attributes.py +++ b/chartmogul/api/attributes.py @@ -6,6 +6,7 @@ class Attributes(Resource): """ https://dev.chartmogul.com/v1.0/reference#customer-attributes """ + _path = "/customers{/uuid}/attributes" class _Schema(Schema): diff --git a/chartmogul/api/config.py b/chartmogul/api/config.py index 6f5db90..d5dd0e4 100644 --- a/chartmogul/api/config.py +++ b/chartmogul/api/config.py @@ -6,7 +6,7 @@ class Config: uri = API_BASE + "/" + VERSION def __init__(self, api_key, request_timeout=None, max_retries=20, backoff_factor=2): - self.auth = (api_key, '') + self.auth = (api_key, "") self.request_timeout = request_timeout self.max_retries = max_retries self.backoff_factor = backoff_factor diff --git a/chartmogul/api/contact.py b/chartmogul/api/contact.py index c381026..6c1b8d1 100644 --- a/chartmogul/api/contact.py +++ b/chartmogul/api/contact.py @@ -7,10 +7,10 @@ class Contact(Resource): """ https://dev.chartmogul.com/v1.0/reference#contacts """ + _path = "/contacts{/uuid}" _root_key = "entries" - _many = namedtuple("Contacts", - [_root_key, "has_more", "cursor"]) + _many = namedtuple("Contacts", [_root_key, "has_more", "cursor"]) class _Schema(Schema): uuid = fields.String() diff --git a/chartmogul/api/custom_attrs.py b/chartmogul/api/custom_attrs.py index faa4ddc..71f31ca 100644 --- a/chartmogul/api/custom_attrs.py +++ b/chartmogul/api/custom_attrs.py @@ -8,6 +8,7 @@ class CustomAttributes(Resource): """ https://dev.chartmogul.com/v1.0/reference#custom-attributes """ + _path = "/customers{/uuid}/attributes/custom" class _Schema(Schema): @@ -17,7 +18,7 @@ class _Schema(Schema): def make(self, data, **kwargs): return CustomAttributes(**data) - _customers = namedtuple('Customers', ['entries']) + _customers = namedtuple("Customers", ["entries"]) _schema = _Schema(unknown=EXCLUDE) @classmethod @@ -30,8 +31,8 @@ def _load(cls, response): if response.status_code == 204: return None jsonObj = response.json() - if 'entries' in jsonObj: - customers = Customer._schema.load(jsonObj['entries'], many=True) + if "entries" in jsonObj: + customers = Customer._schema.load(jsonObj["entries"], many=True) return cls._customers(customers) else: return cls._schema.load(jsonObj) diff --git a/chartmogul/api/customer.py b/chartmogul/api/customer.py index 5fd3697..8165b37 100644 --- a/chartmogul/api/customer.py +++ b/chartmogul/api/customer.py @@ -13,7 +13,6 @@ class Address(DataObject): - class _Schema(Schema): address_zip = fields.String(allow_none=True) city = fields.String(allow_none=True) @@ -29,10 +28,14 @@ class Customer(Resource): """ https://dev.chartmogul.com/v1.0/reference#customers """ + _path = "/customers{/uuid}" - _root_key = 'entries' - _many = namedtuple('Customers', - [_root_key, "has_more", "per_page", "page", "current_page", "total_pages"]) + _root_key = "entries" + _many = namedtuple( + "Customers", + [_root_key, "has_more", "per_page", "page", "current_page", "total_pages", "cursor"], + defaults=[None, None, None, None, None, None], + ) class _Schema(Schema): # All operations @@ -74,8 +77,12 @@ def make(self, data, **kwargs): _schema = _Schema(unknown=EXCLUDE) -Customer.search = Customer._method('all', 'get', '/customers/search') -Customer.merge = Customer._method('merge', 'post', '/customers/merges') -Customer.connectSubscriptions = Customer._method('create', 'post', '/customers/{uuid}/connect_subscriptions') -Customer.contacts = Contact._method('all', 'get', '/customers/{uuid}/contacts', useCallerClass=True) -Customer.createContact = Contact._method('create', 'post', '/customers/{uuid}/contacts', useCallerClass=True) +Customer.search = Customer._method("all", "get", "/customers/search") +Customer.merge = Customer._method("merge", "post", "/customers/merges") +Customer.connectSubscriptions = Customer._method( + "create", "post", "/customers/{uuid}/connect_subscriptions" +) +Customer.contacts = Contact._method("all", "get", "/customers/{uuid}/contacts", useCallerClass=True) +Customer.createContact = Contact._method( + "create", "post", "/customers/{uuid}/contacts", useCallerClass=True +) diff --git a/chartmogul/api/customers/__init__.py b/chartmogul/api/customers/__init__.py index b1402ce..0e686b6 100644 --- a/chartmogul/api/customers/__init__.py +++ b/chartmogul/api/customers/__init__.py @@ -1 +1 @@ -# Allow users to access the submodules of customers \ No newline at end of file +# Allow users to access the submodules of customers diff --git a/chartmogul/api/customers/activity.py b/chartmogul/api/customers/activity.py index b77f76f..de43eb5 100644 --- a/chartmogul/api/customers/activity.py +++ b/chartmogul/api/customers/activity.py @@ -7,21 +7,26 @@ class CustomerActivity(Resource): """ https://dev.chartmogul.com/v1.0/reference#list-customer-subscriptions """ + _path = "/customers{/uuid}/activities" - _root_key = 'entries' - _many = namedtuple('Activities', [_root_key, "has_more", "per_page", "page"]) + _root_key = "entries" + _many = namedtuple( + "Activities", + [_root_key, "has_more", "per_page", "page", "cursor"], + defaults=[None, None, None, None], + ) class _Schema(Schema): id = fields.Int() - activity_arr = fields.Number(data_key='activity-arr') - activity_mrr = fields.Number(data_key='activity-mrr') - activity_mrr_movement = fields.Number(data_key='activity-mrr-movement') + activity_arr = fields.Number(data_key="activity-arr") + activity_mrr = fields.Number(data_key="activity-mrr") + activity_mrr_movement = fields.Number(data_key="activity-mrr-movement") currency = fields.String() - currency_sign = fields.String(data_key='currency-sign') + currency_sign = fields.String(data_key="currency-sign") date = fields.DateTime() description = fields.String() type = fields.String() - subscription_external_id = fields.String(data_key='subscription-external-id') + subscription_external_id = fields.String(data_key="subscription-external-id") @post_load def make(self, data, **kwargs): diff --git a/chartmogul/api/customers/subscription.py b/chartmogul/api/customers/subscription.py index cfc1331..2d14b9a 100644 --- a/chartmogul/api/customers/subscription.py +++ b/chartmogul/api/customers/subscription.py @@ -8,9 +8,14 @@ class CustomerSubscription(Resource): https://dev.chartmogul.com/v1.0/reference#list-customer-subscriptions https://dev.chartmogul.com/v1.0/reference#list-a-customers-subscriptions """ + _path = "/customers{/uuid}/subscriptions" - _root_key = 'entries' - _many = namedtuple('Subscriptions', [_root_key, "has_more", "per_page", "page"]) + _root_key = "entries" + _many = namedtuple( + "Subscriptions", + [_root_key, "has_more", "per_page", "page", "cursor"], + defaults=[None, None, None, None], + ) class _Schema(Schema): id = fields.Int(allow_none=True) @@ -19,12 +24,12 @@ class _Schema(Schema): mrr = fields.Number(allow_none=True) arr = fields.Number(allow_none=True) status = fields.String(allow_none=True) - billing_cycle = fields.String(data_key='billing-cycle', allow_none=True) - billing_cycle_count = fields.Number(data_key='billing-cycle-count', allow_none=True) - start_date = fields.DateTime(data_key='start-date', allow_none=True) - end_date = fields.DateTime(data_key='end-date', allow_none=True) + billing_cycle = fields.String(data_key="billing-cycle", allow_none=True) + billing_cycle_count = fields.Number(data_key="billing-cycle-count", allow_none=True) + start_date = fields.DateTime(data_key="start-date", allow_none=True) + end_date = fields.DateTime(data_key="end-date", allow_none=True) currency = fields.String(allow_none=True) - currency_sign = fields.String(data_key='currency-sign', allow_none=True) + currency_sign = fields.String(data_key="currency-sign", allow_none=True) # /import namespace uuid = fields.String(allow_none=True) @@ -45,16 +50,36 @@ def make(self, data, **kwargs): @classmethod def _loadJSON(cls, jsonObj): if "subscriptions" in jsonObj: - _many = namedtuple('Subscriptions', ["subscriptions", "current_page", "total_pages", "customer_uuid"]) - return _many(cls._schema.load(jsonObj["subscriptions"], many=True), - jsonObj["current_page"], - jsonObj["total_pages"], - jsonObj["customer_uuid"]) + _many = namedtuple( + "Subscriptions", + [ + "subscriptions", + "current_page", + "total_pages", + "customer_uuid", + "has_more", + "cursor", + ], + ) + return _many( + cls._schema.load(jsonObj["subscriptions"], many=True), + current_page=jsonObj.get("current_page", None), + total_pages=jsonObj.get("total_pages", None), + customer_uuid=jsonObj.get("customer_uuid", None), + has_more=jsonObj.get("has_more", None), + cursor=jsonObj.get("cursor", None), + ) else: return super(CustomerSubscription, cls)._loadJSON(jsonObj) # /import namespace -CustomerSubscription.list_imported = CustomerSubscription._method('list_imported', 'get', "/import/customers{/uuid}/subscriptions") -CustomerSubscription.cancel = CustomerSubscription._method('cancel', 'patch', "/import/subscriptions{/uuid}") -CustomerSubscription.modify = CustomerSubscription._method('modify', 'patch', "/import/subscriptions{/uuid}") +CustomerSubscription.list_imported = CustomerSubscription._method( + "list_imported", "get", "/import/customers{/uuid}/subscriptions" +) +CustomerSubscription.cancel = CustomerSubscription._method( + "cancel", "patch", "/import/subscriptions{/uuid}" +) +CustomerSubscription.modify = CustomerSubscription._method( + "modify", "patch", "/import/subscriptions{/uuid}" +) diff --git a/chartmogul/api/data_source.py b/chartmogul/api/data_source.py index 5cb7bf2..eadce4f 100644 --- a/chartmogul/api/data_source.py +++ b/chartmogul/api/data_source.py @@ -7,9 +7,10 @@ class DataSource(Resource): """ https://dev.chartmogul.com/v1.0/reference#data-sources """ - _path = '/data_sources{/uuid}' - _root_key = 'data_sources' - _many = namedtuple('DataSources', [_root_key]) + + _path = "/data_sources{/uuid}" + _root_key = "data_sources" + _many = namedtuple("DataSources", [_root_key]) class _Schema(Schema): uuid = fields.String() diff --git a/chartmogul/api/invoice.py b/chartmogul/api/invoice.py index 342f083..43e3abe 100644 --- a/chartmogul/api/invoice.py +++ b/chartmogul/api/invoice.py @@ -5,7 +5,6 @@ class LineItem(DataObject): - class _Schema(Schema): uuid = fields.String() external_id = fields.String(allow_none=True) @@ -38,9 +37,14 @@ class Invoice(Resource): """ https://dev.chartmogul.com/v1.0/reference#invoices """ + _path = "/import/customers{/uuid}/invoices" - _root_key = 'invoices' - _many = namedtuple('Invoices', [_root_key, "current_page", "total_pages", "customer_uuid"]) + _root_key = "invoices" + _many = namedtuple( + "Invoices", + [_root_key, "current_page", "total_pages", "cursor", "has_more", "customer_uuid"], + defaults=[None, None, None, None, None], + ) _many.__new__.__defaults__ = (None,) * len(_many._fields) class _Schema(Schema): @@ -69,13 +73,15 @@ def all(cls, config, **kwargs): Actually uses two different endpoints, where it dispatches the call depends on whether customer uuid is given with the old parameter name ('uuid') or not. """ - if 'uuid' in kwargs: + if "uuid" in kwargs: return super(Invoice, cls).all(config, **kwargs) else: return cls.all_any(config, **kwargs) -Invoice.all_any = Invoice._method('all', 'get', '/invoices') -Invoice.destroy = Invoice._method('destroy', 'delete', '/invoices{/uuid}') -Invoice.destroy_all = Invoice._method('destroy_all', 'delete', '/data_sources{/data_source_uuid}/customers{/customer_uuid}/invoices') -Invoice.retrieve = Invoice._method('retrieve', 'get', '/invoices{/uuid}') +Invoice.all_any = Invoice._method("all", "get", "/invoices") +Invoice.destroy = Invoice._method("destroy", "delete", "/invoices{/uuid}") +Invoice.destroy_all = Invoice._method( + "destroy_all", "delete", "/data_sources{/data_source_uuid}/customers{/customer_uuid}/invoices" +) +Invoice.retrieve = Invoice._method("retrieve", "get", "/invoices{/uuid}") diff --git a/chartmogul/api/metrics.py b/chartmogul/api/metrics.py index 4a0107e..578229d 100644 --- a/chartmogul/api/metrics.py +++ b/chartmogul/api/metrics.py @@ -2,31 +2,23 @@ from ..resource import Resource, DataObject, _add_method from collections import namedtuple -metrics = [ - 'customers', - 'customer-churn-rate', - 'arr', - 'asp', - 'mrr', - 'arpa', - 'mrr-churn-rate', - 'ltv' -] +metrics = ["customers", "customer-churn-rate", "arr", "asp", "mrr", "arpa", "mrr-churn-rate", "ltv"] class Summary(DataObject): """ Optional information about a series of metrics. """ + class _Schema(Schema): current = fields.Number() previous = fields.Number() - percentage_change = fields.Number(data_key='percentage-change') + percentage_change = fields.Number(data_key="percentage-change") # All metrics percentage change for metric in metrics: - pc = metric + '-percentage-change' - current_pc = 'current-' + metric - previous_pc = 'previous-' + metric + pc = metric + "-percentage-change" + current_pc = "current-" + metric + previous_pc = "previous-" + metric locals()[pc.replace("-", "_")] = fields.Number(data_key=pc) locals()[current_pc.replace("-", "_")] = fields.Number(data_key=current_pc) locals()[previous_pc.replace("-", "_")] = fields.Number(data_key=previous_pc) @@ -42,18 +34,20 @@ class Metrics(Resource): """ https://dev.chartmogul.com/v1.0/reference#introduction-metrics-api """ + _path = "/metrics/all" - _root_key = 'entries' - _many_cls = namedtuple('Metrics', [_root_key, 'summary']) + _root_key = "entries" + _many_cls = namedtuple("Metrics", [_root_key, "summary"]) _many_cls.__new__.__defaults__ = (None,) * len(_many_cls._fields) class _Schema(Schema): """ Fields are optional, so a subset present is good enough """ + date = fields.Date() - customer_churn_rate = fields.Number(data_key='customer-churn-rate') - mrr_churn_rate = fields.Number(data_key='mrr-churn-rate') + customer_churn_rate = fields.Number(data_key="customer-churn-rate") + mrr_churn_rate = fields.Number(data_key="mrr-churn-rate") ltv = fields.Number() customers = fields.Number() asp = fields.Number() @@ -61,14 +55,14 @@ class _Schema(Schema): arr = fields.Number() mrr = fields.Number() # MRR only - mrr_new_business = fields.Number(data_key='mrr-new-business') - mrr_expansion = fields.Number(data_key='mrr-expansion') - mrr_contraction = fields.Number(data_key='mrr-contraction') - mrr_churn = fields.Number(data_key='mrr-churn') - mrr_reactivation = fields.Number(data_key='mrr-reactivation') - percentage_change = fields.Number(data_key='percentage-change') + mrr_new_business = fields.Number(data_key="mrr-new-business") + mrr_expansion = fields.Number(data_key="mrr-expansion") + mrr_contraction = fields.Number(data_key="mrr-contraction") + mrr_churn = fields.Number(data_key="mrr-churn") + mrr_reactivation = fields.Number(data_key="mrr-reactivation") + percentage_change = fields.Number(data_key="percentage-change") for metric in metrics: - pc = metric + '-percentage-change' + pc = metric + "-percentage-change" locals()[pc.replace("-", "_")] = fields.Number(data_key=pc) @post_load @@ -79,16 +73,16 @@ def make(self, data, **kwargs): @classmethod def _many(cls, entries, **kwargs): - if 'summary' in kwargs: - kwargs['summary'] = Summary._schema.load(kwargs['summary']) + if "summary" in kwargs: + kwargs["summary"] = Summary._schema.load(kwargs["summary"]) return cls._many_cls(entries, **kwargs) -_add_method(Metrics, "mrr", "get", path='/metrics/mrr') -_add_method(Metrics, "arr", "get", path='/metrics/arr') -_add_method(Metrics, "arpa", "get", path='/metrics/arpa') -_add_method(Metrics, "asp", "get", path='/metrics/asp') -_add_method(Metrics, "customer_count", "get", path='/metrics/customer-count') -_add_method(Metrics, "customer_churn_rate", "get", path='/metrics/customer-churn-rate') -_add_method(Metrics, "mrr_churn_rate", "get", path='/metrics/mrr-churn-rate') -_add_method(Metrics, "ltv", "get", path='/metrics/ltv') +_add_method(Metrics, "mrr", "get", path="/metrics/mrr") +_add_method(Metrics, "arr", "get", path="/metrics/arr") +_add_method(Metrics, "arpa", "get", path="/metrics/arpa") +_add_method(Metrics, "asp", "get", path="/metrics/asp") +_add_method(Metrics, "customer_count", "get", path="/metrics/customer-count") +_add_method(Metrics, "customer_churn_rate", "get", path="/metrics/customer-churn-rate") +_add_method(Metrics, "mrr_churn_rate", "get", path="/metrics/mrr-churn-rate") +_add_method(Metrics, "ltv", "get", path="/metrics/ltv") diff --git a/chartmogul/api/ping.py b/chartmogul/api/ping.py index 7a50992..2d1e82c 100644 --- a/chartmogul/api/ping.py +++ b/chartmogul/api/ping.py @@ -6,6 +6,7 @@ class Ping(Resource): """ https://dev.chartmogul.com/docs/authentication """ + _path = "/ping" class _Schema(Schema): diff --git a/chartmogul/api/plan.py b/chartmogul/api/plan.py index a82fd38..d35b373 100644 --- a/chartmogul/api/plan.py +++ b/chartmogul/api/plan.py @@ -7,9 +7,14 @@ class Plan(Resource): """ https://dev.chartmogul.com/v1.0/reference#plans """ + _path = "/plans{/uuid}" - _root_key = 'plans' - _many = namedtuple('Plans', [_root_key, "current_page", "total_pages"]) + _root_key = "plans" + _many = namedtuple( + "Plans", + [_root_key, "current_page", "total_pages", "has_more", "cursor"], + defaults=[None, None, None, None], + ) class _Schema(Schema): uuid = fields.String() diff --git a/chartmogul/api/plan_group.py b/chartmogul/api/plan_group.py index ef9f015..105601b 100644 --- a/chartmogul/api/plan_group.py +++ b/chartmogul/api/plan_group.py @@ -8,9 +8,14 @@ class PlanGroup(Resource): """ https://dev.chartmogul.com/v1.0/reference#plan_groups """ + _path = "/plan_groups{/uuid}" - _root_key = 'plan_groups' - _many = namedtuple('PlanGroups', [_root_key, "current_page", "total_pages"]) + _root_key = "plan_groups" + _many = namedtuple( + "PlanGroups", + [_root_key, "current_page", "total_pages", "has_more", "cursor"], + defaults=[None, None, None, None], + ) class _Schema(Schema): uuid = fields.String() @@ -29,10 +34,10 @@ def all(cls, config, **kwargs): Actually uses two different endpoints, where it dispatches the call depends on whether uuid is given in the param or not. """ - if 'uuid' in kwargs: + if "uuid" in kwargs: return PlanGroupPlans.all(config, **kwargs) else: return cls.all_any(config, **kwargs) -PlanGroup.all_any = PlanGroup._method('all', 'get', '/plan_groups') +PlanGroup.all_any = PlanGroup._method("all", "get", "/plan_groups") diff --git a/chartmogul/api/plan_group_plans.py b/chartmogul/api/plan_group_plans.py index b4250cd..4b01832 100644 --- a/chartmogul/api/plan_group_plans.py +++ b/chartmogul/api/plan_group_plans.py @@ -7,9 +7,14 @@ class PlanGroupPlans(Resource): """ https://dev.chartmogul.com/v1.0/reference#plan_groups """ + _path = "/plan_groups{/uuid}/plans" - _root_key = 'plans' - _many = namedtuple('PlanGroupPlans', [_root_key, "current_page", "total_pages"]) + _root_key = "plans" + _many = namedtuple( + "PlanGroupPlans", + [_root_key, "current_page", "total_pages", "has_more", "cursor"], + defaults=[None, None, None, None], + ) class _Schema(Schema): uuid = fields.String() diff --git a/chartmogul/api/subscription_event.py b/chartmogul/api/subscription_event.py index 75e2455..6d65c05 100644 --- a/chartmogul/api/subscription_event.py +++ b/chartmogul/api/subscription_event.py @@ -7,9 +7,12 @@ class SubscriptionEvent(Resource): """ https://dev.chartmogul.com/reference/subscription-events """ + _path = "/subscription_events" - _root_key = 'subscription_events' - _many = namedtuple('SubscriptionEvents', [_root_key, 'meta']) + _root_key = "subscription_events" + _many = namedtuple( + "SubscriptionEvents", [_root_key, "meta", "has_more", "cursor"], defaults=[None, None, None] + ) class _Schema(Schema): id = fields.Int(required=True) @@ -35,6 +38,10 @@ def make(self, data, **kwargs): _schema = _Schema(unknown=EXCLUDE) -SubscriptionEvent.all = SubscriptionEvent._method('all', 'get', '/subscription_events') -SubscriptionEvent.destroy_with_params = SubscriptionEvent._method('destroy_with_params', 'delete', '/subscription_events') -SubscriptionEvent.modify_with_params = SubscriptionEvent._method('modify_with_params', 'patch', "/subscription_events") +SubscriptionEvent.all = SubscriptionEvent._method("all", "get", "/subscription_events") +SubscriptionEvent.destroy_with_params = SubscriptionEvent._method( + "destroy_with_params", "delete", "/subscription_events" +) +SubscriptionEvent.modify_with_params = SubscriptionEvent._method( + "modify_with_params", "patch", "/subscription_events" +) diff --git a/chartmogul/api/tags.py b/chartmogul/api/tags.py index bdb1177..5f6743f 100644 --- a/chartmogul/api/tags.py +++ b/chartmogul/api/tags.py @@ -8,6 +8,7 @@ class Tags(Resource): """ https://dev.chartmogul.com/v1.0/reference#customer-attributes """ + _path = "/customers{/uuid}/attributes/tags" class _Schema(Schema): @@ -17,7 +18,7 @@ class _Schema(Schema): def make(self, data, **kwargs): return Tags(**data) - _customers = namedtuple('Customers', ['entries']) + _customers = namedtuple("Customers", ["entries"]) _schema = _Schema(unknown=EXCLUDE) @classmethod @@ -30,8 +31,8 @@ def _load(cls, response): if response.status_code == 204: return None jsonObj = response.json() - if 'entries' in jsonObj: - customers = Customer._schema.load(jsonObj['entries'], many=True) + if "entries" in jsonObj: + customers = Customer._schema.load(jsonObj["entries"], many=True) return cls._customers(customers) else: return cls._schema.load(jsonObj) diff --git a/chartmogul/api/transaction.py b/chartmogul/api/transaction.py index 2638bfb..4539c82 100644 --- a/chartmogul/api/transaction.py +++ b/chartmogul/api/transaction.py @@ -6,6 +6,7 @@ class Transaction(Resource): """ https://dev.chartmogul.com/v1.0/reference#transactions """ + _path = "/import/invoices{/uuid}/transactions" class _Schema(Schema): diff --git a/chartmogul/imp/subscription.py b/chartmogul/imp/subscription.py index c419a4d..24051c1 100644 --- a/chartmogul/imp/subscription.py +++ b/chartmogul/imp/subscription.py @@ -5,7 +5,9 @@ class Subscription(SubsNew): @classmethod def all(cls, *args, **kwargs): - warn("chartmogul.imp namespace is deprecated, use chartmogul.CustomerSubscription.list_imported!") + warn( + "chartmogul.imp namespace is deprecated, use chartmogul.CustomerSubscription.list_imported!" + ) return super(Subscription, cls).list_imported(*args, **kwargs) @classmethod diff --git a/chartmogul/resource.py b/chartmogul/resource.py index 6d43b49..6521bf3 100644 --- a/chartmogul/resource.py +++ b/chartmogul/resource.py @@ -116,7 +116,7 @@ def _loadJSON(cls, jsonObj): if "_root_key" in dir(cls) is not None and cls._root_key in jsonObj: return cls._many( cls._schema.load(jsonObj[cls._root_key], many=True), - **{key: jsonObj[key] for key in LIST_PARAMS if key in jsonObj} + **{key: jsonObj[key] for key in LIST_PARAMS if key in jsonObj}, ) else: return cls._schema.load(jsonObj) @@ -143,9 +143,7 @@ def _request(cls, config, method, http_verb, path, data=None, **kwargs): Promise( lambda resolve, _: resolve( getattr( - requests_retry_session( - config.max_retries, config.backoff_factor - ), + requests_retry_session(config.max_retries, config.backoff_factor), http_verb, )( config.uri + path, @@ -173,10 +171,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"] 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") diff --git a/chartmogul/retry_request.py b/chartmogul/retry_request.py index 6d9844e..8071649 100644 --- a/chartmogul/retry_request.py +++ b/chartmogul/retry_request.py @@ -2,14 +2,18 @@ from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry -METHOD_WHITELIST = ['HEAD', 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'] +METHOD_WHITELIST = ["HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS", "TRACE"] STATUS_FORCELIST = (429, 500, 502, 503, 504, 520, 524) -def requests_retry_session(retries=20, backoff_factor=2, session=None,): +def requests_retry_session( + retries=20, + backoff_factor=2, + session=None, +): session = session or requests.Session() adapter = _retry_adapter(retries, backoff_factor) - session.mount('https://', adapter) + session.mount("https://", adapter) return session diff --git a/chartmogul/version.py b/chartmogul/version.py index f71b21a..f749372 100644 --- a/chartmogul/version.py +++ b/chartmogul/version.py @@ -1 +1 @@ -__version__ = '3.1.2' +__version__ = "3.1.3" diff --git a/docs/conf.py b/docs/conf.py index bf0acb5..80845f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,8 @@ # import os import sys -sys.path.insert(0, os.path.abspath('..')) + +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ @@ -31,40 +32,40 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', + "sphinx.ext.autodoc", + "sphinx.ext.todo", + "sphinx.ext.coverage", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # # source_encoding = 'utf-8-sig' # The main toctree document. -main_doc = 'index' +main_doc = "index" # General information about the project. -project = 'ChartMogul' -copyright = '2017, ChartMogul Ltd.' -author = 'Petr Kopac' +project = "ChartMogul" +copyright = "2017, ChartMogul Ltd." +author = "Petr Kopac" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.0.1' +version = "0.0.1" # The full version, including alpha/beta/rc tags. -release = '0.0.1' +release = "0.0.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -85,7 +86,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -107,7 +108,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -124,7 +125,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -158,7 +159,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -238,34 +239,30 @@ # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'ChartMoguldoc' +htmlhelp_basename = "ChartMoguldoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (main_doc, 'ChartMogul.tex', 'ChartMogul Documentation', - 'Petr Kopac', 'manual'), + (main_doc, "ChartMogul.tex", "ChartMogul Documentation", "Petr Kopac", "manual"), ] # The name of an image file (relative to this directory) to place at the top of @@ -305,10 +302,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (main_doc, 'chartmogul', 'ChartMogul Documentation', - [author], 1) -] +man_pages = [(main_doc, "chartmogul", "ChartMogul Documentation", [author], 1)] # If true, show URL addresses after external links. # @@ -321,9 +315,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (main_doc, 'ChartMogul', 'ChartMogul Documentation', - author, 'ChartMogul', 'One line description of project.', - 'Miscellaneous'), + ( + main_doc, + "ChartMogul", + "ChartMogul Documentation", + author, + "ChartMogul", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/setup.py b/setup.py index 3d7b9e9..a9164d2 100644 --- a/setup.py +++ b/setup.py @@ -7,67 +7,69 @@ from codecs import open from setuptools import setup -github_url = 'https://github.com/chartmogul/chartmogul-python' +github_url = "https://github.com/chartmogul/chartmogul-python" -if sys.argv[-1] == 'publish': - os.system('python setup.py sdist upload') +if sys.argv[-1] == "publish": + os.system("python setup.py sdist upload") sys.exit() requires = [ - 'requests>=2.10.0', - 'uritemplate>=3.0.0', - 'promise>=1.0.1', - 'marshmallow>=3.10.0', - 'future>=0.18.3', - 'urllib3<2.0' + "requests>=2.10.0", + "uritemplate>=3.0.0", + "promise>=1.0.1", + "marshmallow>=3.10.0", + "future>=0.18.3", + "urllib3<2.0", ] test_requirements = [ # This is needed to circumvent a vcrpy dependency problem And can be # deleted once it is solved. 'yarl; python_version>"3.5"', 'yarl<1.4; python_version=="3.5"', - - 'mock>=1.0.1', - 'requests-mock>=1.3.0', - 'vcrpy<3.0.0', - 'PyYAML>=5.1.2', - 'httpretty>=0.9.6', - 'wrapt>=1.11.2' + "mock>=1.0.1", + "requests-mock>=1.3.0", + "vcrpy<3.0.0", + "PyYAML>=5.1.2", + "httpretty>=0.9.6", + "wrapt>=1.11.2", ] -with open('chartmogul/version.py', 'r') as fd: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) +with open("chartmogul/version.py", "r") as fd: + version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) if not version: - raise RuntimeError('Cannot find version information') + raise RuntimeError("Cannot find version information") setup( - name='chartmogul', + name="chartmogul", version=version, - description='Python library for ChartMogul API.', - long_description='`See documentation on GitHub <' + github_url + '>`_', - author='Petr Kopac (ChartMogul Ltd.)', - author_email='petr@chartmogul.com', - url='https://chartmogul.com', - download_url = github_url + '/tarball/v' + version, - packages=['chartmogul', 'chartmogul.api', 'chartmogul.imp', 'chartmogul.api.customers'], - package_data={'': ['LICENSE', 'NOTICE'], 'chartmogul': ['*.pem']}, - package_dir={'chartmogul': 'chartmogul', 'chartmogul.api': 'chartmogul/api', 'chartmogul.imp': 'chartmogul/imp', 'chartmogul.api.customers': 'chartmogul/api/customers'}, + description="Python library for ChartMogul API.", + long_description="`See documentation on GitHub <" + github_url + ">`_", + author="Petr Kopac (ChartMogul Ltd.)", + author_email="petr@chartmogul.com", + url="https://chartmogul.com", + download_url=github_url + "/tarball/v" + version, + packages=["chartmogul", "chartmogul.api", "chartmogul.imp", "chartmogul.api.customers"], + package_data={"": ["LICENSE", "NOTICE"], "chartmogul": ["*.pem"]}, + package_dir={ + "chartmogul": "chartmogul", + "chartmogul.api": "chartmogul/api", + "chartmogul.imp": "chartmogul/imp", + "chartmogul.api.customers": "chartmogul/api/customers", + }, include_package_data=True, install_requires=requires, - license='MIT', + license="MIT", zip_safe=False, tests_require=test_requirements, test_suite="test", extras_require={}, classifiers=[ - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10' - - ] + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + ], ) diff --git a/test/api/test_account.py b/test/api/test_account.py index 5255683..1f68608 100644 --- a/test/api/test_account.py +++ b/test/api/test_account.py @@ -6,10 +6,10 @@ jsonResponse = { - "name": u"Example Test Company", - "currency": u"EUR", - "time_zone": u"Europe/Berlin", - "week_start_on": u"sunday" + "name": "Example Test Company", + "currency": "EUR", + "time_zone": "Europe/Berlin", + "week_start_on": "sunday", } @@ -17,14 +17,15 @@ class AccountTestCase(unittest.TestCase): """ Tests account endpoint. """ + @requests_mock.mock() def test_retrieve(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/account", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=jsonResponse + json=jsonResponse, ) config = Config("token") # is actually checked in mock diff --git a/test/api/test_activities_export.py b/test/api/test_activities_export.py index 588a2cd..98ef447 100644 --- a/test/api/test_activities_export.py +++ b/test/api/test_activities_export.py @@ -10,57 +10,55 @@ class ActivitiesExportTestCase(unittest.TestCase): """ Tests Creating ActivitiesExport request """ + @requests_mock.mock() def test_create(self, mock_requests): mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/activities_export", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "id": "618b6698-c6d0-42e9-8c4f-6a2bda5ac472", - "status": "pending", - "file_url": None, - "params": { - "kind": "activities", - "params": {} - }, - "expires_at": None, - "created_at": "2021-07-15T08:23:40+00:00" - } + "id": "618b6698-c6d0-42e9-8c4f-6a2bda5ac472", + "status": "pending", + "file_url": None, + "params": {"kind": "activities", "params": {}}, + "expires_at": None, + "created_at": "2021-07-15T08:23:40+00:00", + }, ) config = Config("token") # is actually checked in mock result = ActivitiesExport.create(config, data={}) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(result.get().id, '618b6698-c6d0-42e9-8c4f-6a2bda5ac472') + self.assertEqual(result.get().id, "618b6698-c6d0-42e9-8c4f-6a2bda5ac472") """ Tests Retrieving ActivitiesExport status """ + @requests_mock.mock() def test_retrieve(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/activities_export/618b6698-c6d0-42e9-8c4f-6a2bda5ac472", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "id": "618b6798-c6d0-42e0-8c4f-6a2bdb5ac412", - "status": "succeeded", - "file_url": "https://customer-export.s3.eu-east-1.amazonaws.com/activities-a-2e135794-207e-4623-a485-87fa0a0cc9c5.zip", - "params": { - "kind": "activities", - "params": {} - }, - "expires_at": "2021-07-22T08:23:41+00:00", - "created_at": "2021-07-15T08:23:40+00:00" - } + "id": "618b6798-c6d0-42e0-8c4f-6a2bdb5ac412", + "status": "succeeded", + "file_url": "https://customer-export.s3.eu-east-1.amazonaws.com/activities-a-2e135794-207e-4623-a485-87fa0a0cc9c5.zip", + "params": {"kind": "activities", "params": {}}, + "expires_at": "2021-07-22T08:23:41+00:00", + "created_at": "2021-07-15T08:23:40+00:00", + }, ) config = Config("token") # is actually checked in mock - result = ActivitiesExport.retrieve(config, id='618b6698-c6d0-42e9-8c4f-6a2bda5ac472') + result = ActivitiesExport.retrieve(config, id="618b6698-c6d0-42e9-8c4f-6a2bda5ac472") self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, {'id': ['618b6698-c6d0-42e9-8c4f-6a2bda5ac472']}) - self.assertEqual(result.get().status, 'succeeded') + self.assertEqual( + mock_requests.last_request.qs, {"id": ["618b6698-c6d0-42e9-8c4f-6a2bda5ac472"]} + ) + self.assertEqual(result.get().status, "succeeded") diff --git a/test/api/test_activity.py b/test/api/test_activity.py index c7573bd..1b7a631 100644 --- a/test/api/test_activity.py +++ b/test/api/test_activity.py @@ -10,35 +10,37 @@ class ActivitiesTestCase(unittest.TestCase): """ Tests CustomerActivities """ + @requests_mock.mock() def test_all(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/activities", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "entries":[ - { - "description": "purchased the plan_11 plan", - "activity-mrr-movement": 6000, - "activity-mrr": 6000, - "activity-arr": 72000, - "date": "2020-05-06T01:00:00", - "type": "new_biz", - "currency": "USD", - "subscription-external-id": "sub_2", - "plan-external-id": "11", - "customer-name": "customer_2", - "customer-uuid": "8bc55ab6-c3b5-11eb-ac45-2f9a49d75af7", - "customer-external-id": "customer_2", - "billing-connector-uuid": "99076cb8-97a1-11eb-8798-a73b507e7929", - "uuid": "f1a49735-21c7-4e3f-9ddc-67927aaadcf4" - }, - ], - "has_more":False, - "per_page":200, - } + "entries": [ + { + "description": "purchased the plan_11 plan", + "activity-mrr-movement": 6000, + "activity-mrr": 6000, + "activity-arr": 72000, + "date": "2020-05-06T01:00:00", + "type": "new_biz", + "currency": "USD", + "subscription-external-id": "sub_2", + "plan-external-id": "11", + "customer-name": "customer_2", + "customer-uuid": "8bc55ab6-c3b5-11eb-ac45-2f9a49d75af7", + "customer-external-id": "customer_2", + "billing-connector-uuid": "99076cb8-97a1-11eb-8798-a73b507e7929", + "uuid": "f1a49735-21c7-4e3f-9ddc-67927aaadcf4", + }, + ], + "has_more": False, + "per_page": 200, + "cursor": "cursor==", + }, ) config = Config("token") # is actually checked in mock result = Activity.all(config).get() @@ -46,4 +48,6 @@ def test_all(self, mock_requests): self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(result.__class__.__name__, Activity._many.__name__) - self.assertEqual(result.entries[0].uuid, 'f1a49735-21c7-4e3f-9ddc-67927aaadcf4') + self.assertEqual(result.entries[0].uuid, "f1a49735-21c7-4e3f-9ddc-67927aaadcf4") + self.assertFalse(result.has_more) + self.assertEqual(result.cursor, "cursor==") diff --git a/test/api/test_common.py b/test/api/test_common.py index 4d53556..115b248 100644 --- a/test/api/test_common.py +++ b/test/api/test_common.py @@ -11,18 +11,17 @@ class CommonTestCase(unittest.TestCase): """ Tests errors & user mistakes. """ + @requests_mock.mock() def test_forget_uuid_destroy(self, mock_requests): mock_requests.register_uri( - 'DELETE', - "https://api.chartmogul.com/v1/data_sources/my_uuid", - status_code=204 + "DELETE", "https://api.chartmogul.com/v1/data_sources/my_uuid", status_code=204 ) mock_requests.register_uri( - 'DELETE', + "DELETE", "https://api.chartmogul.com/v1/data_sources", status_code=404, - text="Not found" + text="Not found", ) config = Config("token") @@ -31,17 +30,24 @@ def test_forget_uuid_destroy(self, mock_requests): except ArgumentMissingError: pass else: - self.fail('ArgumentMissingError not raised') + self.fail("ArgumentMissingError not raised") @requests_mock.mock() def test_forget_uuid_retrieve(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/data_sources", status_code=200, - json={"data_sources": [{"name": "test", "uuid": "my_uuid", - "created_at": "2016-01-10 15:34:05", - "status": "never_imported"}]} + json={ + "data_sources": [ + { + "name": "test", + "uuid": "my_uuid", + "created_at": "2016-01-10 15:34:05", + "status": "never_imported", + } + ] + }, ) config = Config("token") @@ -50,19 +56,15 @@ def test_forget_uuid_retrieve(self, mock_requests): except ArgumentMissingError: pass else: - self.fail('ArgumentMissingError not raised') + self.fail("ArgumentMissingError not raised") @requests_mock.mock() def test_api_incorrect(self, mock_requests): mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/data_sources", status_code=400, - json={ - "code": 400, - "message": "Parameter \"name\" is missing", - "param": "name" - } + json={"code": 400, "message": 'Parameter "name" is missing', "param": "name"}, ) config = Config("token") @@ -71,27 +73,23 @@ def test_api_incorrect(self, mock_requests): except APIError as err: self.assertTrue(isinstance(err.__cause__, HTTPError)) else: - self.fail('ArgumentMissingError not raised') + self.fail("ArgumentMissingError not raised") @requests_mock.mock() def test_date_serialization(self, mock_requests): mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/data_sources", status_code=200, # whatever, not testing this - json={'data_sources': []} + json={"data_sources": []}, ) config = Config("token") - DataSource.create(config, data={ - "test_date": date(2015, 1, 1) - }).get() - self.assertEqual(mock_requests.last_request.json(), - {"test_date": u"2015-01-01"}) + DataSource.create(config, data={"test_date": date(2015, 1, 1)}).get() + self.assertEqual(mock_requests.last_request.json(), {"test_date": "2015-01-01"}) - DataSource.create(config, data={ - "test_datetime": datetime(2015, 1, 1, 1, 2, 3) - }).get() - self.assertEqual(mock_requests.last_request.json(), { - 'test_datetime': u'2015-01-01T01:02:03'}) + DataSource.create(config, data={"test_datetime": datetime(2015, 1, 1, 1, 2, 3)}).get() + self.assertEqual( + mock_requests.last_request.json(), {"test_datetime": "2015-01-01T01:02:03"} + ) diff --git a/test/api/test_contact.py b/test/api/test_contact.py index b9e5e9b..3cd041c 100644 --- a/test/api/test_contact.py +++ b/test/api/test_contact.py @@ -18,10 +18,7 @@ "linked_in": "https://linkedin.com/not_found", "twitter": "https://twitter.com/not_found", "notes": "Heading\nBody\nFooter", - "custom": { - "MyStringAttribute": "Test", - "MyIntegerAttribute": 123 - } + "custom": {"MyStringAttribute": "Test", "MyIntegerAttribute": 123}, } createContact = { @@ -38,16 +35,12 @@ "twitter": "https://twitter.com/not_found", "notes": "Heading\nBody\nFooter", "custom": [ - { "key": "MyStringAttribute", "value": "Test" }, - { "key": "MyIntegerAttribute", "value": 123 } - ] + {"key": "MyStringAttribute", "value": "Test"}, + {"key": "MyIntegerAttribute", "value": 123}, + ], } -allContacts = { - "entries": [contact], - "cursor": "MjAyMy0wMy0xMFQwMzo1MzoxNS44MTg1MjUwMDArMDA6MDAmY29uXzE2NDcwZjk4LWJlZjctMTFlZC05MjA4LTdiMDhhNDBmMzA0OQ==", - "has_more": False -} +allContacts = {"entries": [contact], "cursor": "cursor==", "has_more": False} class ContactTestCase(unittest.TestCase): @@ -61,30 +54,37 @@ def test_all(self, mock_requests): "GET", "https://api.chartmogul.com/v1/contacts?cursor=Ym9veWFo&per_page=1&data_source_uuid=ds_00000000-0000-0000-0000-000000000000", status_code=200, - json=allContacts + json=allContacts, ) config = Config("token") - contacts = Contact.all(config, data_source_uuid="ds_00000000-0000-0000-0000-000000000000", cursor="Ym9veWFo", per_page=1).get() + contacts = Contact.all( + config, + data_source_uuid="ds_00000000-0000-0000-0000-000000000000", + cursor="Ym9veWFo", + per_page=1, + ).get() expected = Contact._many(**allContacts) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - "cursor": ["ym9vewfo"], - "per_page": ["1"], - "data_source_uuid": ["ds_00000000-0000-0000-0000-000000000000"] - }) + self.assertEqual( + mock_requests.last_request.qs, + { + "cursor": ["ym9vewfo"], + "per_page": ["1"], + "data_source_uuid": ["ds_00000000-0000-0000-0000-000000000000"], + }, + ) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(dir(contacts), dir(expected)) self.assertTrue(isinstance(contacts.entries[0], Contact)) + self.assertFalse(contacts.has_more) + self.assertEqual(contacts.cursor, "cursor==") @requests_mock.mock() def test_create(self, mock_requests): mock_requests.register_uri( - "POST", - "https://api.chartmogul.com/v1/contacts", - status_code=200, - json=contact + "POST", "https://api.chartmogul.com/v1/contacts", status_code=200, json=contact ) config = Config("token") @@ -99,11 +99,15 @@ def test_merge(self, mock_requests): "POST", "https://api.chartmogul.com/v1/contacts/con_00000000-0000-0000-0000-000000000000/merge/con_00000000-0000-0000-0000-000000000001", status_code=200, - json=contact + json=contact, ) config = Config("token") - expected = Contact.merge(config, into_uuid="con_00000000-0000-0000-0000-000000000000", from_uuid="con_00000000-0000-0000-0000-000000000001").get() + expected = Contact.merge( + config, + into_uuid="con_00000000-0000-0000-0000-000000000000", + from_uuid="con_00000000-0000-0000-0000-000000000001", + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) @@ -111,23 +115,23 @@ def test_merge(self, mock_requests): @requests_mock.mock() def test_modify(self, mock_requests): - mock_requests.register_uri( - "PATCH", - "https://api.chartmogul.com/v1/contacts/con_00000000-0000-0000-0000-000000000000", - status_code=200, - json=contact - ) - - jsonRequest = { - "email": "test2@example.com" - } - config = Config("token") - expected = Contact.modify(config, uuid="con_00000000-0000-0000-0000-000000000000", data=jsonRequest).get() - - self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), jsonRequest) - self.assertTrue(isinstance(expected, Contact)) + mock_requests.register_uri( + "PATCH", + "https://api.chartmogul.com/v1/contacts/con_00000000-0000-0000-0000-000000000000", + status_code=200, + json=contact, + ) + + jsonRequest = {"email": "test2@example.com"} + config = Config("token") + expected = Contact.modify( + config, uuid="con_00000000-0000-0000-0000-000000000000", data=jsonRequest + ).get() + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.json(), jsonRequest) + self.assertTrue(isinstance(expected, Contact)) @requests_mock.mock() def test_retrieve(self, mock_requests): @@ -135,7 +139,7 @@ def test_retrieve(self, mock_requests): "GET", "https://api.chartmogul.com/v1/contacts/con_00000000-0000-0000-0000-000000000000", status_code=200, - json=contact + json=contact, ) config = Config("token") @@ -151,7 +155,7 @@ def test_destroy(self, mock_requests): "DELETE", "https://api.chartmogul.com/v1/contacts/con_00000000-0000-0000-0000-000000000000", status_code=200, - json={} + json={}, ) config = Config("token") diff --git a/test/api/test_custom_attrs.py b/test/api/test_custom_attrs.py index 98ba97d..7b0fa7f 100644 --- a/test/api/test_custom_attrs.py +++ b/test/api/test_custom_attrs.py @@ -6,36 +6,30 @@ # First case data dictData = { - 'custom': [ - {'type': 'String', 'key': 'channel', - 'value': 'Facebook'}, - {'type': 'Integer', - 'key': 'age', 'value': 8}, - {'type': 'Timestamp', - 'key': 'convertedAt', 'value': datetime(2015, 9, 8, 0, 0, 0)} + "custom": [ + {"type": "String", "key": "channel", "value": "Facebook"}, + {"type": "Integer", "key": "age", "value": 8}, + {"type": "Timestamp", "key": "convertedAt", "value": datetime(2015, 9, 8, 0, 0, 0)}, ] } jsonData = { - 'custom': [ - {'type': 'String', 'key': 'channel', - 'value': 'Facebook'}, - {'type': 'Integer', - 'key': 'age', 'value': 8}, - {'type': 'Timestamp', - 'key': 'convertedAt', 'value': '2015-09-08 00:00:00'} + "custom": [ + {"type": "String", "key": "channel", "value": "Facebook"}, + {"type": "Integer", "key": "age", "value": 8}, + {"type": "Timestamp", "key": "convertedAt", "value": "2015-09-08 00:00:00"}, ] } simpleJSONResult = { - 'custom': { - 'CAC': 213, - 'utmCampaign': 'social media 1', - 'convertedAt': '2015-09-08 00:00:00', - 'pro': False, - 'salesRep': 'Gabi', - 'channel': 'Facebook', - 'age': 8 + "custom": { + "CAC": 213, + "utmCampaign": "social media 1", + "convertedAt": "2015-09-08 00:00:00", + "pro": False, + "salesRep": "Gabi", + "channel": "Facebook", + "age": 8, } } @@ -43,9 +37,7 @@ jsonRequest2 = { "email": "adam@smith.com", - "custom": [ - {"type": "String", "key": "channel", "value": "Facebook"} - ] + "custom": [{"type": "String", "key": "channel", "value": "Facebook"}], } jsonResponse2 = { @@ -60,16 +52,10 @@ "status": "Active", "attributes": { "tags": ["important", "Prio1"], - "stripe": { - "coupon": True - }, - "clearbit": { - "name": "Acme" - }, - "custom": { - "channel": "Facebook" - } - } + "stripe": {"coupon": True}, + "clearbit": {"name": "Acme"}, + "custom": {"channel": "Facebook"}, + }, }, { "id": 13456, @@ -81,17 +67,11 @@ "status": "Active", "attributes": { "tags": ["important", "Prio1"], - "stripe": { - "coupon": False - }, - "clearbit": { - "name": "Umbrella Corp." - }, - "custom": { - "channel": "Facebook" - } - } - } + "stripe": {"coupon": False}, + "clearbit": {"name": "Umbrella Corp."}, + "custom": {"channel": "Facebook"}, + }, + }, ] } @@ -104,44 +84,40 @@ class CustomAttributesTestCase(unittest.TestCase): @requests_mock.mock() def test_add(self, mock_requests): mock_requests.register_uri( - 'POST', - 'https://api.chartmogul.com/v1/customers/CUSTOMER_UUID/attributes/custom', + "POST", + "https://api.chartmogul.com/v1/customers/CUSTOMER_UUID/attributes/custom", status_code=200, - json=simpleJSONResult + json=simpleJSONResult, ) expected = CustomAttributes(**simpleJSONResult) - config = Config('token') - result = CustomAttributes.add(config, - uuid='CUSTOMER_UUID', - data=jsonData).get() + config = Config("token") + result = CustomAttributes.add(config, uuid="CUSTOMER_UUID", data=jsonData).get() - self.assertEqual(mock_requests.call_count, 1, 'expected call') + self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), jsonData) self.assertTrue(isinstance(result, CustomAttributes)) - self.assertEqual(result.custom['CAC'], expected.custom['CAC']) - self.assertEqual(result.custom['utmCampaign'], expected.custom['utmCampaign']) - self.assertEqual(result.custom['convertedAt'], expected.custom['convertedAt']) - self.assertEqual(result.custom['pro'], expected.custom['pro']) - self.assertEqual(result.custom['salesRep'], expected.custom['salesRep']) + self.assertEqual(result.custom["CAC"], expected.custom["CAC"]) + self.assertEqual(result.custom["utmCampaign"], expected.custom["utmCampaign"]) + self.assertEqual(result.custom["convertedAt"], expected.custom["convertedAt"]) + self.assertEqual(result.custom["pro"], expected.custom["pro"]) + self.assertEqual(result.custom["salesRep"], expected.custom["salesRep"]) @requests_mock.mock() def test_add_to_email(self, mock_requests): mock_requests.register_uri( - 'POST', - 'https://api.chartmogul.com/v1/customers/CUSTOMER_UUID/attributes/custom', + "POST", + "https://api.chartmogul.com/v1/customers/CUSTOMER_UUID/attributes/custom", status_code=200, - json=jsonResponse2 + json=jsonResponse2, ) - config = Config('token') - result = CustomAttributes.add(config, - uuid='CUSTOMER_UUID', - data=jsonRequest2).get() + config = Config("token") + result = CustomAttributes.add(config, uuid="CUSTOMER_UUID", data=jsonRequest2).get() - self.assertEqual(mock_requests.call_count, 1, 'expected call') + self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), jsonRequest2) # No comparison, because unicode strings, dates, serialization order etc. diff --git a/test/api/test_customer.py b/test/api/test_customer.py index 358d6f3..9d5f4bf 100644 --- a/test/api/test_customer.py +++ b/test/api/test_customer.py @@ -7,91 +7,85 @@ from pprint import pprint -docsListSample = { - "entries": [ - { - "id": 25647, - "uuid": "cus_de305d54-75b4-431b-adb2-eb6b9e546012", - "external_id": "34916129", - "external_ids": ["34916129"], - "data_source_uuid": "ds_610b7a84-c50f-11e6-8aab-97d6db98913a", - "data_source_uuids": ["ds_610b7a84-c50f-11e6-8aab-97d6db98913a"], - "name": "Example Company", - "company": "", - "email": "bob@examplecompany.com", - "status": "Active", - "lead_created_at": "2015-01-01T10:00:00-04:00", - "free_trial_started_at": "2015-01-09T10:00:00-04:00", - "customer-since": "2015-06-09T13:16:00-04:00", - "city": "Nowhereville", - "state": "Alaska", - "country": "US", - "zip": "0185128", - "attributes":{ - "tags": ["engage", "unit loss", "discountable"], - "stripe":{ - "uid": 7, - "coupon": True +entry = { + "id": 25647, + "uuid": "cus_de305d54-75b4-431b-adb2-eb6b9e546012", + "external_id": "34916129", + "external_ids": ["34916129"], + "data_source_uuid": "ds_610b7a84-c50f-11e6-8aab-97d6db98913a", + "data_source_uuids": ["ds_610b7a84-c50f-11e6-8aab-97d6db98913a"], + "name": "Example Company", + "company": "", + "email": "bob@examplecompany.com", + "status": "Active", + "lead_created_at": "2015-01-01T10:00:00-04:00", + "free_trial_started_at": "2015-01-09T10:00:00-04:00", + "customer-since": "2015-06-09T13:16:00-04:00", + "city": "Nowhereville", + "state": "Alaska", + "country": "US", + "zip": "0185128", + "attributes": { + "tags": ["engage", "unit loss", "discountable"], + "stripe": {"uid": 7, "coupon": True}, + "clearbit": { + "company": { + "name": "Example Company", + "legalName": "Example Company Inc.", + "domain": "examplecompany.com", + "url": "http://examplecompany.com", + "category": { + "sector": "Information Technology", + "industryGroup": "Software and Services", + "industry": "Software", + "subIndustry": "Application Software", }, - "clearbit": { - "company": { - "name": "Example Company", - "legalName": "Example Company Inc.", - "domain": "examplecompany.com", - "url": "http://examplecompany.com", - "category": { - "sector": "Information Technology", - "industryGroup": "Software and Services", - "industry": "Software", - "subIndustry": "Application Software" - }, - "metrics": { - "raised": 1502450000, - "employees": 1000, - "googleRank": 7, - "alexaGlobalRank": 2319, - "marketCap": None - }, - }, - "person": { - "name": { - "fullName": "Bob Kramer" - }, - "employment": { - "name": "Example Company" - } - } + "metrics": { + "raised": 1502450000, + "employees": 1000, + "googleRank": 7, + "alexaGlobalRank": 2319, + "marketCap": None, }, - "custom": { - "CAC": 213, - "utmCampaign": "social media 1", - "convertedAt": "2015-09-08 00:00:00", - "pro": False, - "salesRep": "Gabi" - } }, - "address": { - "address_zip": "0185128", - "city": "Nowhereville", - "country": "US", - "state": "Alaska" + "person": { + "name": {"fullName": "Bob Kramer"}, + "employment": {"name": "Example Company"}, }, - "mrr": 3000.0, - "arr": 36000.0, - "billing-system-url": "https:\/\/dashboard.stripe.com\/customers\/cus_4Z2ZpyJFuQ0XMb", - "chartmogul-url": "https:\/\/app.chartmogul.com\/#customers\/25647-Example_Company", - "billing-system-type": "Stripe", - "currency": "USD", - "currency-sign": "$" - } - ], - "has_more": True, + }, + "custom": { + "CAC": 213, + "utmCampaign": "social media 1", + "convertedAt": "2015-09-08 00:00:00", + "pro": False, + "salesRep": "Gabi", + }, + }, + "address": { + "address_zip": "0185128", + "city": "Nowhereville", + "country": "US", + "state": "Alaska", + }, + "mrr": 3000.0, + "arr": 36000.0, + "billing-system-url": "https:\/\/dashboard.stripe.com\/customers\/cus_4Z2ZpyJFuQ0XMb", + "chartmogul-url": "https:\/\/app.chartmogul.com\/#customers\/25647-Example_Company", + "billing-system-type": "Stripe", + "currency": "USD", + "currency-sign": "$", +} + +allContactsOld = { + "entries": [entry], "per_page": 50, "page": 1, "current_page": 1, - "total_pages": 4 + "total_pages": 4, } +allContactsNew = {"entries": [entry], "cursor": "cursor==", "has_more": True} + deserializedCustomer = Customer( id=25647, uuid="cus_de305d54-75b4-431b-adb2-eb6b9e546012", @@ -112,10 +106,7 @@ zip="0185128", attributes=Attributes( tags=["engage", "unit loss", "discountable"], - stripe={ - "uid": 7, - "coupon": True - }, + stripe={"uid": 7, "coupon": True}, clearbit={ "company": { "name": "Example Company", @@ -126,46 +117,37 @@ "sector": "Information Technology", "industryGroup": "Software and Services", "industry": "Software", - "subIndustry": "Application Software" + "subIndustry": "Application Software", }, "metrics": { "alexaGlobalRank": 2319, "googleRank": 7, "employees": 1000, "marketCap": None, - "raised": 1502450000 + "raised": 1502450000, }, }, "person": { - "name": { - "fullName": "Bob Kramer" - }, - "employment": { - "name": "Example Company" - } - } + "name": {"fullName": "Bob Kramer"}, + "employment": {"name": "Example Company"}, + }, }, custom={ "CAC": 213, "utmCampaign": "social media 1", "convertedAt": "2015-09-08 00:00:00", "pro": False, - "salesRep": "Gabi" - } - ), - address=Address( - address_zip="0185128", - city="Nowhereville", - country="US", - state="Alaska" + "salesRep": "Gabi", + }, ), + address=Address(address_zip="0185128", city="Nowhereville", country="US", state="Alaska"), mrr=3000.0, arr=36000.0, billing_system_url="https:\/\/dashboard.stripe.com\/customers\/cus_4Z2ZpyJFuQ0XMb", chartmogul_url="https:\/\/app.chartmogul.com\/#customers\/25647-Example_Company", billing_system_type="Stripe", currency="USD", - currency_sign="$" + currency_sign="$", ) createCustomer = { @@ -183,10 +165,7 @@ "zip": "0185128", "attributes": { "tags": ["engage", "unit loss", "discountable"], - "stripe":{ - "uid": 7, - "coupon": True - }, + "stripe": {"uid": 7, "coupon": True}, "clearbit": { "company": { "name": "Example Company", @@ -197,41 +176,34 @@ "sector": "Information Technology", "industryGroup": "Software and Services", "industry": "Software", - "subIndustry": "Application Software" + "subIndustry": "Application Software", }, "metrics": { "raised": 1502450000, "employees": 1000, "googleRank": 7, "alexaGlobalRank": 2319, - "marketCap": None + "marketCap": None, }, }, "person": { - "name": { - "fullName": "Bob Kramer" - }, - "employment": { - "name": "Example Company" - } - } + "name": {"fullName": "Bob Kramer"}, + "employment": {"name": "Example Company"}, + }, }, "custom": [ - {'key': 'CAC', 'type': 'Integer', 'value': 213}, + {"key": "CAC", "type": "Integer", "value": 213}, {"key": "utmCampaign", "value": "social media 1", "type": "String"}, - {"key": "convertedAt", "value": datetime( - 2015, 9, 8), "type": "Timestamp"}, + {"key": "convertedAt", "value": datetime(2015, 9, 8), "type": "Timestamp"}, {"key": "pro", "value": False, "type": "Boolean"}, - {"key": "salesRep", "value": "Gabi", "type": "String"}] - } + {"key": "salesRep", "value": "Gabi", "type": "String"}, + ], + }, } sentCreateExpected = { - 'attributes': { - "stripe":{ - "uid": 7, - "coupon": True - }, + "attributes": { + "stripe": {"uid": 7, "coupon": True}, "clearbit": { "company": { "name": "Example Company", @@ -242,45 +214,42 @@ "sector": "Information Technology", "industryGroup": "Software and Services", "industry": "Software", - "subIndustry": "Application Software" + "subIndustry": "Application Software", }, "metrics": { "raised": 1502450000, "employees": 1000, "googleRank": 7, "alexaGlobalRank": 2319, - "marketCap": None + "marketCap": None, }, }, "person": { - "name": { - "fullName": "Bob Kramer" - }, - "employment": { - "name": "Example Company" - } - } + "name": {"fullName": "Bob Kramer"}, + "employment": {"name": "Example Company"}, + }, }, - 'custom': [ - {'key': 'CAC', 'type': 'Integer', 'value': 213}, + "custom": [ + {"key": "CAC", "type": "Integer", "value": 213}, {"key": "utmCampaign", "value": "social media 1", "type": "String"}, {"key": "convertedAt", "value": "2015-09-08T00:00:00", "type": "Timestamp"}, {"key": "pro", "value": False, "type": "Boolean"}, - {"key": "salesRep", "value": "Gabi", "type": "String"} + {"key": "salesRep", "value": "Gabi", "type": "String"}, ], - 'tags': ['engage', 'unit loss', 'discountable']}, - 'city': 'Nowhereville', - 'company': '', - 'country': 'US', - 'customer-since': '2015-06-09T13:16:00', - 'data_source_uuid': 'ds_610b7a84-c50f-11e6-8aab-97d6db98913a', - 'email': 'bob@examplecompany.com', - 'external_id': '34916129', - 'free_trial_started_at': '2015-01-09T10:00:00', - 'lead_created_at': '2015-01-01T10:00:00', - 'name': 'Example Company', - 'state': 'Alaska', - 'zip': '0185128' + "tags": ["engage", "unit loss", "discountable"], + }, + "city": "Nowhereville", + "company": "", + "country": "US", + "customer-since": "2015-06-09T13:16:00", + "data_source_uuid": "ds_610b7a84-c50f-11e6-8aab-97d6db98913a", + "email": "bob@examplecompany.com", + "external_id": "34916129", + "free_trial_started_at": "2015-01-09T10:00:00", + "lead_created_at": "2015-01-01T10:00:00", + "name": "Example Company", + "state": "Alaska", + "zip": "0185128", } contact = { @@ -297,10 +266,7 @@ "linked_in": "https://linkedin.com/not_found", "twitter": "https://twitter.com/not_found", "notes": "Heading\nBody\nFooter", - "custom": { - "MyStringAttribute": "Test", - "MyIntegerAttribute": 123 - } + "custom": {"MyStringAttribute": "Test", "MyIntegerAttribute": 123}, } createContact = { @@ -316,15 +282,9 @@ "twitter": "https://twitter.com/not_found", "notes": "Heading\nBody\nFooter", "custom": [ - { "key": "MyStringAttribute", "value": "Test" }, - { "key": "MyIntegerAttribute", "value": 123 } - ] -} - -allContacts = { - "entries": [contact], - "cursor": "MjAyMy0wMy0xMFQwMzo1MzoxNS44MTg1MjUwMDArMDA6MDAmY29uXzE2NDcwZjk4LWJlZjctMTFlZC05MjA4LTdiMDhhNDBmMzA0OQ==", - "has_more": False + {"key": "MyStringAttribute", "value": "Test"}, + {"key": "MyIntegerAttribute", "value": 123}, + ], } @@ -334,25 +294,20 @@ class CustomerTestCase(unittest.TestCase): """ @requests_mock.mock() - def test_all(self, mock_requests): + def test_all_old_pagination(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/customers", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=docsListSample + json=allContactsOld, ) config = Config("token") customers = Customer.all(config).get() expected = Customer._many( - entries=[deserializedCustomer], - has_more=True, - per_page=50, - page=1, - current_page=1, - total_pages=4 + entries=[deserializedCustomer], per_page=50, page=1, current_page=1, total_pages=4 ) self.assertEqual(mock_requests.call_count, 1, "expected call") @@ -364,17 +319,54 @@ def test_all(self, mock_requests): # self.assertEqual(str(customers), str(expected)) # => check only first level fields are OK self.assertEqual(sorted(dir(customers)), sorted(dir(expected))) - self.assertEqual(sorted(customers.entries[0].attributes.stripe), sorted(expected.entries[0].attributes.stripe)) - self.assertEqual(sorted(customers.entries[0].attributes.clearbit), sorted(expected.entries[0].attributes.clearbit)) + self.assertEqual( + sorted(customers.entries[0].attributes.stripe), + sorted(expected.entries[0].attributes.stripe), + ) + self.assertEqual( + sorted(customers.entries[0].attributes.clearbit), + sorted(expected.entries[0].attributes.clearbit), + ) self.assertTrue(isinstance(customers.entries[0], Customer)) @requests_mock.mock() - def test_create(self, mock_requests): + def test_all_new_pagination(self, mock_requests): mock_requests.register_uri( - 'POST', + "GET", "https://api.chartmogul.com/v1/customers", + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=docsListSample["entries"][0] + json=allContactsNew, + ) + + config = Config("token") + customers = Customer.all(config).get() + + expected = Customer._many(entries=[deserializedCustomer], has_more=True, cursor="cursor==") + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.text, None) + # Complete comparing too complicated, would need to: + # 1) sort all dictionaries, + # 2) use special class/library for timezones (Python has no default) + # self.assertEqual(str(customers), str(expected)) + # => check only first level fields are OK + self.assertEqual(sorted(dir(customers)), sorted(dir(expected))) + self.assertEqual( + sorted(customers.entries[0].attributes.stripe), + sorted(expected.entries[0].attributes.stripe), + ) + self.assertEqual( + sorted(customers.entries[0].attributes.clearbit), + sorted(expected.entries[0].attributes.clearbit), + ) + self.assertTrue(isinstance(customers.entries[0], Customer)) + + @requests_mock.mock() + def test_create(self, mock_requests): + mock_requests.register_uri( + "POST", "https://api.chartmogul.com/v1/customers", status_code=200, json=entry ) config = Config("token") @@ -384,34 +376,52 @@ def test_create(self, mock_requests): self.assertEqual(mock_requests.last_request.json(), sentCreateExpected) @requests_mock.mock() - def test_search(self, mock_requests): + def test_search_old_pagination(self, mock_requests): + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/customers/search?email=tralala@someemail.com", + status_code=200, + json=allContactsOld, + ) + + config = Config("token") + result = Customer.search(config, email="tralala@someemail.com").get() + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {"email": ["tralala@someemail.com"]}) + self.assertEqual(mock_requests.last_request.text, None) + self.assertTrue(isinstance(result, Customer._many)) + self.assertTrue(isinstance(result.entries[0], Customer)) + self.assertEqual(result.current_page, 1) + self.assertEqual(result.total_pages, 4) + + @requests_mock.mock() + def test_search_new_pagination(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/customers/search?email=tralala@someemail.com", status_code=200, - json=docsListSample + json=allContactsNew, ) config = Config("token") - result = Customer.search(config, email='tralala@someemail.com').get() + result = Customer.search(config, email="tralala@someemail.com").get() self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - 'email': ['tralala@someemail.com']}) + self.assertEqual(mock_requests.last_request.qs, {"email": ["tralala@someemail.com"]}) self.assertEqual(mock_requests.last_request.text, None) self.assertTrue(isinstance(result, Customer._many)) self.assertTrue(isinstance(result.entries[0], Customer)) + self.assertTrue(result.has_more) + self.assertEqual(result.cursor, "cursor==") @requests_mock.mock() def test_merge(self, mock_requests): mock_requests.register_uri( - 'POST', - "https://api.chartmogul.com/v1/customers/merges", - status_code=204 + "POST", "https://api.chartmogul.com/v1/customers/merges", status_code=204 ) jsonRequest = { "from": {"customer_uuid": "cus_de305d54-75b4-431b-adb2-eb6b9e546012"}, - "into": {"customer_uuid": "cus_ab223d54-75b4-431b-adb2-eb6b9e234571"} + "into": {"customer_uuid": "cus_ab223d54-75b4-431b-adb2-eb6b9e234571"}, } config = Config("token") @@ -420,28 +430,31 @@ def test_merge(self, mock_requests): self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), jsonRequest) self.assertEqual(result, None) + @requests_mock.mock() def test_connectSubscriptions(self, mock_requests): mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/customers/cus_5915ee5a-babd-406b-b8ce-d207133fb4cb/connect_subscriptions", - status_code=202 + status_code=202, ) jsonRequest = { - 'subscriptions': [ - { - "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0" - }, - { - "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4" - } - ] + "subscriptions": [ + { + "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", + "external_id": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0", + }, + { + "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", + "external_id": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4", + }, + ] } config = Config("token") - result = Customer.connectSubscriptions(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data=jsonRequest).get() + result = Customer.connectSubscriptions( + config, uuid="cus_5915ee5a-babd-406b-b8ce-d207133fb4cb", data=jsonRequest + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), jsonRequest) @@ -450,45 +463,43 @@ def test_connectSubscriptions(self, mock_requests): @requests_mock.mock() def test_modify_uuid_missing(self, mock_requests): mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/customers/", status_code=400, - json= { + json={ "code": 400, "message": "Please pass 'uuid' parameter", - } + }, ) - jsonRequest = { - "country": "US" - } + jsonRequest = {"country": "US"} config = Config("token") with self.assertRaises(APIError) as context: - result = Customer.modify(config, uuid='', data=jsonRequest).get() + result = Customer.modify(config, uuid="", data=jsonRequest).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertTrue("Please pass \\\'uuid\\\' parameter" in str(context.exception)) + self.assertTrue("Please pass \\'uuid\\' parameter" in str(context.exception)) @requests_mock.mock() def test_modify(self, mock_requests): - mock_requests.register_uri( - 'PATCH', - "https://api.chartmogul.com/v1/customers/cus_5915ee5a-babd-406b-b8ce-d207133fb4cb", - status_code=200 - ) + mock_requests.register_uri( + "PATCH", + "https://api.chartmogul.com/v1/customers/cus_5915ee5a-babd-406b-b8ce-d207133fb4cb", + status_code=200, + ) - jsonRequest = { - "country": "US" - } - config = Config("token") + jsonRequest = {"country": "US"} + config = Config("token") - result = Customer.modify(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data=jsonRequest).get() + result = Customer.modify( + config, uuid="cus_5915ee5a-babd-406b-b8ce-d207133fb4cb", data=jsonRequest + ).get() - self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), jsonRequest) + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.json(), jsonRequest) @requests_mock.mock() def test_contacts(self, mock_requests): @@ -496,18 +507,20 @@ def test_contacts(self, mock_requests): "GET", "https://api.chartmogul.com/v1/customers/cus_00000000-0000-0000-0000-000000000000/contacts", status_code=200, - json=allContacts + json=allContactsNew, ) config = Config("token") contacts = Customer.contacts(config, uuid="cus_00000000-0000-0000-0000-000000000000").get() - expected = Contact._many(**allContacts) + expected = Contact._many(**allContactsNew) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(sorted(dir(contacts)), sorted(dir(expected))) self.assertTrue(isinstance(contacts.entries[0], Contact)) + self.assertEqual(contacts.cursor, "cursor==") + self.assertTrue(contacts.has_more) @requests_mock.mock() def test_createContact(self, mock_requests): @@ -515,11 +528,13 @@ def test_createContact(self, mock_requests): "POST", "https://api.chartmogul.com/v1/customers/cus_00000000-0000-0000-0000-000000000000/contacts", status_code=200, - json=contact + json=contact, ) config = Config("token") - expected = Customer.createContact(config, uuid="cus_00000000-0000-0000-0000-000000000000", data=createContact).get() + expected = Customer.createContact( + config, uuid="cus_00000000-0000-0000-0000-000000000000", data=createContact + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) diff --git a/test/api/test_customers/test_activity.py b/test/api/test_customers/test_activity.py index 86deac8..3dac82c 100644 --- a/test/api/test_customers/test_activity.py +++ b/test/api/test_customers/test_activity.py @@ -10,32 +10,34 @@ class CustomerActivitiesTestCase(unittest.TestCase): """ Tests CustomerActivities """ + @requests_mock.mock() def test_all(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/customers/some_uuid/activities", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "entries":[ - { - "activity-arr": 24000, - "activity-mrr": 2000, - "activity-mrr-movement": 2000, - "currency": "USD", - "currency-sign": "$", - "date": "2015-06-09T13:16:00-04:00", - "description": "purchased the Silver Monthly plan (1)", - "id": 48730, - "type": "new_biz", - "subscription-external-id": "1" - } - ], - "has_more":False, - "per_page":200, - "page":1 - } + "entries": [ + { + "activity-arr": 24000, + "activity-mrr": 2000, + "activity-mrr-movement": 2000, + "currency": "USD", + "currency-sign": "$", + "date": "2015-06-09T13:16:00-04:00", + "description": "purchased the Silver Monthly plan (1)", + "id": 48730, + "type": "new_biz", + "subscription-external-id": "1", + } + ], + "has_more": False, + "per_page": 200, + "page": 1, + "cursor": "cursor==", + }, ) config = Config("token") # is actually checked in mock result = CustomerActivity.all(config, uuid="some_uuid").get() @@ -44,3 +46,5 @@ def test_all(self, mock_requests): self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(result.__class__.__name__, CustomerActivity._many.__name__) self.assertEqual(result.entries[0].id, 48730) + self.assertEqual(result.cursor, "cursor==") + self.assertFalse(result.has_more) diff --git a/test/api/test_customers/test_subscription.py b/test/api/test_customers/test_subscription.py index ccd42cc..563a3d4 100644 --- a/test/api/test_customers/test_subscription.py +++ b/test/api/test_customers/test_subscription.py @@ -13,12 +13,11 @@ class CustomerSubscriptionsTestCase(unittest.TestCase): @requests_mock.mock() def test_cancel_subscription(self, mock_requests): - """ Test cancel (patch) subscription (cancelled_at). - """ + """Test cancel (patch) subscription (cancelled_at).""" mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/import/subscriptions/some_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ "uuid": "some_uuid", @@ -26,31 +25,27 @@ def test_cancel_subscription(self, mock_requests): "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", "cancellation_dates": ["2016-01-15T00:00:00.000Z"], - "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430" - } + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + }, ) config = Config("token") # is actually checked in mock - result = CustomerSubscription.cancel(config, - uuid="some_uuid", - data={ - "cancelled_at": datetime(2016, 1, 15, 0, 0, 0) - }).get() + result = CustomerSubscription.cancel( + config, uuid="some_uuid", data={"cancelled_at": datetime(2016, 1, 15, 0, 0, 0)} + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), { - "cancelled_at": "2016-01-15T00:00:00"}) + self.assertEqual(mock_requests.last_request.json(), {"cancelled_at": "2016-01-15T00:00:00"}) self.assertTrue(isinstance(result, CustomerSubscription)) self.assertEqual(result.uuid, "some_uuid") @requests_mock.mock() def test_modify_subscription(self, mock_requests): - """ Test modify (patch) subscription (cancellation_dates). - """ + """Test modify (patch) subscription (cancellation_dates).""" mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/import/subscriptions/some_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ "uuid": "some_uuid", @@ -58,51 +53,55 @@ def test_modify_subscription(self, mock_requests): "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", "cancellation_dates": [], - "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430" - } + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + }, ) config = Config("token") # is actually checked in mock - result = CustomerSubscription.modify(config, uuid="some_uuid", data={ - "cancellation_dates": []}).get() + result = CustomerSubscription.modify( + config, uuid="some_uuid", data={"cancellation_dates": []} + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), - {"cancellation_dates": []}) + self.assertEqual(mock_requests.last_request.json(), {"cancellation_dates": []}) self.assertEqual(result.__class__, CustomerSubscription) - self.assertEqual(result.__dict__, CustomerSubscription(**{ - "cancellation_dates": [], - "customer_uuid": u"cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", - "data_source_uuid": u"ds_fef05d54-47b4-431b-aed2-eb6b9e545430", - "uuid": u"some_uuid", - "external_id": u"sub_0001", - "plan_uuid": u"pl_eed05d54-75b4-431b-adb2-eb6b9e543206" - }).__dict__) + self.assertEqual( + result.__dict__, + CustomerSubscription( + **{ + "cancellation_dates": [], + "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + "uuid": "some_uuid", + "external_id": "sub_0001", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + } + ).__dict__, + ) @requests_mock.mock() def test_list_imported_subscriptions(self, mock_requests): - """ Test listing (get) subscriptions. - """ + """Test listing (get) subscriptions.""" mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/import/customers/some_uuid/subscriptions", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "customer_uuid": "some_uuid", - "subscriptions":[ + "customer_uuid": "some_uuid", + "subscriptions": [ { - "uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", - "external_id": "sub_0001", - "subscription_set_external_id": "sub_set_0001", - "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", - "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", - "cancellation_dates":[] + "uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", + "external_id": "sub_0001", + "subscription_set_external_id": "sub_set_0001", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + "cancellation_dates": [], } - ], - "current_page": 1, - "total_pages": 1 - } + ], + "cursor": "cursor==", + "has_more": False, + }, ) config = Config("token") # is actually checked in mock result = CustomerSubscription.list_imported(config, uuid="some_uuid").get() @@ -114,35 +113,35 @@ def test_list_imported_subscriptions(self, mock_requests): @requests_mock.mock() def test_all(self, mock_requests): - """ Test getting metrics of all subscriptions for a customer. - """ + """Test getting metrics of all subscriptions for a customer.""" mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/customers/some_uuid/subscriptions", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "entries": [ - { - "id": 9306830, - "external_id": "sub_0001", - "plan": "PRO Plan (10,000 active cust.) monthly", - "quantity": 1, - "mrr": 70800, - "arr": 849600, - "status": "active", - "billing-cycle": "month", - "billing-cycle-count": 1, - "start-date": "2015-12-20T08:26:49-05:00", - "end-date": "2016-03-20T09:26:49-05:00", - "currency": "USD", - "currency-sign": "$" - } - ], - "has_more": False, - "per_page": 200, - "page": 1 - } + "entries": [ + { + "id": 9306830, + "external_id": "sub_0001", + "plan": "PRO Plan (10,000 active cust.) monthly", + "quantity": 1, + "mrr": 70800, + "arr": 849600, + "status": "active", + "billing-cycle": "month", + "billing-cycle-count": 1, + "start-date": "2015-12-20T08:26:49-05:00", + "end-date": "2016-03-20T09:26:49-05:00", + "currency": "USD", + "currency-sign": "$", + } + ], + "has_more": False, + "per_page": 200, + "page": 1, + "cursor": "cursor==", + }, ) config = Config("token") # is actually checked in mock result = CustomerSubscription.all(config, uuid="some_uuid").get() @@ -151,3 +150,5 @@ def test_all(self, mock_requests): self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(result.__class__.__name__, CustomerSubscription._many.__name__) self.assertEqual(result.entries[0].external_id, "sub_0001") + self.assertEqual(result.page, 1) + self.assertEqual(result.cursor, "cursor==") diff --git a/test/api/test_data_source.py b/test/api/test_data_source.py index f630777..902ad98 100644 --- a/test/api/test_data_source.py +++ b/test/api/test_data_source.py @@ -15,13 +15,15 @@ class DataSourceTestCase(unittest.TestCase): @requests_mock.mock() def test_create(self, mock_requests): mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/data_sources", status_code=200, - json={"name": "test", - "uuid": "my_uuid", - "created_at": "2016-01-10T15:34:05.144Z", - "status": "never_imported"} + json={ + "name": "test", + "uuid": "my_uuid", + "created_at": "2016-01-10T15:34:05.144Z", + "status": "never_imported", + }, ) config = Config("token") @@ -33,55 +35,72 @@ def test_create(self, mock_requests): # Direct comparison impossible because of tzinfo difference between 2.7 and 3.3+ self.assertTrue(isinstance(ds, DataSource)) self.assertTrue(isinstance(ds.created_at, datetime)) - self.assertEqual(ds.uuid, u"my_uuid") + self.assertEqual(ds.uuid, "my_uuid") @requests_mock.mock() def test_retrieve(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/data_sources/my_uuid", status_code=200, - json={"name": "test", - "uuid": "my_uuid", - "created_at": "2016-01-10T15:34:05Z", - "status": "never_imported"} + json={ + "name": "test", + "uuid": "my_uuid", + "created_at": "2016-01-10T15:34:05Z", + "status": "never_imported", + }, ) config = Config("token") ds = DataSource.retrieve(config, uuid="my_uuid").get() - expected = DataSource(**{"name": u"test", - "uuid": u"my_uuid", - "created_at": datetime(2016, 1, 10, 15, 34, 5), - "status": u"never_imported"}) + expected = DataSource( + **{ + "name": "test", + "uuid": "my_uuid", + "created_at": datetime(2016, 1, 10, 15, 34, 5), + "status": "never_imported", + } + ) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.text, None) self.assertTrue(isinstance(ds, DataSource)) self.assertTrue(isinstance(ds.created_at, datetime)) - self.assertEqual(ds.name, u"test") + self.assertEqual(ds.name, "test") @requests_mock.mock() def test_all(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/data_sources", status_code=200, - json={"data_sources": [ - {"name": "test", - "uuid": "my_uuid", - "created_at": "2016-01-10T15:34:05Z", - "status": "never_imported"} - ]} + json={ + "data_sources": [ + { + "name": "test", + "uuid": "my_uuid", + "created_at": "2016-01-10T15:34:05Z", + "status": "never_imported", + } + ] + }, ) config = Config("token") ds = DataSource.all(config).get() - expected = DataSource._many(data_sources=[ - DataSource(**{"name": u"test", - "uuid": u"my_uuid", - "created_at": datetime(2016, 1, 10, 15, 34, 5), - "status": u"never_imported"})]) + expected = DataSource._many( + data_sources=[ + DataSource( + **{ + "name": "test", + "uuid": "my_uuid", + "created_at": datetime(2016, 1, 10, 15, 34, 5), + "status": "never_imported", + } + ) + ] + ) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) @@ -91,9 +110,7 @@ def test_all(self, mock_requests): @requests_mock.mock() def test_destroy(self, mock_requests): mock_requests.register_uri( - 'DELETE', - "https://api.chartmogul.com/v1/data_sources/my_uuid", - status_code=204 + "DELETE", "https://api.chartmogul.com/v1/data_sources/my_uuid", status_code=204 ) config = Config("token") diff --git a/test/api/test_invoice.py b/test/api/test_invoice.py index 2608692..24cabb4 100644 --- a/test/api/test_invoice.py +++ b/test/api/test_invoice.py @@ -33,7 +33,7 @@ "quantity": 1, "discount_code": "PSO86", "discount_amount_in_cents": 1000, - "tax_amount_in_cents": 900 + "tax_amount_in_cents": 900, }, { "type": "one_time", @@ -46,8 +46,8 @@ "discount_description": "Special 20 % discount", "transaction_fees_in_cents": 50, "transaction_fees_currency": "CZK", - "event_order": 5 - } + "event_order": 5, + }, ], "transactions": [ { @@ -55,33 +55,33 @@ "date": datetime(2015, 11, 5, 0, 4, 3), "type": "payment", "result": "successful", - "amount_in_cents": 7500 + "amount_in_cents": 7500, } - ] + ], } ] } requestSerialized = { - u"invoices": [ + "invoices": [ { - u"external_id": u"INV0001", - u"date": u"2015-11-01T00:00:00", - u"currency": u"USD", - u"due_date": u"2015-11-15T00:00:00", - u"customer_external_id": u"ext-id", - u"line_items": [ + "external_id": "INV0001", + "date": "2015-11-01T00:00:00", + "currency": "USD", + "due_date": "2015-11-15T00:00:00", + "customer_external_id": "ext-id", + "line_items": [ { - u"type": "subscription", - u"subscription_external_id": "sub_0001", - u"plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", - u"service_period_start": "2015-11-01T00:00:00", - u"service_period_end": "2015-12-01T00:00:00", - u"amount_in_cents": 5000, - u"quantity": 1, - u"discount_code": "PSO86", - u"discount_amount_in_cents": 1000, - u"tax_amount_in_cents": 900 + "type": "subscription", + "subscription_external_id": "sub_0001", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + "service_period_start": "2015-11-01T00:00:00", + "service_period_end": "2015-12-01T00:00:00", + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, }, { "type": "one_time", @@ -94,17 +94,17 @@ "discount_description": "Special 20 % discount", "transaction_fees_in_cents": 50, "transaction_fees_currency": "CZK", - "event_order": 5 - } + "event_order": 5, + }, ], "transactions": [ { "date": "2015-11-05T00:04:03", "type": "payment", "result": "successful", - "amount_in_cents": 7500 + "amount_in_cents": 7500, } - ] + ], } ] } @@ -132,7 +132,7 @@ "discount_code": "PSO86", "discount_amount_in_cents": 1000, "tax_amount_in_cents": 900, - "account_code": None + "account_code": None, }, { "uuid": "li_0cc8c112-beac-416d-af11-f35744ca4e83", @@ -149,8 +149,8 @@ "discount_description": "Special 20 % discount", "transaction_fees_in_cents": 50, "transaction_fees_currency": "CZK", - "event_order": 5 - } + "event_order": 5, + }, ], "transactions": [ { @@ -159,140 +159,198 @@ "type": "payment", "date": "2015-11-05T00:04:03.000Z", "result": "successful", - "amount_in_cents": 7500 + "amount_in_cents": 7500, } - ] + ], } ] } -newInvoiceListExample = """ -{ - "invoices": [ - { - "uuid": "inv_565c73b2-85b9-49c9-a25e-2b7df6a677c9", - "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", - "external_id": "INV0001", - "date": "2015-11-01T00:00:00.000Z", - "due_date": "2015-11-15T00:00:00.000Z", - "currency": "USD", - "line_items": [ - { - "uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56", - "external_id": null, - "type": "subscription", - "subscription_uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", - "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", - "prorated": false, - "service_period_start": "2015-11-01T00:00:00.000Z", - "service_period_end": "2015-12-01T00:00:00.000Z", - "amount_in_cents": 5000, - "quantity": 1, - "discount_code": "PSO86", - "discount_amount_in_cents": 1000, - "tax_amount_in_cents": 900, - "account_code": null - }, +oldInvoiceListExample = { + "invoices": [ { - "uuid": "li_0cc8c112-beac-416d-af11-f35744ca4e83", - "external_id": null, - "type": "one_time", - "description": "Setup Fees", - "amount_in_cents": 2500, - "quantity": 1, - "discount_code": "PSO86", - "discount_amount_in_cents": 500, - "tax_amount_in_cents": 450, - "account_code": null, - "discount_description": "Special 20 % discount", - "transaction_fees_in_cents": 50, - "transaction_fees_currency": "CZK", - "event_order": 5 + "uuid": "inv_565c73b2-85b9-49c9-a25e-2b7df6a677c9", + "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", + "external_id": "INV0001", + "date": "2015-11-01T00:00:00.000Z", + "due_date": "2015-11-15T00:00:00.000Z", + "currency": "USD", + "line_items": [ + { + "uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56", + "external_id": None, + "type": "subscription", + "subscription_uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + "prorated": False, + "service_period_start": "2015-11-01T00:00:00.000Z", + "service_period_end": "2015-12-01T00:00:00.000Z", + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, + "account_code": None, + }, + { + "uuid": "li_0cc8c112-beac-416d-af11-f35744ca4e83", + "external_id": None, + "type": "one_time", + "description": "Setup Fees", + "amount_in_cents": 2500, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 500, + "tax_amount_in_cents": 450, + "account_code": None, + "discount_description": "Special 20 % discount", + "transaction_fees_in_cents": 50, + "transaction_fees_currency": "CZK", + "event_order": 5, + }, + ], + "transactions": [ + { + "uuid": "tr_879d560a-1bec-41bb-986e-665e38a2f7bc", + "external_id": None, + "type": "payment", + "date": "2015-11-05T00:14:23.000Z", + "result": "successful", + } + ], } - ], - "transactions": [ + ], + "current_page": 1, + "total_pages": 1, +} + + +newInvoiceListExample = { + "invoices": [ { - "uuid": "tr_879d560a-1bec-41bb-986e-665e38a2f7bc", - "external_id": null, - "type": "payment", - "date": "2015-11-05T00:14:23.000Z", - "result": "successful" + "uuid": "inv_565c73b2-85b9-49c9-a25e-2b7df6a677c9", + "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", + "external_id": "INV0001", + "date": "2015-11-01T00:00:00.000Z", + "due_date": "2015-11-15T00:00:00.000Z", + "currency": "USD", + "line_items": [ + { + "uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56", + "external_id": None, + "type": "subscription", + "subscription_uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + "prorated": False, + "service_period_start": "2015-11-01T00:00:00.000Z", + "service_period_end": "2015-12-01T00:00:00.000Z", + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, + "account_code": None, + }, + { + "uuid": "li_0cc8c112-beac-416d-af11-f35744ca4e83", + "external_id": None, + "type": "one_time", + "description": "Setup Fees", + "amount_in_cents": 2500, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 500, + "tax_amount_in_cents": 450, + "account_code": None, + "discount_description": "Special 20 % discount", + "transaction_fees_in_cents": 50, + "transaction_fees_currency": "CZK", + "event_order": 5, + }, + ], + "transactions": [ + { + "uuid": "tr_879d560a-1bec-41bb-986e-665e38a2f7bc", + "external_id": None, + "type": "payment", + "date": "2015-11-05T00:14:23.000Z", + "result": "successful", + } + ], } - ] - } - ], - "current_page": 1, - "total_pages": 1 + ], + "cursor": "cursor==", + "has_more": False, } -""" - -retrieveInvoiceExample = """ -{ - "uuid": "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059", - "external_id": "INV0001", - "date": "2015-11-01T00:00:00.000Z", - "due_date": "2015-11-15T00:00:00.000Z", - "currency": "USD", - "line_items": [ - { - "uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56", - "external_id": null, - "type": "subscription", - "subscription_uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", - "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", - "prorated": false, - "service_period_start": "2015-11-01T00:00:00.000Z", - "service_period_end": "2015-12-01T00:00:00.000Z", - "amount_in_cents": 5000, - "quantity": 1, - "discount_code": "PSO86", - "discount_amount_in_cents": 1000, - "tax_amount_in_cents": 900, - "account_code": null - }, - { - "uuid": "li_0cc8c112-beac-416d-af11-f35744ca4e83", - "external_id": null, - "type": "one_time", - "description": "Setup Fees", - "amount_in_cents": 2500, - "quantity": 1, - "discount_code": "PSO86", - "discount_amount_in_cents": 500, - "tax_amount_in_cents": 450, - "account_code": null, - "discount_description": "Special 20 % discount", - "transaction_fees_in_cents": 50, - "transaction_fees_currency": "CZK", - "event_order": 5 - } - ], - "transactions": [ - { - "uuid": "tr_879d560a-1bec-41bb-986e-665e38a2f7bc", - "external_id": null, - "type": "payment", - "date": "2015-11-05T00:14:23.000Z", - "result": "successful" - } - ] + + +retrieveInvoiceExample = { + "uuid": "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059", + "external_id": "INV0001", + "date": "2015-11-01T00:00:00.000Z", + "due_date": "2015-11-15T00:00:00.000Z", + "currency": "USD", + "line_items": [ + { + "uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56", + "external_id": None, + "type": "subscription", + "subscription_uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + "prorated": False, + "service_period_start": "2015-11-01T00:00:00.000Z", + "service_period_end": "2015-12-01T00:00:00.000Z", + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, + "account_code": None, + }, + { + "uuid": "li_0cc8c112-beac-416d-af11-f35744ca4e83", + "external_id": None, + "type": "one_time", + "description": "Setup Fees", + "amount_in_cents": 2500, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 500, + "tax_amount_in_cents": 450, + "account_code": None, + "discount_description": "Special 20 % discount", + "transaction_fees_in_cents": 50, + "transaction_fees_currency": "CZK", + "event_order": 5, + }, + ], + "transactions": [ + { + "uuid": "tr_879d560a-1bec-41bb-986e-665e38a2f7bc", + "external_id": None, + "type": "payment", + "date": "2015-11-05T00:14:23.000Z", + "result": "successful", + } + ], } -""" + class InvoiceTestCase(unittest.TestCase): """ Tests most important Import API part and its nested schemas. """ + maxDiff = None @requests_mock.mock() def test_create(self, mock_requests): mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/import/customers/UUID/invoices", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=responseData + json=responseData, ) config = Config("token") # is actually checked in mock @@ -307,14 +365,16 @@ def test_create(self, mock_requests): @requests_mock.mock() def test_list_has_customer_uuid(self, mock_requests): - responseData['customer_uuid'] = 'UUID' + responseData["customer_uuid"] = "UUID" + responseData["total_pages"] = 1 + responseData["current_page"] = 1 mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/import/customers/UUID/invoices", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=responseData + json=responseData, ) config = Config("token") # is actually checked in mock @@ -327,54 +387,91 @@ def test_list_has_customer_uuid(self, mock_requests): self.assertEqual(len(result.invoices), 1) self.assertTrue(isinstance(result.invoices[0], Invoice)) self.assertEqual(result.invoices[0].uuid, "inv_565c73b2-85b9-49c9-a25e-2b7df6a677c9") - self.assertEqual(result.customer_uuid, 'UUID') + self.assertEqual(result.customer_uuid, "UUID") + self.assertEqual(result.total_pages, 1) + self.assertEqual(result.current_page, 1) @requests_mock.mock() - def test_new_list(self, mock_requests): - + def test_new_list_old_pagination(self, mock_requests): mock_requests.register_uri( - 'GET', - ("https://api.chartmogul.com/v1/invoices" - "?external_id=INV0001&customer_uuid=cus_f466e33d-ff2b-4a11-8f85-417eb02157a7"), - request_headers={'Authorization': 'Basic dG9rZW46'}, - headers={'Content-Type': 'application/json'}, + "GET", + ( + "https://api.chartmogul.com/v1/invoices" + "?external_id=INV0001&customer_uuid=cus_f466e33d-ff2b-4a11-8f85-417eb02157a7" + ), + request_headers={"Authorization": "Basic dG9rZW46"}, + headers={"Content-Type": "application/json"}, status_code=200, - text=newInvoiceListExample + json=oldInvoiceListExample, ) config = Config("token") # is actually checked in mock - result = Invoice.all(config, - customer_uuid='cus_f466e33d-ff2b-4a11-8f85-417eb02157a7', - external_id='INV0001').get() + result = Invoice.all( + config, customer_uuid="cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", external_id="INV0001" + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") cu = [] - cu.append('cus_f466e33d-ff2b-4a11-8f85-417eb02157a7') + cu.append("cus_f466e33d-ff2b-4a11-8f85-417eb02157a7") ei = [] - ei.append('inv0001') - self.assertEqual(mock_requests.last_request.qs, {'customer_uuid': cu,'external_id': ei}) + ei.append("inv0001") + self.assertEqual(mock_requests.last_request.qs, {"customer_uuid": cu, "external_id": ei}) # Struct too complex to do 1:1 comparison self.assertTrue(isinstance(result, Invoice._many)) self.assertEqual(len(result.invoices), 1) - self.assertEqual(result.invoices[0].customer_uuid, 'cus_f466e33d-ff2b-4a11-8f85-417eb02157a7') + self.assertEqual( + result.invoices[0].customer_uuid, "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7" + ) self.assertEqual(result.current_page, 1) self.assertEqual(result.total_pages, 1) @requests_mock.mock() - def test_delete(self, mock_requests): + def test_new_list_new_pagination(self, mock_requests): + mock_requests.register_uri( + "GET", + ( + "https://api.chartmogul.com/v1/invoices" + "?external_id=INV0001&customer_uuid=cus_f466e33d-ff2b-4a11-8f85-417eb02157a7" + ), + request_headers={"Authorization": "Basic dG9rZW46"}, + headers={"Content-Type": "application/json"}, + status_code=200, + json=newInvoiceListExample, + ) + + config = Config("token") # is actually checked in mock + result = Invoice.all( + config, customer_uuid="cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", external_id="INV0001" + ).get() + self.assertEqual(mock_requests.call_count, 1, "expected call") + cu = [] + cu.append("cus_f466e33d-ff2b-4a11-8f85-417eb02157a7") + ei = [] + ei.append("inv0001") + self.assertEqual(mock_requests.last_request.qs, {"customer_uuid": cu, "external_id": ei}) + # Struct too complex to do 1:1 comparison + self.assertTrue(isinstance(result, Invoice._many)) + self.assertEqual(len(result.invoices), 1) + + self.assertEqual( + result.invoices[0].customer_uuid, "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7" + ) + self.assertFalse(result.has_more) + self.assertEqual(result.cursor, "cursor==") + + @requests_mock.mock() + def test_delete(self, mock_requests): mock_requests.register_uri( - 'DELETE', - ("https://api.chartmogul.com/v1/invoices" - "/inv_f466e33d-ff2b-4a11-8f85-417eb02157a7"), - request_headers={'Authorization': 'Basic dG9rZW46'}, - status_code=204 + "DELETE", + ("https://api.chartmogul.com/v1/invoices" "/inv_f466e33d-ff2b-4a11-8f85-417eb02157a7"), + request_headers={"Authorization": "Basic dG9rZW46"}, + status_code=204, ) config = Config("token") # is actually checked in mock - result = Invoice.destroy(config, - uuid='inv_f466e33d-ff2b-4a11-8f85-417eb02157a7').get() + result = Invoice.destroy(config, uuid="inv_f466e33d-ff2b-4a11-8f85-417eb02157a7").get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) @@ -382,39 +479,41 @@ def test_delete(self, mock_requests): @requests_mock.mock() def test_delete_not_found(self, mock_requests): - mock_requests.register_uri( - 'DELETE', - ("https://api.chartmogul.com/v1/invoices" - "/inv_f466e33d-ff2b-4a11-8f85-417eb02157a7"), - request_headers={'Authorization': 'Basic dG9rZW46'}, - headers={'Content-Type': 'application/json'}, + "DELETE", + ("https://api.chartmogul.com/v1/invoices" "/inv_f466e33d-ff2b-4a11-8f85-417eb02157a7"), + request_headers={"Authorization": "Basic dG9rZW46"}, + headers={"Content-Type": "application/json"}, status_code=404, - json={'error': 'Invoice not found'} + json={"error": "Invoice not found"}, ) config = Config("token") # is actually checked in mock with self.assertRaises(APIError) as context: - result = Invoice.destroy(config, uuid='inv_f466e33d-ff2b-4a11-8f85-417eb02157a7').get() + result = Invoice.destroy(config, uuid="inv_f466e33d-ff2b-4a11-8f85-417eb02157a7").get() self.assertEqual(mock_requests.call_count, 1, "expected call") @requests_mock.mock() def test_delete_all(self, mock_requests): mock_requests.register_uri( - 'DELETE', - ("https://api.chartmogul.com/v1/data_sources" - "/ds_f466e33d-ff2b-4a11-8f85-417eb02157a7/customers" - "/cus_f466e33d-ff2b-4a11-8f85-417eb02157a7/invoices"), - request_headers={'Authorization': 'Basic dG9rZW46'}, - headers={'Content-Type': 'application/json'}, - status_code=204 + "DELETE", + ( + "https://api.chartmogul.com/v1/data_sources" + "/ds_f466e33d-ff2b-4a11-8f85-417eb02157a7/customers" + "/cus_f466e33d-ff2b-4a11-8f85-417eb02157a7/invoices" + ), + request_headers={"Authorization": "Basic dG9rZW46"}, + headers={"Content-Type": "application/json"}, + status_code=204, ) config = Config("token") # is actually checked in mock - result = Invoice.destroy_all(config, - data_source_uuid='ds_f466e33d-ff2b-4a11-8f85-417eb02157a7', - customer_uuid='cus_f466e33d-ff2b-4a11-8f85-417eb02157a7').get() + result = Invoice.destroy_all( + config, + data_source_uuid="ds_f466e33d-ff2b-4a11-8f85-417eb02157a7", + customer_uuid="cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) @@ -422,22 +521,21 @@ def test_delete_all(self, mock_requests): @requests_mock.mock() def test_retrieve_invoice(self, mock_requests): - mock_requests.register_uri( - 'GET', + "GET", ("https://api.chartmogul.com/v1/invoices/inv_22910fc6-c931-48e7-ac12-90d2cb5f0059"), - request_headers={'Authorization': 'Basic dG9rZW46'}, - headers={'Content-Type': 'application/json'}, + request_headers={"Authorization": "Basic dG9rZW46"}, + headers={"Content-Type": "application/json"}, status_code=200, - text=retrieveInvoiceExample + json=retrieveInvoiceExample, ) config = Config("token") # is actually checked in mock - result = Invoice.retrieve(config, uuid='inv_22910fc6-c931-48e7-ac12-90d2cb5f0059').get() + result = Invoice.retrieve(config, uuid="inv_22910fc6-c931-48e7-ac12-90d2cb5f0059").get() self.assertEqual(mock_requests.call_count, 1, "expected call") # Struct too complex to do 1:1 comparison self.assertTrue(isinstance(result, Invoice)) - self.assertEqual(result.uuid, 'inv_22910fc6-c931-48e7-ac12-90d2cb5f0059') + self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059") diff --git a/test/api/test_metrics.py b/test/api/test_metrics.py index f5c9420..f0c849d 100644 --- a/test/api/test_metrics.py +++ b/test/api/test_metrics.py @@ -4,6 +4,7 @@ from datetime import date from collections import namedtuple from chartmogul.api.metrics import Summary + allMetricsJSON = { "entries": [ { @@ -23,7 +24,7 @@ "asp": 46107, "asp-percentage-change": 0.0, "arpa": 656170, - "arpa-percentage-change": 0.0 + "arpa-percentage-change": 0.0, }, { "date": "2022-05-31", @@ -42,8 +43,8 @@ "asp": 22035, "asp-percentage-change": -52.21, "arpa": 1113423, - "arpa-percentage-change": 69.69 - } + "arpa-percentage-change": 69.69, + }, ], "summary": { "current-mrr": 670567114, @@ -69,12 +70,13 @@ "asp-percentage-change": 65.59, "current-arpa": 1102906, "previous-arpa": 1112977, - "arpa-percentage-change": -0.9 - } + "arpa-percentage-change": -0.9, + }, } parsedEntries = [ - Metrics(**{ + Metrics( + **{ "date": date(2022, 4, 30), "mrr": 383859969.0, "mrr_percentage_change": 0.0, @@ -91,9 +93,11 @@ "asp": 46107.0, "asp_percentage_change": 0.0, "arpa": 656170.0, - "arpa_percentage_change": 0.0 - }), - Metrics(**{ + "arpa_percentage_change": 0.0, + } + ), + Metrics( + **{ "date": date(2022, 5, 31), "mrr": 67028090.0, "mrr_percentage_change": 74.62, @@ -110,8 +114,9 @@ "asp": 22035.0, "asp_percentage_change": -52.21, "arpa": 1113423.0, - "arpa_percentage_change": 69.69 - }) + "arpa_percentage_change": 69.69, + } + ), ] mrrResponse = { @@ -135,79 +140,51 @@ "mrr-churn": 0, "mrr-reactivation": 0, "percentage-change": 74.62, - } + }, ], - "summary": { - "current": 43145000, - "previous": 43145000, - "percentage-change": 0.0 - } + "summary": {"current": 43145000, "previous": 43145000, "percentage-change": 0.0}, } parsedMrrEntries = [ - Metrics(**{ - "date": date(2015, 1, 3), - "mrr": 30000.0, - "mrr_new_business": 10000.0, - "mrr_expansion": 15000.0, - "mrr_contraction": 0.0, - "mrr_churn": 0.0, - "mrr_reactivation": 0.0, - "percentage_change": 0.0, - }), - Metrics(**{ - "date": date(2015, 1, 10), - "mrr": 30000.0, - "mrr_new_business": 0.0, - "mrr_expansion": 0.0, - "mrr_contraction": 0.0, - "mrr_churn": 0.0, - "mrr_reactivation": 0.0, - "percentage_change": 74.62, - }) + Metrics( + **{ + "date": date(2015, 1, 3), + "mrr": 30000.0, + "mrr_new_business": 10000.0, + "mrr_expansion": 15000.0, + "mrr_contraction": 0.0, + "mrr_churn": 0.0, + "mrr_reactivation": 0.0, + "percentage_change": 0.0, + } + ), + Metrics( + **{ + "date": date(2015, 1, 10), + "mrr": 30000.0, + "mrr_new_business": 0.0, + "mrr_expansion": 0.0, + "mrr_contraction": 0.0, + "mrr_churn": 0.0, + "mrr_reactivation": 0.0, + "percentage_change": 74.62, + } + ), ] ltvResponse = { "entries": [ - { - "date": "2015-01-31", - "ltv": 0, - "percentage-change": 0.0 - }, - { - "date": "2015-02-28", - "ltv": 0, - "percentage-change": -44.43 - }, - { - "date": "2015-03-31", - "ltv": 1862989.7959183701, - "percentage-change": -44.43 - } + {"date": "2015-01-31", "ltv": 0, "percentage-change": 0.0}, + {"date": "2015-02-28", "ltv": 0, "percentage-change": -44.43}, + {"date": "2015-03-31", "ltv": 1862989.7959183701, "percentage-change": -44.43}, ], - "summary": { - "current": 980568, - "previous": 980568, - "percentage-change": -44.43 - } + "summary": {"current": 980568, "previous": 980568, "percentage-change": -44.43}, } parsedLtvEntries = [ - Metrics(**{ - "date": date(2015, 1, 31), - "ltv": 0.0, - "percentage_change": 0.0 - }), - Metrics(**{ - "date": date(2015, 2, 28), - "ltv": 0.0, - "percentage_change": -44.43 - }), - Metrics(**{ - "date": date(2015, 3, 31), - "ltv": 1862989.7959183701, - "percentage_change": -44.43 - }) + Metrics(**{"date": date(2015, 1, 31), "ltv": 0.0, "percentage_change": 0.0}), + Metrics(**{"date": date(2015, 2, 28), "ltv": 0.0, "percentage_change": -44.43}), + Metrics(**{"date": date(2015, 3, 31), "ltv": 1862989.7959183701, "percentage_change": -44.43}), ] @@ -215,80 +192,87 @@ class MetricsTestCase(unittest.TestCase): """ Tests all & singular metrics - optional fields, optional namedtuple summary. """ + maxDiff = None @requests_mock.mock() def test_all(self, mock_requests): mock_requests.register_uri( - 'GET', - ("https://api.chartmogul.com/v1/metrics/all?start-date=2022-04-01" - "&end-date=2022-05-31&interval=month&geo=GB&plans=PRO+Plan"), + "GET", + ( + "https://api.chartmogul.com/v1/metrics/all?start-date=2022-04-01" + "&end-date=2022-05-31&interval=month&geo=GB&plans=PRO+Plan" + ), status_code=200, - json=allMetricsJSON + json=allMetricsJSON, ) config = Config("token") - result = Metrics.all(config, - start_date='2022-04-01', - end_date='2022-05-31', - interval='month', - geo='GB', - plans='PRO Plan').get() + result = Metrics.all( + config, + start_date="2022-04-01", + end_date="2022-05-31", + interval="month", + geo="GB", + plans="PRO Plan", + ).get() expected = Metrics._many(parsedEntries, summary=allMetricsJSON["summary"]) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - 'end-date': ['2022-05-31'], - 'geo': ['gb'], - 'interval': ['month'], - 'plans': ['pro plan'], - 'start-date': ['2022-04-01']}) + self.assertEqual( + mock_requests.last_request.qs, + { + "end-date": ["2022-05-31"], + "geo": ["gb"], + "interval": ["month"], + "plans": ["pro plan"], + "start-date": ["2022-04-01"], + }, + ) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(str(result), str(expected)) @requests_mock.mock() def test_mrr_summary(self, mock_requests): mock_requests.register_uri( - 'GET', - ("https://api.chartmogul.com/v1/metrics/mrr?start-date=2015-01-01" - "&end-date=2015-11-01&interval=week"), + "GET", + ( + "https://api.chartmogul.com/v1/metrics/mrr?start-date=2015-01-01" + "&end-date=2015-11-01&interval=week" + ), status_code=200, - json=mrrResponse + json=mrrResponse, ) config = Config("token") - result = Metrics.mrr(config, - start_date='2015-01-01', - end_date='2015-11-01', - interval='week').get() - expected = Metrics._many( - parsedMrrEntries, summary=mrrResponse["summary"]) + result = Metrics.mrr( + config, start_date="2015-01-01", end_date="2015-11-01", interval="week" + ).get() + expected = Metrics._many(parsedMrrEntries, summary=mrrResponse["summary"]) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - 'end-date': ['2015-11-01'], - 'interval': ['week'], - 'start-date': ['2015-01-01']}) + self.assertEqual( + mock_requests.last_request.qs, + {"end-date": ["2015-11-01"], "interval": ["week"], "start-date": ["2015-01-01"]}, + ) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(str(result), str(expected)) @requests_mock.mock() def test_ltv(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/metrics/ltv?start-date=2015-01-01&end-date=2015-11-01", status_code=200, - json=ltvResponse + json=ltvResponse, ) config = Config("token") - result = Metrics.ltv(config, - start_date='2015-01-01', - end_date='2015-11-01').get() - expected = Metrics._many( - parsedLtvEntries, summary=ltvResponse["summary"]) + result = Metrics.ltv(config, start_date="2015-01-01", end_date="2015-11-01").get() + expected = Metrics._many(parsedLtvEntries, summary=ltvResponse["summary"]) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - 'end-date': ['2015-11-01'], - 'start-date': ['2015-01-01']}) + self.assertEqual( + mock_requests.last_request.qs, + {"end-date": ["2015-11-01"], "start-date": ["2015-01-01"]}, + ) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(str(result), str(expected)) diff --git a/test/api/test_ping.py b/test/api/test_ping.py index 1522878..b8d00e2 100644 --- a/test/api/test_ping.py +++ b/test/api/test_ping.py @@ -13,16 +13,16 @@ class PingTestCase(unittest.TestCase): @requests_mock.mock() def test_ping(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/ping", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json={"data": "pong!"} + json={"data": "pong!"}, ) config = Config("token") # is actually checked in mock pong = Ping.ping(config).get() - expected = Ping(**{"data": u"pong!"}) + expected = Ping(**{"data": "pong!"}) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.text, None) diff --git a/test/api/test_plan.py b/test/api/test_plan.py index 84435a1..c0f4776 100644 --- a/test/api/test_plan.py +++ b/test/api/test_plan.py @@ -9,98 +9,129 @@ class PlanTestCase(unittest.TestCase): """ Tests cursor query parameters & modify (patch a plan). """ + maxDiff = None @requests_mock.mock() - def test_cursor_list_plans(self, mock_requests): - expected_plan_dict = {"uuid": u"whatever_uuid", - "data_source_uuid": u"some_uuid", - "name": u"some plan", - "interval_count": 2, - "interval_unit": u"moonshines", - "external_id": u"custom_filter"} + def test_cursor_list_plans_old_pagination(self, mock_requests): + expected_plan_dict = { + "uuid": "whatever_uuid", + "data_source_uuid": "some_uuid", + "name": "some plan", + "interval_count": 2, + "interval_unit": "moonshines", + "external_id": "custom_filter", + } mock_requests.register_uri( - 'GET', - ("https://api.chartmogul.com/v1/plans?page=5" - "&per_page=12&data_source_uuid=some_uuid&external_id=custom_filter"), - request_headers={'Authorization': 'Basic dG9rZW46'}, + "GET", + ( + "https://api.chartmogul.com/v1/plans?page=5" + "&per_page=12&data_source_uuid=some_uuid&external_id=custom_filter" + ), + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json={"plans": [expected_plan_dict], - "current_page": 5, - "total_pages": 18} + json={"plans": [expected_plan_dict], "current_page": 5, "total_pages": 18}, ) config = Config("token") # is actually checked in mock - plan = Plan.all(config, page=5, per_page=12, - data_source_uuid="some_uuid", - external_id="custom_filter").get() - expected = Plan._many([Plan(**expected_plan_dict)], - current_page=5, total_pages=18) + plan = Plan.all( + config, page=5, per_page=12, data_source_uuid="some_uuid", external_id="custom_filter" + ).get() + expected = Plan._many([Plan(**expected_plan_dict)], current_page=5, total_pages=18) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - 'data_source_uuid': ['some_uuid'], - 'external_id': ['custom_filter'], - 'page': ['5'], - 'per_page': ['12'] - }) + self.assertEqual( + mock_requests.last_request.qs, + { + "data_source_uuid": ["some_uuid"], + "external_id": ["custom_filter"], + "page": ["5"], + "per_page": ["12"], + }, + ) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(str(plan), str(expected)) + @requests_mock.mock() + def test_cursor_list_plans_new_pagination(self, mock_requests): + expected_plan_dict = { + "uuid": "whatever_uuid", + "data_source_uuid": "some_uuid", + "name": "some plan", + "interval_count": 2, + "interval_unit": "moonshines", + "external_id": "custom_filter", + } + mock_requests.register_uri( + "GET", + ( + "https://api.chartmogul.com/v1/plans?cursor=cursor==" + "&per_page=12&data_source_uuid=some_uuid&external_id=custom_filter" + ), + request_headers={"Authorization": "Basic dG9rZW46"}, + status_code=200, + json={"plans": [expected_plan_dict], "has_more": True, "cursor": "cursor=="}, + ) + config = Config("token") # is actually checked in mock + plan = Plan.all( + config, + cursor="cursor==", + per_page=12, + data_source_uuid="some_uuid", + external_id="custom_filter", + ).get() + expected = Plan._many([Plan(**expected_plan_dict)], has_more=True, cursor="cursor==") + self.assertEqual(str(plan), str(expected)) + @requests_mock.mock() def test_modify_plan(self, mock_requests): expected_plan_dict = { - "uuid": u"whatever_uuid", - "data_source_uuid": u"some_uuid", - "name": u"new_name", + "uuid": "whatever_uuid", + "data_source_uuid": "some_uuid", + "name": "new_name", "interval_count": 2, - "interval_unit": u"moonshines", - "external_id": u"custom_filter" + "interval_unit": "moonshines", + "external_id": "custom_filter", } mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/plans/whatever_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plan_dict + json=expected_plan_dict, ) config = Config("token") # is actually checked in mock - plan = Plan.modify(config, - uuid="whatever_uuid", - data={"name": "new_name"}).get() + plan = Plan.modify(config, uuid="whatever_uuid", data={"name": "new_name"}).get() expected = Plan(**expected_plan_dict) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), - {"name": u"new_name"}) + self.assertEqual(mock_requests.last_request.json(), {"name": "new_name"}) self.assertEqual(str(plan), str(expected)) @requests_mock.mock() def test_create_plan(self, mock_requests): expected_plan_dict = { - "uuid": u"whatever_uuid", - "data_source_uuid": u"some_uuid", - "name": u"new_name", + "uuid": "whatever_uuid", + "data_source_uuid": "some_uuid", + "name": "new_name", "interval_count": 2, - "interval_unit": u"moonshines", - "external_id": u"custom_filter" + "interval_unit": "moonshines", + "external_id": "custom_filter", } sent = { "data_source_uuid": "ds_9bb53a1e-edfd-11e6-bf83-af49e978cb11", "name": "Gold Plan", "interval_count": 1, "interval_unit": "month", - "external_id": "plan_0002" + "external_id": "plan_0002", } mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/plans", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plan_dict + json=expected_plan_dict, ) config = Config("token") # is actually checked in mock - plan = Plan.create( - config, - data=sent).get() + plan = Plan.create(config, data=sent).get() expected = Plan(**expected_plan_dict) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) diff --git a/test/api/test_plan_group.py b/test/api/test_plan_group.py index fe20515..fab5511 100644 --- a/test/api/test_plan_group.py +++ b/test/api/test_plan_group.py @@ -13,25 +13,26 @@ class PlanGroupTestCase(unittest.TestCase): @requests_mock.mock() def test_create_plan_group(self, mock_requests): expected_plan_group_dict = { - "uuid": u"whatever_uuid", - "name": u"Gold Plan Group", - "plans_count": 2 + "uuid": "whatever_uuid", + "name": "Gold Plan Group", + "plans_count": 2, } sent = { "name": "Gold Plan Group", - "plans": ["pl_fe0824c8-4738-11ea-a26c-3b021eb4c733","pl_fe160a70-4738-11ea-a26c-e38e12dbd8be"] + "plans": [ + "pl_fe0824c8-4738-11ea-a26c-3b021eb4c733", + "pl_fe160a70-4738-11ea-a26c-e38e12dbd8be", + ], } mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/plan_groups", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plan_group_dict + json=expected_plan_group_dict, ) config = Config("token") # is actually checked in mock - plan_group = PlanGroup.create( - config, - data=sent).get() + plan_group = PlanGroup.create(config, data=sent).get() expected = PlanGroup(**expected_plan_group_dict) self.assertEqual(mock_requests.call_count, 1, "expected call") @@ -42,16 +43,16 @@ def test_create_plan_group(self, mock_requests): @requests_mock.mock() def test_retrieve_plan_group(self, mock_requests): expected_plan_group_dict = { - "uuid": u"whatever_uuid", - "name": u"Gold Plan Group", - "plans_count": 2 + "uuid": "whatever_uuid", + "name": "Gold Plan Group", + "plans_count": 2, } mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/plan_groups/whatever_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plan_group_dict + json=expected_plan_group_dict, ) config = Config("token") # is actually checked in mock @@ -61,11 +62,11 @@ def test_retrieve_plan_group(self, mock_requests): self.assertEqual(mock_requests.last_request.qs, {}) self.assertTrue(isinstance(result, PlanGroup)) self.assertEqual(result.uuid, "whatever_uuid") - self.assertEqual(result.name, 'Gold Plan Group') + self.assertEqual(result.name, "Gold Plan Group") self.assertEqual(result.plans_count, 2) @requests_mock.mock() - def test_retrieve_plan_group_plans(self, mock_requests): + def test_retrieve_plan_group_plans_old_pagination(self, mock_requests): expected_plans = { "plans": [ { @@ -74,7 +75,7 @@ def test_retrieve_plan_group_plans(self, mock_requests): "data_source_uuid": "ds_73c24b7e-37be-11ea-85a4-03a4322daccc", "interval_count": 6, "interval_unit": "month", - "external_id": "plan_EOwj9vInDKILy1" + "external_id": "plan_EOwj9vInDKILy1", }, { "name": "Berghain Flatrate Pack - Skip the Queue Pack", @@ -82,18 +83,18 @@ def test_retrieve_plan_group_plans(self, mock_requests): "data_source_uuid": "ds_73c24b7e-37be-11ea-85a4-03a4322daccc", "interval_count": 1, "interval_unit": "month", - "external_id": "plan_EOsEG3pyySMBEP" - } + "external_id": "plan_EOsEG3pyySMBEP", + }, ], "current_page": 1, - "total_pages": 1 + "total_pages": 1, } mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/plan_groups/whatever_uuid/plans", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plans + json=expected_plans, ) config = Config("token") # is actually checked in mock @@ -104,30 +105,67 @@ def test_retrieve_plan_group_plans(self, mock_requests): self.assertEqual(result.plans[0].uuid, "pl_cef31082-37be-11ea-a7bc-cb55fa0afcbb") self.assertEqual(result.plans[1].uuid, "pl_cef46630-37be-11ea-a7bc-a316cc9407e9") self.assertEqual(len(result.plans), 2) + self.assertEqual(result.total_pages, 1) + self.assertEqual(result.current_page, 1) @requests_mock.mock() - def test_all_plan_groups(self, mock_requests): - expected_plan_groups = { - "plan_groups": [ + def test_retrieve_plan_group_plans_new_pagination(self, mock_requests): + expected_plans = { + "plans": [ { - "uuid": u"whatever_uuid", - "name": u"good_plan", - "plans_count": 2 + "name": "Berghain Flatrate Pack - bi-annual", + "uuid": "pl_cef31082-37be-11ea-a7bc-cb55fa0afcbb", + "data_source_uuid": "ds_73c24b7e-37be-11ea-85a4-03a4322daccc", + "interval_count": 6, + "interval_unit": "month", + "external_id": "plan_EOwj9vInDKILy1", }, { - "uuid": u"my_uuid", - "name": u"best_plan", - "plans_count": 5 + "name": "Berghain Flatrate Pack - Skip the Queue Pack", + "uuid": "pl_cef46630-37be-11ea-a7bc-a316cc9407e9", + "data_source_uuid": "ds_73c24b7e-37be-11ea-85a4-03a4322daccc", + "interval_count": 1, + "interval_unit": "month", + "external_id": "plan_EOsEG3pyySMBEP", }, ], + "has_more": False, + "cursor": "cursor==", + } + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/plan_groups/whatever_uuid/plans", + request_headers={"Authorization": "Basic dG9rZW46"}, + status_code=200, + json=expected_plans, + ) + + config = Config("token") # is actually checked in mock + result = PlanGroup.all(config, uuid="whatever_uuid").get() + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(result.plans[0].uuid, "pl_cef31082-37be-11ea-a7bc-cb55fa0afcbb") + self.assertEqual(result.plans[1].uuid, "pl_cef46630-37be-11ea-a7bc-a316cc9407e9") + self.assertEqual(len(result.plans), 2) + self.assertEqual(result.cursor, "cursor==") + self.assertFalse(result.has_more) + + @requests_mock.mock() + def test_all_plan_groups_old_pagination(self, mock_requests): + expected_plan_groups = { + "plan_groups": [ + {"uuid": "whatever_uuid", "name": "good_plan", "plans_count": 2}, + {"uuid": "my_uuid", "name": "best_plan", "plans_count": 5}, + ], "current_page": 1, - "total_pages": 1 + "total_pages": 1, } mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/plan_groups", status_code=200, - json=expected_plan_groups + json=expected_plan_groups, ) config = Config("token") @@ -139,13 +177,42 @@ def test_all_plan_groups(self, mock_requests): self.assertTrue(isinstance(result.plan_groups[0], PlanGroup)) self.assertEqual(result.plan_groups[0].uuid, "whatever_uuid") self.assertEqual(result.plan_groups[1].uuid, "my_uuid") + self.assertEqual(result.total_pages, 1) + self.assertEqual(result.current_page, 1) + + @requests_mock.mock() + def test_all_plan_groups_new_pagination(self, mock_requests): + expected_plan_groups = { + "plan_groups": [ + {"uuid": "whatever_uuid", "name": "good_plan", "plans_count": 2}, + {"uuid": "my_uuid", "name": "best_plan", "plans_count": 5}, + ], + "has_more": False, + "cursor": "cursor==", + } + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/plan_groups", + status_code=200, + json=expected_plan_groups, + ) + + config = Config("token") + result = PlanGroup.all(config).get() + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.text, None) + self.assertTrue(isinstance(result.plan_groups[0], PlanGroup)) + self.assertEqual(result.plan_groups[0].uuid, "whatever_uuid") + self.assertEqual(result.plan_groups[1].uuid, "my_uuid") + self.assertFalse(result.has_more) + self.assertEqual(result.cursor, "cursor==") @requests_mock.mock() def test_destroy_plan_group(self, mock_requests): mock_requests.register_uri( - 'DELETE', - "https://api.chartmogul.com/v1/plan_groups/my_uuid", - status_code=204 + "DELETE", "https://api.chartmogul.com/v1/plan_groups/my_uuid", status_code=204 ) config = Config("token") @@ -158,48 +225,38 @@ def test_destroy_plan_group(self, mock_requests): @requests_mock.mock() def test_modify_plan_group_name(self, mock_requests): - expected_plan_group_dict = { - "uuid": u"whatever_uuid", - "name": u"new_name", - "plans_count": 2 - } + expected_plan_group_dict = {"uuid": "whatever_uuid", "name": "new_name", "plans_count": 2} mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/plan_groups/whatever_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plan_group_dict + json=expected_plan_group_dict, ) config = Config("token") # is actually checked in mock - result = PlanGroup.modify(config, - uuid="whatever_uuid", - data={"name": "new_name"}).get() + result = PlanGroup.modify(config, uuid="whatever_uuid", data={"name": "new_name"}).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), {"name": u"new_name"}) + self.assertEqual(mock_requests.last_request.json(), {"name": "new_name"}) self.assertEqual(result.uuid, "whatever_uuid") self.assertEqual(result.name, "new_name") self.assertEqual(result.plans_count, 2) @requests_mock.mock() def test_modify_plan_group_plans(self, mock_requests): - expected_plan_group_dict = { - "uuid": u"whatever_uuid", - "name": u"new_name", - "plans_count": 3 - } + expected_plan_group_dict = {"uuid": "whatever_uuid", "name": "new_name", "plans_count": 3} mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/plan_groups/whatever_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_plan_group_dict + json=expected_plan_group_dict, ) config = Config("token") # is actually checked in mock - result = PlanGroup.modify(config, - uuid="whatever_uuid", - data={"plans": "[pl_uuid_1, pl_uuid_2, pl_uuid_3]"}).get() + result = PlanGroup.modify( + config, uuid="whatever_uuid", data={"plans": "[pl_uuid_1, pl_uuid_2, pl_uuid_3]"} + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) diff --git a/test/api/test_retry_request.py b/test/api/test_retry_request.py index 25a534e..6297fbf 100644 --- a/test/api/test_retry_request.py +++ b/test/api/test_retry_request.py @@ -7,24 +7,24 @@ from requests.exceptions import RetryError from chartmogul.retry_request import requests_retry_session -class RetryRequestTestCase(unittest.TestCase): +class RetryRequestTestCase(unittest.TestCase): @httpretty.activate def test_retry_request(self): httpretty.register_uri( httpretty.GET, "https://example:444/testing", responses=[ - httpretty.Response(body='{}', status=500), - httpretty.Response(body='{}', status=200), - ] + httpretty.Response(body="{}", status=500), + httpretty.Response(body="{}", status=200), + ], ) with self.assertRaises(RetryError): - requests_retry_session(0).get('https://example:444/testing') + requests_retry_session(0).get("https://example:444/testing") - response = requests_retry_session(2, 0).get('https://example:444/testing') - self.assertEqual(response.text, '{}') + response = requests_retry_session(2, 0).get("https://example:444/testing") + self.assertEqual(response.text, "{}") @httpretty.activate def test_requests_retry_session_on_resource(self): @@ -32,18 +32,18 @@ def test_requests_retry_session_on_resource(self): httpretty.POST, "https://api.chartmogul.com/v1/data_sources", responses=[ - httpretty.Response(body='{}', status=500), - httpretty.Response(body='{}', status=500), - httpretty.Response(body='{}', status=500), - httpretty.Response(body='{}', status=500), - httpretty.Response(body='{}', status=200), - ] + httpretty.Response(body="{}", status=500), + httpretty.Response(body="{}", status=500), + httpretty.Response(body="{}", status=500), + httpretty.Response(body="{}", status=500), + httpretty.Response(body="{}", status=200), + ], ) # max_retries set as 4 # backoff_factor set as 0 to avoid waiting while testing config = Config("token", None, 4, 0) try: - DataSource.create(config, data={ "test_date": date(2015, 1, 1) }).get() + DataSource.create(config, data={"test_date": date(2015, 1, 1)}).get() except RetryError: self.fail("request raised retryError unexpectedly!") diff --git a/test/api/test_subscription.py b/test/api/test_subscription.py index d04524f..683ce46 100644 --- a/test/api/test_subscription.py +++ b/test/api/test_subscription.py @@ -13,12 +13,11 @@ class SubscriptionsTestCase(unittest.TestCase): @requests_mock.mock() def test_cancel_subscription(self, mock_requests): - """ Test cancel (patch) subscription (cancelled_at). - """ + """Test cancel (patch) subscription (cancelled_at).""" mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/import/subscriptions/some_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ "uuid": "some_uuid", @@ -26,31 +25,27 @@ def test_cancel_subscription(self, mock_requests): "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", "cancellation_dates": ["2016-01-15T00:00:00.000Z"], - "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430" - } + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + }, ) config = Config("token") # is actually checked in mock - result = Subscription.cancel(config, - uuid="some_uuid", - data={ - "cancelled_at": datetime(2016, 1, 15, 0, 0, 0) - }).get() + result = Subscription.cancel( + config, uuid="some_uuid", data={"cancelled_at": datetime(2016, 1, 15, 0, 0, 0)} + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), { - "cancelled_at": "2016-01-15T00:00:00"}) + self.assertEqual(mock_requests.last_request.json(), {"cancelled_at": "2016-01-15T00:00:00"}) self.assertTrue(isinstance(result, Subscription)) self.assertEqual(result.uuid, "some_uuid") @requests_mock.mock() def test_modify_subscription(self, mock_requests): - """ Test modify (patch) subscription (cancellation_dates). - """ + """Test modify (patch) subscription (cancellation_dates).""" mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/import/subscriptions/some_uuid", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ "uuid": "some_uuid", @@ -58,51 +53,55 @@ def test_modify_subscription(self, mock_requests): "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", "cancellation_dates": [], - "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430" - } + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + }, ) config = Config("token") # is actually checked in mock - result = Subscription.modify(config, uuid="some_uuid", data={ - "cancellation_dates": []}).get() + result = Subscription.modify( + config, uuid="some_uuid", data={"cancellation_dates": []} + ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), - {"cancellation_dates": []}) + self.assertEqual(mock_requests.last_request.json(), {"cancellation_dates": []}) self.assertEqual(result.__class__, Subscription) - self.assertEqual(result.__dict__, Subscription(**{ - "cancellation_dates": [], - "customer_uuid": u"cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", - "data_source_uuid": u"ds_fef05d54-47b4-431b-aed2-eb6b9e545430", - "uuid": u"some_uuid", - "external_id": u"sub_0001", - "plan_uuid": u"pl_eed05d54-75b4-431b-adb2-eb6b9e543206" - }).__dict__) + self.assertEqual( + result.__dict__, + Subscription( + **{ + "cancellation_dates": [], + "customer_uuid": "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + "uuid": "some_uuid", + "external_id": "sub_0001", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + } + ).__dict__, + ) @requests_mock.mock() def test_list_imported_subscriptions(self, mock_requests): - """ Test listing (get) subscriptions. - """ + """Test listing (get) subscriptions.""" mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/import/customers/some_uuid/subscriptions", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, json={ - "customer_uuid": "some_uuid", - "subscriptions":[ + "customer_uuid": "some_uuid", + "subscriptions": [ { - "uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", - "external_id": "sub_0001", - "subscription_set_external_id": "sub_set_0001", - "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", - "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", - "cancellation_dates":[] + "uuid": "sub_e6bc5407-e258-4de0-bb43-61faaf062035", + "external_id": "sub_0001", + "subscription_set_external_id": "sub_set_0001", + "plan_uuid": "pl_eed05d54-75b4-431b-adb2-eb6b9e543206", + "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430", + "cancellation_dates": [], } - ], - "current_page": 1, - "total_pages": 1 - } + ], + "has_more": False, + "cursor": "cursor==", + }, ) config = Config("token") # is actually checked in mock result = Subscription.list_imported(config, uuid="some_uuid").get() @@ -111,3 +110,5 @@ def test_list_imported_subscriptions(self, mock_requests): self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(result.__class__.__name__, Subscription._many.__name__) self.assertEqual(result.customer_uuid, "some_uuid") + self.assertEqual(result.cursor, "cursor==") + self.assertFalse(result.has_more) diff --git a/test/api/test_subscription_event.py b/test/api/test_subscription_event.py index 9fc80c1..356f32f 100644 --- a/test/api/test_subscription_event.py +++ b/test/api/test_subscription_event.py @@ -10,60 +10,80 @@ from chartmogul import ArgumentMissingError expected_sub_ev = { - "id": 7654321, - "external_id": "evnt_026", - "customer_external_id": "scus_022", - "data_source_uuid": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", - "event_type": "subscription_start_scheduled", - "event_date": "2022-03-30 23:00:00.000", - "effective_date": "2022-04-01 23:00:00.000", - "subscription_external_id": "sub_0001", - "plan_external_id": "gol d_monthly", - "currency": "USD", - "amount_in_cents": 1000 + "id": 7654321, + "external_id": "evnt_026", + "customer_external_id": "scus_022", + "data_source_uuid": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", + "event_type": "subscription_start_scheduled", + "event_date": "2022-03-30 23:00:00.000", + "effective_date": "2022-04-01 23:00:00.000", + "subscription_external_id": "sub_0001", + "plan_external_id": "gol d_monthly", + "currency": "USD", + "amount_in_cents": 1000, } -sub_ev_list_expected = { +sub_ev_list_expected_old = { "subscription_events": [ - { - "id": 7654321, - "external_id": "evnt_026", - "customer_external_id": "scus_022", - "data_source_uuid": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", - "event_type": "subscription_start_scheduled", - "event_date": "2022-03-30 23:00:00.000", - "effective_date": "2022-04-01 23:00:00.000", - "subscription_external_id": "sub_0001", - "plan_external_id": "gol d_monthly", - "currency": "USD", - "amount_in_cents": 1000 - } + { + "id": 7654321, + "external_id": "evnt_026", + "customer_external_id": "scus_022", + "data_source_uuid": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", + "event_type": "subscription_start_scheduled", + "event_date": "2022-03-30 23:00:00.000", + "effective_date": "2022-04-01 23:00:00.000", + "subscription_external_id": "sub_0001", + "plan_external_id": "gol d_monthly", + "currency": "USD", + "amount_in_cents": 1000, + } ], "meta": { "next_key": 67048503, "prev_key": None, "before_key": "2022-04-10T22:27:35.834Z", - "page": 1, - "total_pages": 166 - } + "page": 1, + "total_pages": 166, + }, +} + +sub_ev_list_expected_new = { + "subscription_events": [ + { + "id": 7654321, + "external_id": "evnt_026", + "customer_external_id": "scus_022", + "data_source_uuid": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", + "event_type": "subscription_start_scheduled", + "event_date": "2022-03-30 23:00:00.000", + "effective_date": "2022-04-01 23:00:00.000", + "subscription_external_id": "sub_0001", + "plan_external_id": "gol d_monthly", + "currency": "USD", + "amount_in_cents": 1000, + } + ], + "cursor": "cursor==", + "has_more": False, } sub_ev_one = SubscriptionEvent( - id=7654321, - external_id="evnt_026", - customer_external_id="scus_022", - data_source_uuid="ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", - event_type="subscription_start_scheduled", - event_date="2022-03-30 23:00:00.000", - effective_date="2022-04-01 23:00:00.000", - subscription_external_id="sub_0001", - plan_external_id="gol d_monthly", - currency="USD", - amount_in_cents=1000 + id=7654321, + external_id="evnt_026", + customer_external_id="scus_022", + data_source_uuid="ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", + event_type="subscription_start_scheduled", + event_date="2022-03-30 23:00:00.000", + effective_date="2022-04-01 23:00:00.000", + subscription_external_id="sub_0001", + plan_external_id="gol d_monthly", + currency="USD", + amount_in_cents=1000, ) -class SubscriptionEventTestCase(unittest.TestCase): +class SubscriptionEventTestCase(unittest.TestCase): @requests_mock.mock() def test_create_subscription_event(self, mock_requests): sent = { @@ -76,42 +96,38 @@ def test_create_subscription_event(self, mock_requests): "subscription_external_id": "sub_0001", "plan_external_id": "gold_monthly", "currency": "USD", - "amount_in_cents": 1000 + "amount_in_cents": 1000, } mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/subscription_events", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_sub_ev + json=expected_sub_ev, ) config = Config("token") # is actually checked in mock - sub_ev = SubscriptionEvent.create( - config, - uuid = "UUID", - data = sent - ).get() + sub_ev = SubscriptionEvent.create(config, uuid="UUID", data=sent).get() expected = SubscriptionEvent(**expected_sub_ev) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), sent) self.assertTrue(isinstance(sub_ev, SubscriptionEvent)) - self.assertEqual(sub_ev.id,expected.id) - self.assertEqual(sub_ev.external_id,expected.external_id) + self.assertEqual(sub_ev.id, expected.id) + self.assertEqual(sub_ev.external_id, expected.external_id) @requests_mock.mock() def test_delete_subscription_event_with_id(self, mock_requests): data = {"subscription_event": {"id": 7654321}} mock_requests.register_uri( - 'DELETE', + "DELETE", ("https://api.chartmogul.com/v1/subscription_events"), - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=204, - json = data + json=data, ) config = Config("token") # is actually checked in mock - result = SubscriptionEvent.destroy_with_params(config, data = data).get() + result = SubscriptionEvent.destroy_with_params(config, data=data).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) @@ -122,19 +138,19 @@ def test_delete_subscription_event_with_ds_uuid_and_external_id(self, mock_reque data = { "subscription_event": { "data_source_uuid": "evnt_026", - "external_id": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba" + "external_id": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", } } mock_requests.register_uri( - 'DELETE', + "DELETE", ("https://api.chartmogul.com/v1/subscription_events"), - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=204, - json = data + json=data, ) config = Config("token") # is actually checked in mock - result = SubscriptionEvent.destroy_with_params(config, data = data).get() + result = SubscriptionEvent.destroy_with_params(config, data=data).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) @@ -142,21 +158,16 @@ def test_delete_subscription_event_with_ds_uuid_and_external_id(self, mock_reque @requests_mock.mock() def test_modify_subscription_event_with_id(self, mock_requests): - data={ - "subscription_event": { - "id": 7654321, - "amount_in_cents": 10 - } - } + data = {"subscription_event": {"id": 7654321, "amount_in_cents": 10}} mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/subscription_events", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_sub_ev + json=expected_sub_ev, ) config = Config("token") # is actually checked in mock - sub_ev = SubscriptionEvent.modify_with_params(config, data = data).get() + sub_ev = SubscriptionEvent.modify_with_params(config, data=data).get() expected = SubscriptionEvent(**expected_sub_ev) self.assertEqual(mock_requests.call_count, 1, "expected call") @@ -167,19 +178,19 @@ def test_modify_subscription_event_with_id(self, mock_requests): @requests_mock.mock() def test_modify_subscription_event_with_ds_uuid_and_external_id(self, mock_requests): - data={ + data = { "subscription_event": { "external_id": "evnt_026", "data_source_uuid": "ds_1fm3eaac-62d0-31ec-clf4-4bf0mbe81aba", - "amount_in_cents": 10 + "amount_in_cents": 10, } } mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/subscription_events", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_sub_ev + json=expected_sub_ev, ) config = Config("token") # is actually checked in mock sub_ev = SubscriptionEvent.modify_with_params(config, data=data).get() @@ -187,24 +198,19 @@ def test_modify_subscription_event_with_ds_uuid_and_external_id(self, mock_reque expected = SubscriptionEvent(**expected_sub_ev) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(),data) + self.assertEqual(mock_requests.last_request.json(), data) self.assertTrue(isinstance(sub_ev, SubscriptionEvent)) self.assertEqual(sub_ev.id, 7654321) @requests_mock.mock() def test_modify_subscription_event_with_bad_params(self, mock_requests): - data={ - "subscription_event": { - "external_id": "evnt_026", - "amount_in_cents": 10 - } - } + data = {"subscription_event": {"external_id": "evnt_026", "amount_in_cents": 10}} mock_requests.register_uri( - 'PATCH', + "PATCH", "https://api.chartmogul.com/v1/subscription_events", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=400, - json=expected_sub_ev + json=expected_sub_ev, ) config = Config("token") # is actually checked in mock try: @@ -212,16 +218,16 @@ def test_modify_subscription_event_with_bad_params(self, mock_requests): except ArgumentMissingError: pass else: - self.fail('ArgumentMissingError not raised') + self.fail("ArgumentMissingError not raised") @requests_mock.mock() - def test_all_subscription_events(self, mock_requests): + def test_all_subscription_events_old_pagination(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/subscription_events", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=sub_ev_list_expected + json=sub_ev_list_expected_old, ) config = Config("token") @@ -234,53 +240,58 @@ def test_all_subscription_events(self, mock_requests): "prev_key": None, "before_key": "2022-04-10T22:27:35.834Z", "page": 1, - "total_pages": 166 - } + "total_pages": 166, + }, ) self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(sorted(dir(subscription_events)), sorted(dir(expected))) - self.assertEqual(sorted(subscription_events.subscription_events[0].external_id), sorted(expected.subscription_events[0].external_id)) + self.assertEqual( + sorted(subscription_events.subscription_events[0].external_id), + sorted(expected.subscription_events[0].external_id), + ) self.assertTrue(isinstance(subscription_events.subscription_events[0], SubscriptionEvent)) @requests_mock.mock() def test_all_subscription_events_with_filters(self, mock_requests): mock_requests.register_uri( - 'GET', + "GET", "https://api.chartmogul.com/v1/subscription_events?external_id=evnt_026" "&customer_external_id=scus_022&event_type=subscription_start_scheduled&plan_external_id=gol d_monthly", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=sub_ev_list_expected + json=sub_ev_list_expected_new, ) config = Config("token") - subscription_events = SubscriptionEvent.all(config, external_id="evnt_026", - customer_external_id="scus_022", - event_type="subscription_start_scheduled", - plan_external_id="gol d_monthly").get() + subscription_events = SubscriptionEvent.all( + config, + external_id="evnt_026", + customer_external_id="scus_022", + event_type="subscription_start_scheduled", + plan_external_id="gol d_monthly", + ).get() expected = SubscriptionEvent._many( - [SubscriptionEvent(**expected_sub_ev)], - meta={ - "next_key": 67048503, - "prev_key": None, - "before_key": "2022-04-10T22:27:35.834Z", - "page": 1, - "total_pages": 166 - } + [SubscriptionEvent(**expected_sub_ev)], has_more=True, cursor="cursor==" ) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, { - 'customer_external_id': ['scus_022'], - 'event_type': ['subscription_start_scheduled'], - 'external_id': ['evnt_026'], - 'plan_external_id': ['gol d_monthly'] - }) + self.assertEqual( + mock_requests.last_request.qs, + { + "customer_external_id": ["scus_022"], + "event_type": ["subscription_start_scheduled"], + "external_id": ["evnt_026"], + "plan_external_id": ["gol d_monthly"], + }, + ) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(sorted(dir(subscription_events)), sorted(dir(expected))) - self.assertEqual(sorted(subscription_events.subscription_events[0].external_id), sorted(expected.subscription_events[0].external_id)) + self.assertEqual( + sorted(subscription_events.subscription_events[0].external_id), + sorted(expected.subscription_events[0].external_id), + ) self.assertTrue(isinstance(subscription_events.subscription_events[0], SubscriptionEvent)) diff --git a/test/api/test_tags.py b/test/api/test_tags.py index ebc3429..31f558e 100644 --- a/test/api/test_tags.py +++ b/test/api/test_tags.py @@ -12,18 +12,14 @@ class TagsTestCase(unittest.TestCase): @requests_mock.mock() def test_add(self, mock_requests): - requestData = { - "tags": ["important", "Prio1"] - } - expected_dict = { - "tags": [u"engage", u"unit loss", u"discountable", u"important", u"Prio1"] - } + requestData = {"tags": ["important", "Prio1"]} + expected_dict = {"tags": ["engage", "unit loss", "discountable", "important", "Prio1"]} mock_requests.register_uri( - 'POST', + "POST", "https://api.chartmogul.com/v1/customers/UUID/attributes/tags", - request_headers={'Authorization': 'Basic dG9rZW46'}, + request_headers={"Authorization": "Basic dG9rZW46"}, status_code=200, - json=expected_dict + json=expected_dict, ) config = Config("token") # is actually checked in mock tags = Tags.add(config, uuid="UUID", data=requestData).get() diff --git a/test/integration/test_delete_invoice.py b/test/integration/test_delete_invoice.py index 37894e7..0b86c8e 100644 --- a/test/integration/test_delete_invoice.py +++ b/test/integration/test_delete_invoice.py @@ -8,70 +8,86 @@ from chartmogul import DataSource, Customer, Plan, Config, Invoice -config = Config('-') +config = Config("-") + def _create_plan(ds): - customer = Customer.create(config, data={ - "data_source_uuid": ds.uuid, - "external_id": "cus_0001", - "name": "Adam Smith", - "email": "adam@smith.com", - "country": "US", - "city": "New York" - }) - plan = Plan.create(config, data={ - "data_source_uuid": ds.uuid, - "name": "Bronze Plan", - "interval_count": 1, - "interval_unit": "month", - "external_id": "plan_0001" - }) + customer = Customer.create( + config, + data={ + "data_source_uuid": ds.uuid, + "external_id": "cus_0001", + "name": "Adam Smith", + "email": "adam@smith.com", + "country": "US", + "city": "New York", + }, + ) + plan = Plan.create( + config, + data={ + "data_source_uuid": ds.uuid, + "name": "Bronze Plan", + "interval_count": 1, + "interval_unit": "month", + "external_id": "plan_0001", + }, + ) return Promise.all([ds.uuid, customer, plan]) + def _create_invoice(result): ds_uuid, customer, plan = result return Invoice.create( - config, - uuid=customer.uuid, - data={ - "invoices": [{ - "external_id": "INV0001", - "date": datetime(2015, 11, 1, 0, 0, 0), - "currency": "USD", - "due_date": datetime(2015, 11, 15, 0, 0, 0), - "line_items": [{ - "type": "subscription", - "subscription_external_id": "sub_0001", - "plan_uuid": plan.uuid, - "service_period_start": datetime(2015, 11, 1, 0, 0, 0), - "service_period_end": datetime(2015, 12, 1, 0, 0, 0), - "amount_in_cents": 5000, - "quantity": 1, - "discount_code": "PSO86", - "discount_amount_in_cents": 1000, - "tax_amount_in_cents": 900 - }], - "transactions": [{ - "date": datetime(2015, 11, 5, 0, 14, 23), - "type": "payment", - "result": "successful" - }] - }] - }) + config, + uuid=customer.uuid, + data={ + "invoices": [ + { + "external_id": "INV0001", + "date": datetime(2015, 11, 1, 0, 0, 0), + "currency": "USD", + "due_date": datetime(2015, 11, 15, 0, 0, 0), + "line_items": [ + { + "type": "subscription", + "subscription_external_id": "sub_0001", + "plan_uuid": plan.uuid, + "service_period_start": datetime(2015, 11, 1, 0, 0, 0), + "service_period_end": datetime(2015, 12, 1, 0, 0, 0), + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, + } + ], + "transactions": [ + { + "date": datetime(2015, 11, 5, 0, 14, 23), + "type": "payment", + "result": "successful", + } + ], + } + ] + }, + ) + def _delete_invoice(result): return Invoice.destroy(config, uuid=result.invoices[0].uuid) + class DeleteInvoiceTestCase(unittest.TestCase): """ Tests errors & user mistakes. """ - @vcr.use_cassette('fixtures/delete_invoice.yaml', filter_headers=['authorization']) + + @vcr.use_cassette("fixtures/delete_invoice.yaml", filter_headers=["authorization"]) def test_delete_invoice(self): result = ( - DataSource.create( - config, data={'name': 'Test'} - ) + DataSource.create(config, data={"name": "Test"}) .then(_create_plan) .then(_create_invoice) .then(_delete_invoice) diff --git a/test/integration/test_fetch_subscription_events.py b/test/integration/test_fetch_subscription_events.py index cac0b53..cad28ee 100644 --- a/test/integration/test_fetch_subscription_events.py +++ b/test/integration/test_fetch_subscription_events.py @@ -2,12 +2,14 @@ import vcr from chartmogul import Config, SubscriptionEvent -config = Config(api_key = '-') +config = Config(api_key="-") + class FetchSubscriptionEventsTestCase(unittest.TestCase): """ Tests errors & user mistakes. """ - @vcr.use_cassette('fixtures/fetch_subscription_events.yaml', filter_headers=['authorization']) + + @vcr.use_cassette("fixtures/fetch_subscription_events.yaml", filter_headers=["authorization"]) def test_subscription_events(self): - result = SubscriptionEvent.all(config).get() \ No newline at end of file + result = SubscriptionEvent.all(config).get()