Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions chartmogul/api/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,6 @@ class DataSource(Resource):
defaults=[None, None, None]
)

@classmethod
def _preProcessParams(cls, params):
params = super()._preProcessParams(params)

for query_param in cls._bool_query_params:
if query_param in params and isinstance(params[query_param], bool):
if params[query_param] is True:
params[query_param] = 'true'
else:
del params[query_param]

return params

class _Schema(Schema):
uuid = fields.String()
name = fields.String()
Expand Down
10 changes: 10 additions & 0 deletions chartmogul/api/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class Invoice(Resource):

_path = "/import/customers{/uuid}/invoices"
_root_key = "invoices"
_bool_query_params = [
'include_edit_histories',
'with_disabled'
]
_many = namedtuple(
"Invoices",
[_root_key, "cursor", "has_more", "customer_uuid"],

Choose a reason for hiding this comment

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

[question] Shouldn't _bool_query_params be added to this array, similar to DataSource (or perhaps even add them automatically in Resource)? If not, is it still necessary to add them there?

Expand All @@ -58,6 +62,12 @@ class _Schema(Schema):
date = fields.DateTime()
due_date = fields.DateTime(allow_none=True)

disabled = fields.Boolean(allow_none=True)
disabled_at = fields.DateTime(allow_none=True)
disabled_by = fields.String(allow_none=True)
edit_history_summary = fields.Dict(allow_none=True)

Choose a reason for hiding this comment

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

[major] When trying to retrieve this field from few random invoices in production, I get:

AttributeError: 'Invoice' object has no attribute 'edit_history_summary'

Could you confirm that it works for you in production using any invoice?

errors = fields.Dict(allow_none=True)

line_items = fields.Nested(LineItem._Schema, many=True, unknown=EXCLUDE)
transactions = fields.Nested(Transaction._Schema, many=True, unknown=EXCLUDE)

Expand Down
9 changes: 9 additions & 0 deletions chartmogul/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ def _preProcessParams(cls, params):
if key in params:
params[replacement] = params[key]
del params[key]

if hasattr(cls, '_bool_query_params'):
for query_param in cls._bool_query_params:
if query_param in params and isinstance(params[query_param], bool):
if params[query_param] is True:
params[query_param] = 'true'
elif params[query_param] is False:
params[query_param] = 'false'

Choose a reason for hiding this comment

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

[question] Why not delete it as it was before - is it needed because sometimes value can be true by default?


return params

@classmethod
Expand Down
176 changes: 175 additions & 1 deletion test/api/test_invoice.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pylama:ignore=W0212
import unittest
from datetime import datetime
from datetime import datetime, timezone

import requests_mock

Expand Down Expand Up @@ -175,6 +175,20 @@
"date": "2015-11-01T00:00:00.000Z",
"due_date": "2015-11-15T00:00:00.000Z",
"currency": "USD",
"disabled": False,
"disabled_at": None,
"disabled_by": None,
"edit_history_summary": {
"values_changed": {
"amount_in_cents": {
"original_value": 4500,
"edited_value": 5000
}
},
"latest_edit_author": "admin@example.com",
"latest_edit_performed_at": "2024-01-10T12:00:00.000Z"
},
"errors": None,
"line_items": [
{
"uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56",
Expand Down Expand Up @@ -231,6 +245,27 @@
"date": "2015-11-01T00:00:00.000Z",
"due_date": "2015-11-15T00:00:00.000Z",
"currency": "USD",
"disabled": True,
"disabled_at": "2024-01-15T10:30:00.000Z",
"disabled_by": "user@example.com",
"edit_history_summary": {
"values_changed": {
"currency": {
"original_value": "EUR",
"edited_value": "USD"
},
"date": {
"original_value": "2024-01-01T00:00:00.000Z",
"edited_value": "2024-01-02T00:00:00.000Z"
}
},
"latest_edit_author": "editor@example.com",
"latest_edit_performed_at": "2024-01-20T15:45:00.000Z"
},
"errors": {
"currency": ["Currency is invalid", "Currency must be supported"],
"date": ["Date is in the future"]
},
"line_items": [
{
"uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56",
Expand Down Expand Up @@ -447,3 +482,142 @@ def test_retrieve_invoice(self, mock_requests):
self.assertTrue(isinstance(result, Invoice))

self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059")

@requests_mock.mock()
def test_retrieve_invoice_with_validation_type(self, mock_requests):
mock_requests.register_uri(
"GET",
("https://api.chartmogul.com/v1/invoices/inv_22910fc6-c931-48e7-ac12-90d2cb5f0059"
"?validation_type=all"),
request_headers={"Authorization": "Basic dG9rZW46"},
headers={"Content-Type": "application/json"},
status_code=200,
json=retrieveInvoiceExample,
)

config = Config("token")
result = Invoice.retrieve(
config,
uuid="inv_22910fc6-c931-48e7-ac12-90d2cb5f0059",
validation_type="all"
).get()

self.assertEqual(mock_requests.call_count, 1, "expected call")
self.assertEqual(
mock_requests.last_request.qs,
{"validation_type": ["all"]},
)
self.assertTrue(isinstance(result, Invoice))
self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059")

@requests_mock.mock()
def test_retrieve_invoice_with_all_params(self, mock_requests):
mock_requests.register_uri(
"GET",
("https://api.chartmogul.com/v1/invoices/inv_22910fc6-c931-48e7-ac12-90d2cb5f0059"
"?validation_type=invalid&include_edit_histories=true&with_disabled=false"),
request_headers={"Authorization": "Basic dG9rZW46"},
headers={"Content-Type": "application/json"},
status_code=200,
json=retrieveInvoiceExample,
)

config = Config("token")
result = Invoice.retrieve(
config,
uuid="inv_22910fc6-c931-48e7-ac12-90d2cb5f0059",
validation_type="invalid",
include_edit_histories=True,
with_disabled=False
).get()

self.assertEqual(mock_requests.call_count, 1, "expected call")
qs = mock_requests.last_request.qs
self.assertEqual(qs["validation_type"], ["invalid"])
self.assertEqual(qs["include_edit_histories"], ["true"])
self.assertEqual(qs["with_disabled"], ["false"])
self.assertTrue(isinstance(result, Invoice))
self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059")
self.assertTrue(result.disabled)
self.assertEqual(result.disabled_at, datetime(2024, 1, 15, 10, 30, tzinfo=timezone.utc))
self.assertEqual(result.disabled_by, "user@example.com")
self.assertIsNotNone(result.edit_history_summary)
self.assertIn("values_changed", result.edit_history_summary)
self.assertIn("currency", result.edit_history_summary["values_changed"])
self.assertEqual(
result.edit_history_summary["values_changed"]["currency"]["original_value"],
"EUR"
)
self.assertEqual(
result.edit_history_summary["values_changed"]["currency"]["edited_value"],
"USD"
)
self.assertEqual(
result.edit_history_summary["latest_edit_author"],
"editor@example.com"
)
self.assertEqual(
result.edit_history_summary["latest_edit_performed_at"],
"2024-01-20T15:45:00.000Z"
)
self.assertIsNotNone(result.errors)
self.assertIn("currency", result.errors)
self.assertIsInstance(result.errors["currency"], list)
self.assertEqual(len(result.errors["currency"]), 2)
self.assertEqual(result.errors["currency"][0], "Currency is invalid")
self.assertEqual(result.errors["currency"][1], "Currency must be supported")
self.assertIn("date", result.errors)
self.assertIsInstance(result.errors["date"], list)
self.assertEqual(len(result.errors["date"]), 1)
self.assertEqual(result.errors["date"][0], "Date is in the future")

@requests_mock.mock()
def test_all_invoices_with_validation_type(self, mock_requests):
mock_requests.register_uri(
"GET",
"https://api.chartmogul.com/v1/invoices?validation_type=all",
request_headers={"Authorization": "Basic dG9rZW46"},
headers={"Content-Type": "application/json"},
status_code=200,
json=invoiceListExample,
)

config = Config("token")
result = Invoice.all(config, validation_type="all").get()

self.assertEqual(mock_requests.call_count, 1, "expected call")
self.assertEqual(
mock_requests.last_request.qs,
{"validation_type": ["all"]},
)

self.assertTrue(isinstance(result, Invoice._many))
self.assertEqual(len(result.invoices), 1)

@requests_mock.mock()
def test_all_invoices_with_all_params(self, mock_requests):
mock_requests.register_uri(
"GET",
("https://api.chartmogul.com/v1/invoices"
"?validation_type=valid&include_edit_histories=true&with_disabled=true"),
request_headers={"Authorization": "Basic dG9rZW46"},
headers={"Content-Type": "application/json"},
status_code=200,
json=invoiceListExample,
)

config = Config("token")
result = Invoice.all(
config,
validation_type="valid",
include_edit_histories=True,
with_disabled=True
).get()

self.assertEqual(mock_requests.call_count, 1, "expected call")
qs = mock_requests.last_request.qs
self.assertEqual(qs["validation_type"], ["valid"])
self.assertEqual(qs["include_edit_histories"], ["true"])
self.assertEqual(qs["with_disabled"], ["true"])
self.assertTrue(isinstance(result, Invoice._many))
self.assertEqual(len(result.invoices), 1)