From 30d23b45e8e543c08d5e52b0df517d1532af7ea7 Mon Sep 17 00:00:00 2001 From: Ivan Novosad Date: Wed, 15 Nov 2023 21:35:46 +0100 Subject: [PATCH] feat(analytics): Add analytics resources --- lago_python_client/client.py | 20 ++++++++++ lago_python_client/gross_revenues/__init__.py | 0 lago_python_client/gross_revenues/clients.py | 39 +++++++++++++++++++ .../invoiced_usages/__init__.py | 0 lago_python_client/invoiced_usages/clients.py | 39 +++++++++++++++++++ lago_python_client/models/gross_revenue.py | 13 +++++++ lago_python_client/models/invoiced_usage.py | 14 +++++++ lago_python_client/models/mrr.py | 13 +++++++ .../models/outstanding_invoice.py | 15 +++++++ lago_python_client/mrrs/__init__.py | 0 lago_python_client/mrrs/clients.py | 39 +++++++++++++++++++ .../outstanding_invoices/__init__.py | 0 .../outstanding_invoices/clients.py | 39 +++++++++++++++++++ tests/fixtures/gross_revenue_index.json | 15 +++++++ tests/fixtures/invoiced_usage_index.json | 17 ++++++++ tests/fixtures/mrr_index.json | 15 +++++++ tests/fixtures/outstanding_invoice_index.json | 19 +++++++++ tests/test_gross_revenue_client.py | 27 +++++++++++++ tests/test_invoiced_usage_client.py | 28 +++++++++++++ tests/test_mrr_client.py | 27 +++++++++++++ tests/test_outstanding_invoices_client.py | 29 ++++++++++++++ 21 files changed, 408 insertions(+) create mode 100644 lago_python_client/gross_revenues/__init__.py create mode 100644 lago_python_client/gross_revenues/clients.py create mode 100644 lago_python_client/invoiced_usages/__init__.py create mode 100644 lago_python_client/invoiced_usages/clients.py create mode 100644 lago_python_client/models/gross_revenue.py create mode 100644 lago_python_client/models/invoiced_usage.py create mode 100644 lago_python_client/models/mrr.py create mode 100644 lago_python_client/models/outstanding_invoice.py create mode 100644 lago_python_client/mrrs/__init__.py create mode 100644 lago_python_client/mrrs/clients.py create mode 100644 lago_python_client/outstanding_invoices/__init__.py create mode 100644 lago_python_client/outstanding_invoices/clients.py create mode 100644 tests/fixtures/gross_revenue_index.json create mode 100644 tests/fixtures/invoiced_usage_index.json create mode 100644 tests/fixtures/mrr_index.json create mode 100644 tests/fixtures/outstanding_invoice_index.json create mode 100644 tests/test_gross_revenue_client.py create mode 100644 tests/test_invoiced_usage_client.py create mode 100644 tests/test_mrr_client.py create mode 100644 tests/test_outstanding_invoices_client.py diff --git a/lago_python_client/client.py b/lago_python_client/client.py index 316fdbc4..a35544fd 100644 --- a/lago_python_client/client.py +++ b/lago_python_client/client.py @@ -8,8 +8,12 @@ from .events.clients import EventClient from .fees.clients import FeeClient from .functools_ext import callable_cached_property +from .gross_revenues.clients import GrossRevenueClient from .invoices.clients import InvoiceClient +from .invoiced_usages.clients import InvoicedUsageClient +from .mrrs.clients import MrrClient from .organizations.clients import OrganizationClient +from .outstanding_invoices.clients import OutstandingInvoiceClient from .plans.clients import PlanClient from .subscriptions.clients import SubscriptionClient from .taxes.clients import TaxClient @@ -71,14 +75,30 @@ def events(self) -> EventClient: def fees(self) -> FeeClient: return FeeClient(self.base_api_url, self.api_key) + @callable_cached_property + def gross_revenues(self) -> GrossRevenueClient: + return GrossRevenueClient(self.base_api_url, self.api_key) + @callable_cached_property def invoices(self) -> InvoiceClient: return InvoiceClient(self.base_api_url, self.api_key) + @callable_cached_property + def invoiced_usages(self) -> InvoicedUsageClient: + return InvoicedUsageClient(self.base_api_url, self.api_key) + + @callable_cached_property + def mrrs(self) -> MrrClient: + return MrrClient(self.base_api_url, self.api_key) + @callable_cached_property def organizations(self) -> OrganizationClient: return OrganizationClient(self.base_api_url, self.api_key) + @callable_cached_property + def outstanding_invoices(self) -> OutstandingInvoiceClient: + return OutstandingInvoiceClient(self.base_api_url, self.api_key) + @callable_cached_property def plans(self) -> PlanClient: return PlanClient(self.base_api_url, self.api_key) diff --git a/lago_python_client/gross_revenues/__init__.py b/lago_python_client/gross_revenues/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lago_python_client/gross_revenues/clients.py b/lago_python_client/gross_revenues/clients.py new file mode 100644 index 00000000..0b134983 --- /dev/null +++ b/lago_python_client/gross_revenues/clients.py @@ -0,0 +1,39 @@ +import sys +from typing import Any, ClassVar, Type, Union + +from ..base_client import BaseClient +from ..mixins import FindAllCommandMixin +from ..models.gross_revenue import GrossRevenueResponse +from ..services.request import make_headers, make_url, send_get_request +from ..services.response import get_response_data, prepare_index_response, Response + +if sys.version_info >= (3, 9): + from collections.abc import Mapping +else: + from typing import Mapping + + +class GrossRevenueClient( + FindAllCommandMixin[GrossRevenueResponse], + BaseClient, +): + API_RESOURCE: ClassVar[str] = 'gross_revenues' + RESPONSE_MODEL: ClassVar[Type[GrossRevenueResponse]] = GrossRevenueResponse + ROOT_NAME: ClassVar[str] = 'gross_revenue' + + def find_all(self, options: Mapping[str, Union[int, str]] = {}) -> Mapping[str, Any]: + api_response: Response = send_get_request( + url=make_url( + origin=self.base_url, + path_parts=('analytics', 'gross_revenue'), + query_pairs=options, + ), + headers=make_headers(api_key=self.api_key), + ) + + # Process response data + return prepare_index_response( + api_resource=self.API_RESOURCE, + response_model=self.RESPONSE_MODEL, + data=get_response_data(response=api_response), + ) diff --git a/lago_python_client/invoiced_usages/__init__.py b/lago_python_client/invoiced_usages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lago_python_client/invoiced_usages/clients.py b/lago_python_client/invoiced_usages/clients.py new file mode 100644 index 00000000..c0694385 --- /dev/null +++ b/lago_python_client/invoiced_usages/clients.py @@ -0,0 +1,39 @@ +import sys +from typing import Any, ClassVar, Type, Union + +from ..base_client import BaseClient +from ..mixins import FindAllCommandMixin +from ..models.invoiced_usage import InvoicedUsageResponse +from ..services.request import make_headers, make_url, send_get_request +from ..services.response import get_response_data, prepare_index_response, Response + +if sys.version_info >= (3, 9): + from collections.abc import Mapping +else: + from typing import Mapping + + +class InvoicedUsageClient( + FindAllCommandMixin[InvoicedUsageResponse], + BaseClient, +): + API_RESOURCE: ClassVar[str] = 'invoiced_usages' + RESPONSE_MODEL: ClassVar[Type[InvoicedUsageResponse]] = InvoicedUsageResponse + ROOT_NAME: ClassVar[str] = 'invoiced_usage' + + def find_all(self, options: Mapping[str, Union[int, str]] = {}) -> Mapping[str, Any]: + api_response: Response = send_get_request( + url=make_url( + origin=self.base_url, + path_parts=('analytics', 'invoiced_usage'), + query_pairs=options, + ), + headers=make_headers(api_key=self.api_key), + ) + + # Process response data + return prepare_index_response( + api_resource=self.API_RESOURCE, + response_model=self.RESPONSE_MODEL, + data=get_response_data(response=api_response), + ) diff --git a/lago_python_client/models/gross_revenue.py b/lago_python_client/models/gross_revenue.py new file mode 100644 index 00000000..f0d20aac --- /dev/null +++ b/lago_python_client/models/gross_revenue.py @@ -0,0 +1,13 @@ +from typing import List, Optional + +from ..base_model import BaseModel, BaseResponseModel + + +class GrossRevenueResponse(BaseResponseModel): + amount_cents: Optional[int] + currency: Optional[str] + month: str + + +class GrossRevenuesResponse(BaseResponseModel): + __root__: List[GrossRevenueResponse] diff --git a/lago_python_client/models/invoiced_usage.py b/lago_python_client/models/invoiced_usage.py new file mode 100644 index 00000000..95d861e5 --- /dev/null +++ b/lago_python_client/models/invoiced_usage.py @@ -0,0 +1,14 @@ +from typing import List, Optional + +from ..base_model import BaseModel, BaseResponseModel + + +class InvoicedUsageResponse(BaseResponseModel): + amount_cents: int + code: Optional[str] + currency: str + month: str + + +class InvoicedUsagesResponse(BaseResponseModel): + __root__: List[InvoicedUsageResponse] diff --git a/lago_python_client/models/mrr.py b/lago_python_client/models/mrr.py new file mode 100644 index 00000000..dad632c3 --- /dev/null +++ b/lago_python_client/models/mrr.py @@ -0,0 +1,13 @@ +from typing import List, Optional + +from ..base_model import BaseModel, BaseResponseModel + + +class MrrResponse(BaseResponseModel): + amount_cents: Optional[int] + currency: Optional[str] + month: str + + +class MrrsResponse(BaseResponseModel): + __root__: List[MrrResponse] diff --git a/lago_python_client/models/outstanding_invoice.py b/lago_python_client/models/outstanding_invoice.py new file mode 100644 index 00000000..c07d47fe --- /dev/null +++ b/lago_python_client/models/outstanding_invoice.py @@ -0,0 +1,15 @@ +from typing import List, Optional + +from ..base_model import BaseModel, BaseResponseModel + + +class OutstandingInvoiceResponse(BaseResponseModel): + amount_cents: int + currency: Optional[str] + month: str + invoices_count: int + payment_status: Optional[str] + + +class OutstandingInvoicesResponse(BaseResponseModel): + __root__: List[OutstandingInvoiceResponse] diff --git a/lago_python_client/mrrs/__init__.py b/lago_python_client/mrrs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lago_python_client/mrrs/clients.py b/lago_python_client/mrrs/clients.py new file mode 100644 index 00000000..2652aaa5 --- /dev/null +++ b/lago_python_client/mrrs/clients.py @@ -0,0 +1,39 @@ +import sys +from typing import Any, ClassVar, Type, Union + +from ..base_client import BaseClient +from ..mixins import FindAllCommandMixin +from ..models.mrr import MrrResponse +from ..services.request import make_headers, make_url, send_get_request +from ..services.response import get_response_data, prepare_index_response, Response + +if sys.version_info >= (3, 9): + from collections.abc import Mapping +else: + from typing import Mapping + + +class MrrClient( + FindAllCommandMixin[MrrResponse], + BaseClient, +): + API_RESOURCE: ClassVar[str] = 'mrrs' + RESPONSE_MODEL: ClassVar[Type[MrrResponse]] = MrrResponse + ROOT_NAME: ClassVar[str] = 'mrr' + + def find_all(self, options: Mapping[str, Union[int, str]] = {}) -> Mapping[str, Any]: + api_response: Response = send_get_request( + url=make_url( + origin=self.base_url, + path_parts=('analytics', 'mrr'), + query_pairs=options, + ), + headers=make_headers(api_key=self.api_key), + ) + + # Process response data + return prepare_index_response( + api_resource=self.API_RESOURCE, + response_model=self.RESPONSE_MODEL, + data=get_response_data(response=api_response), + ) diff --git a/lago_python_client/outstanding_invoices/__init__.py b/lago_python_client/outstanding_invoices/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lago_python_client/outstanding_invoices/clients.py b/lago_python_client/outstanding_invoices/clients.py new file mode 100644 index 00000000..6326378a --- /dev/null +++ b/lago_python_client/outstanding_invoices/clients.py @@ -0,0 +1,39 @@ +import sys +from typing import Any, ClassVar, Type, Union + +from ..base_client import BaseClient +from ..mixins import FindAllCommandMixin +from ..models.outstanding_invoice import OutstandingInvoiceResponse +from ..services.request import make_headers, make_url, send_get_request +from ..services.response import get_response_data, prepare_index_response, Response + +if sys.version_info >= (3, 9): + from collections.abc import Mapping +else: + from typing import Mapping + + +class OutstandingInvoiceClient( + FindAllCommandMixin[OutstandingInvoiceResponse], + BaseClient, +): + API_RESOURCE: ClassVar[str] = 'outstanding_invoices' + RESPONSE_MODEL: ClassVar[Type[OutstandingInvoiceResponse]] = OutstandingInvoiceResponse + ROOT_NAME: ClassVar[str] = 'outstanding_invoice' + + def find_all(self, options: Mapping[str, Union[int, str]] = {}) -> Mapping[str, Any]: + api_response: Response = send_get_request( + url=make_url( + origin=self.base_url, + path_parts=('analytics', self.API_RESOURCE), + query_pairs=options, + ), + headers=make_headers(api_key=self.api_key), + ) + + # Process response data + return prepare_index_response( + api_resource=self.API_RESOURCE, + response_model=self.RESPONSE_MODEL, + data=get_response_data(response=api_response), + ) diff --git a/tests/fixtures/gross_revenue_index.json b/tests/fixtures/gross_revenue_index.json new file mode 100644 index 00000000..0fa4bbf3 --- /dev/null +++ b/tests/fixtures/gross_revenue_index.json @@ -0,0 +1,15 @@ +{ + "gross_revenues": [ + { + "month": "2023-11-01T00:00:00.000Z", + "amount_cents": 100, + "currency": "EUR" + }, + { + "month": "2023-12-01T00:00:00.000Z", + "amount_cents": 200, + "currency": "USD" + } + ], + "meta": {} +} diff --git a/tests/fixtures/invoiced_usage_index.json b/tests/fixtures/invoiced_usage_index.json new file mode 100644 index 00000000..5de71886 --- /dev/null +++ b/tests/fixtures/invoiced_usage_index.json @@ -0,0 +1,17 @@ +{ + "invoiced_usages": [ + { + "month": "2023-11-01T00:00:00.000Z", + "amount_cents": 100, + "currency": "EUR", + "code": "code" + }, + { + "month": "2023-12-01T00:00:00.000Z", + "amount_cents": 200, + "currency": "USD", + "code": "code" + } + ], + "meta": {} +} diff --git a/tests/fixtures/mrr_index.json b/tests/fixtures/mrr_index.json new file mode 100644 index 00000000..7121450f --- /dev/null +++ b/tests/fixtures/mrr_index.json @@ -0,0 +1,15 @@ +{ + "mrrs": [ + { + "month": "2023-11-01T00:00:00.000Z", + "amount_cents": 100, + "currency": "EUR" + }, + { + "month": "2023-12-01T00:00:00.000Z", + "amount_cents": 200, + "currency": "USD" + } + ], + "meta": {} +} diff --git a/tests/fixtures/outstanding_invoice_index.json b/tests/fixtures/outstanding_invoice_index.json new file mode 100644 index 00000000..7b6566b1 --- /dev/null +++ b/tests/fixtures/outstanding_invoice_index.json @@ -0,0 +1,19 @@ +{ + "outstanding_invoices": [ + { + "month": "2023-11-01T00:00:00.000Z", + "amount_cents": 100, + "currency": "EUR", + "invoices_count": 10, + "payment_status": "pending" + }, + { + "month": "2023-12-01T00:00:00.000Z", + "amount_cents": 200, + "currency": "USD", + "invoices_count": 5, + "payment_status": "pending" + } + ], + "meta": {} +} diff --git a/tests/test_gross_revenue_client.py b/tests/test_gross_revenue_client.py new file mode 100644 index 00000000..162a4903 --- /dev/null +++ b/tests/test_gross_revenue_client.py @@ -0,0 +1,27 @@ +import os + +import pytest +from pytest_httpx import HTTPXMock + +from lago_python_client.client import Client +from lago_python_client.exceptions import LagoApiError +from lago_python_client.models.gross_revenue import GrossRevenueResponse + + +def mock_collection_response(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + data_path = os.path.join(current_dir, 'fixtures/gross_revenue_index.json') + + with open(data_path, 'rb') as gross_revenues_response: + return gross_revenues_response.read() + + +def test_valid_find_all_gross_revenues_request(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/analytics/gross_revenue', content=mock_collection_response()) + response = client.gross_revenues.find_all() + + assert response['gross_revenues'][0].currency == 'EUR' + assert response['gross_revenues'][0].amount_cents == 100 + assert response['gross_revenues'][0].month == '2023-11-01T00:00:00.000Z' diff --git a/tests/test_invoiced_usage_client.py b/tests/test_invoiced_usage_client.py new file mode 100644 index 00000000..dcf40fa5 --- /dev/null +++ b/tests/test_invoiced_usage_client.py @@ -0,0 +1,28 @@ +import os + +import pytest +from pytest_httpx import HTTPXMock + +from lago_python_client.client import Client +from lago_python_client.exceptions import LagoApiError +from lago_python_client.models.invoiced_usage import InvoicedUsageResponse + + +def mock_collection_response(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + data_path = os.path.join(current_dir, 'fixtures/invoiced_usage_index.json') + + with open(data_path, 'rb') as invoiced_usages_response: + return invoiced_usages_response.read() + + +def test_valid_find_all_invoiced_usages_request(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/analytics/invoiced_usage', content=mock_collection_response()) + response = client.invoiced_usages.find_all() + + assert response['invoiced_usages'][0].currency == 'EUR' + assert response['invoiced_usages'][0].amount_cents == 100 + assert response['invoiced_usages'][0].code == 'code' + assert response['invoiced_usages'][0].month == '2023-11-01T00:00:00.000Z' diff --git a/tests/test_mrr_client.py b/tests/test_mrr_client.py new file mode 100644 index 00000000..c35a61e8 --- /dev/null +++ b/tests/test_mrr_client.py @@ -0,0 +1,27 @@ +import os + +import pytest +from pytest_httpx import HTTPXMock + +from lago_python_client.client import Client +from lago_python_client.exceptions import LagoApiError +from lago_python_client.models.mrr import MrrResponse + + +def mock_collection_response(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + data_path = os.path.join(current_dir, 'fixtures/mrr_index.json') + + with open(data_path, 'rb') as mrrs_response: + return mrrs_response.read() + + +def test_valid_find_all_mrrs_request(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/analytics/mrr', content=mock_collection_response()) + response = client.mrrs.find_all() + + assert response['mrrs'][0].currency == 'EUR' + assert response['mrrs'][0].amount_cents == 100 + assert response['mrrs'][0].month == '2023-11-01T00:00:00.000Z' diff --git a/tests/test_outstanding_invoices_client.py b/tests/test_outstanding_invoices_client.py new file mode 100644 index 00000000..102fc740 --- /dev/null +++ b/tests/test_outstanding_invoices_client.py @@ -0,0 +1,29 @@ +import os + +import pytest +from pytest_httpx import HTTPXMock + +from lago_python_client.client import Client +from lago_python_client.exceptions import LagoApiError +from lago_python_client.models.outstanding_invoice import OutstandingInvoiceResponse + + +def mock_collection_response(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + data_path = os.path.join(current_dir, 'fixtures/outstanding_invoice_index.json') + + with open(data_path, 'rb') as outstanding_invoices_response: + return outstanding_invoices_response.read() + + +def test_valid_find_all_outstanding_invoices_request(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/analytics/outstanding_invoices', content=mock_collection_response()) + response = client.outstanding_invoices.find_all() + + assert response['outstanding_invoices'][0].currency == 'EUR' + assert response['outstanding_invoices'][0].amount_cents == 100 + assert response['outstanding_invoices'][0].month == '2023-11-01T00:00:00.000Z' + assert response['outstanding_invoices'][0].invoices_count == 10 + assert response['outstanding_invoices'][0].payment_status == 'pending'