From 9eed10313541ded12dbf5c9d1963e45782357959 Mon Sep 17 00:00:00 2001 From: Vincent Pochet Date: Mon, 2 Oct 2023 10:49:17 +0200 Subject: [PATCH] feat(past_usage): Expose route to fetch customer past usage --- lago_python_client/customers/clients.py | 24 +++++++++++-- lago_python_client/models/customer_usage.py | 2 ++ tests/fixtures/customer_past_usage.json | 40 +++++++++++++++++++++ tests/fixtures/customer_usage.json | 3 ++ tests/test_customer_client.py | 31 ++++++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/customer_past_usage.json diff --git a/lago_python_client/customers/clients.py b/lago_python_client/customers/clients.py index a5789229..8758d6ea 100644 --- a/lago_python_client/customers/clients.py +++ b/lago_python_client/customers/clients.py @@ -1,12 +1,12 @@ import sys -from typing import ClassVar, Type +from typing import Any, Mapping, ClassVar, Type, Union from ..base_client import BaseClient from ..mixins import CreateCommandMixin, DestroyCommandMixin, FindAllCommandMixin, FindCommandMixin from ..models.customer import CustomerResponse from ..models.customer_usage import CustomerUsageResponse from ..services.request import make_headers, make_url, send_get_request -from ..services.response import get_response_data, prepare_object_response, Response +from ..services.response import get_response_data, prepare_index_response, prepare_object_response, Response if sys.version_info >= (3, 9): from collections.abc import Mapping @@ -42,6 +42,26 @@ def current_usage(self, resource_id: str, external_subscription_id: str) -> Cust data=get_response_data(response=api_response, key='customer_usage'), ) + def past_usage(self, resource_id: str, external_subscription_id: str, options: Mapping[str, Union[int, str]] = {}) -> Mapping[str, Any]: + api_response: Response = send_get_request( + url=make_url( + origin=self.base_url, + path_parts=(self.API_RESOURCE, resource_id, 'past_usage'), + query_pairs={ + 'external_subscription_id': external_subscription_id, + **options, + }, + ), + headers=make_headers(api_key=self.api_key), + ) + + return prepare_index_response( + api_resource='usage_periods', + response_model=CustomerUsageResponse, + data=get_response_data(response=api_response), + ) + + def portal_url(self, resource_id: str) -> str: api_response: Response = send_get_request( url=make_url( diff --git a/lago_python_client/models/customer_usage.py b/lago_python_client/models/customer_usage.py index 803e9a86..961a7706 100644 --- a/lago_python_client/models/customer_usage.py +++ b/lago_python_client/models/customer_usage.py @@ -25,6 +25,7 @@ class ChargeObject(BaseModel): class ChargeUsage(BaseModel): units: float + events_count: int amount_cents: int amount_currency: str charge: ChargeObject @@ -36,6 +37,7 @@ class CustomerUsageResponse(BaseResponseModel): from_datetime: str to_datetime: str issuing_date: str + invoice_id: Optional[str] currency: str amount_cents: int total_amount_cents: int diff --git a/tests/fixtures/customer_past_usage.json b/tests/fixtures/customer_past_usage.json new file mode 100644 index 00000000..76a0f32c --- /dev/null +++ b/tests/fixtures/customer_past_usage.json @@ -0,0 +1,40 @@ +{ + "usage_periods": [ + { + "from_datetime": "2022-07-01T00:00:00Z", + "to_datetime": "2022-07-31T23:59:59Z", + "issuing_date": "2022-08-01", + "lago_invoice_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "currency": "EUR", + "amount_cents": 123, + "total_amount_cents": 123, + "taxes_amount_cents": 0, + "charges_usage": [ + { + "units": "1.0", + "events_count": 1, + "amount_cents": 123, + "amount_currency": "EUR", + "charge": { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "charge_model": "graduated" + }, + "billable_metric": { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "name": "Usage metric", + "code": "usage_metric", + "aggregation_type": "sum" + }, + "groups": [] + } + ] + } + ], + "meta": { + "current_page": 1, + "next_page": 2, + "prev_page": null, + "total_pages": 2, + "total_count": 49 + } +} diff --git a/tests/fixtures/customer_usage.json b/tests/fixtures/customer_usage.json index 0a28b616..b2810bb3 100644 --- a/tests/fixtures/customer_usage.json +++ b/tests/fixtures/customer_usage.json @@ -3,6 +3,7 @@ "from_datetime": "2022-07-01T00:00:00Z", "to_datetime": "2022-07-31T23:59:59Z", "issuing_date": "2022-08-01", + "invoice_id": null, "currency": "EUR", "amount_cents": 123, "total_amount_cents": 123, @@ -10,6 +11,7 @@ "charges_usage": [ { "units": "1.0", + "events_count": 1, "amount_cents": 123, "amount_currency": "EUR", "charge": { @@ -28,6 +30,7 @@ "key": "google", "value": "europe", "units": "3.0", + "events_count": 1, "amount_cents": 123 } ] diff --git a/tests/test_customer_client.py b/tests/test_customer_client.py index c046c850..cbdf976d 100644 --- a/tests/test_customer_client.py +++ b/tests/test_customer_client.py @@ -104,6 +104,37 @@ def test_invalid_current_usage(httpx_mock: HTTPXMock): client.customers.current_usage('invalid_customer', '123') +def test_valid_past_usage(httpx_mock: HTTPXMock): + client = Client(api_key='886fe239-927d-4072-ab72-6dd345e8dd0d') + + httpx_mock.add_response( + method='GET', + url='https://api.getlago.com/api/v1/customers/external_customer_id/past_usage?external_subscription_id=123', + content=mock_response('customer_past_usage'), + ) + response = client.customers.past_usage('external_customer_id', '123') + + assert len(response['usage_periods']) == 1 + assert response['usage_periods'][0].from_datetime == '2022-07-01T00:00:00Z' + assert len(response['usage_periods'][0].charges_usage) == 1 + assert response['usage_periods'][0].charges_usage[0].units == 1.0 + assert len(response['usage_periods'][0].charges_usage[0].groups) == 0 + + +def test_invalid_past_usage(httpx_mock: HTTPXMock): + client = Client(api_key='886fe239-927d-4072-ab72-6dd345e8dd0d') + + httpx_mock.add_response( + method='GET', + url='https://api.getlago.com/api/v1/customers/invalid_customer/past_usage?external_subscription_id=123', + status_code=404, + content=b'', + ) + + with pytest.raises(LagoApiError): + client.customers.past_usage('invalid_customer', '123') + + def test_valid_portal_url(httpx_mock: HTTPXMock): client = Client(api_key='886fe239-927d-4072-ab72-6dd345e8dd0d')