Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ProgressiveBilling) - Add usage_thresholds & progressive_billing_credit_amount_cents #261

Merged
merged 7 commits into from
Aug 30, 2024
Merged
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
20 changes: 20 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Ruff
on: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: 3.11
- name: Install dependencies
run: |
python -m pip install .[test]
- name: Validate linter rules with Ruff
uses: chartboost/ruff-action@v1
- name: Validate formatting with Ruff
uses: chartboost/ruff-action@v1
with:
args: 'format --check'
4 changes: 2 additions & 2 deletions lago_python_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from lago_python_client.client import Client
from lago_python_client.version import LAGO_VERSION
from lago_python_client.client import Client as Client
from lago_python_client.version import LAGO_VERSION as LAGO_VERSION
12 changes: 9 additions & 3 deletions lago_python_client/add_ons/clients.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
from typing import ClassVar, Type

from ..base_client import BaseClient
from ..mixins import CreateCommandMixin, DestroyCommandMixin, FindAllCommandMixin, FindCommandMixin, UpdateCommandMixin
from ..mixins import (
CreateCommandMixin,
DestroyCommandMixin,
FindAllCommandMixin,
FindCommandMixin,
UpdateCommandMixin,
)
from ..models.add_on import AddOnResponse


Expand All @@ -13,6 +19,6 @@ class AddOnClient(
UpdateCommandMixin[AddOnResponse],
BaseClient,
):
API_RESOURCE: ClassVar[str] = 'add_ons'
API_RESOURCE: ClassVar[str] = "add_ons"
RESPONSE_MODEL: ClassVar[Type[AddOnResponse]] = AddOnResponse
ROOT_NAME: ClassVar[str] = 'add_on'
ROOT_NAME: ClassVar[str] = "add_on"
22 changes: 10 additions & 12 deletions lago_python_client/billable_metrics/clients.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import sys
from typing import Any, ClassVar, Type, Union
from typing import ClassVar, Type

from ..base_client import BaseClient
from ..mixins import CreateCommandMixin, DestroyCommandMixin, FindAllCommandMixin, FindCommandMixin, UpdateCommandMixin
from ..mixins import (
CreateCommandMixin,
DestroyCommandMixin,
FindAllCommandMixin,
FindCommandMixin,
UpdateCommandMixin,
)
from ..models.billable_metric import BillableMetricResponse
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 BillableMetricClient(
Expand All @@ -21,6 +19,6 @@ class BillableMetricClient(
UpdateCommandMixin[BillableMetricResponse],
BaseClient,
):
API_RESOURCE: ClassVar[str] = 'billable_metrics'
API_RESOURCE: ClassVar[str] = "billable_metrics"
RESPONSE_MODEL: ClassVar[Type[BillableMetricResponse]] = BillableMetricResponse
ROOT_NAME: ClassVar[str] = 'billable_metric'
ROOT_NAME: ClassVar[str] = "billable_metric"
6 changes: 3 additions & 3 deletions lago_python_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@


class Client:
BASE_URL: Final[str] = 'https://api.getlago.com/'
API_PATH: Final[str] = 'api/v1/'
BASE_URL: Final[str] = "https://api.getlago.com/"
API_PATH: Final[str] = "api/v1/"

def __init__(self, api_key: str = '', api_url: str = '') -> None:
def __init__(self, api_key: str = "", api_url: str = "") -> None:
self.api_key: str = api_key
self.api_url: str = api_url

Expand Down
29 changes: 22 additions & 7 deletions lago_python_client/coupons/clients.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
from typing import ClassVar, Type

from ..base_client import BaseClient
from ..mixins import CreateCommandMixin, DestroyCommandMixin, FindAllCommandMixin, FindCommandMixin, UpdateCommandMixin
from ..mixins import (
CreateCommandMixin,
DestroyCommandMixin,
FindAllCommandMixin,
FindCommandMixin,
UpdateCommandMixin,
)
from ..models.coupon import CouponResponse
from ..models.applied_coupon import AppliedCouponResponse
from ..services.request import make_headers, make_url, send_delete_request
Expand All @@ -16,21 +22,30 @@ class CouponClient(
UpdateCommandMixin[CouponResponse],
BaseClient,
):
API_RESOURCE: ClassVar[str] = 'coupons'
API_RESOURCE: ClassVar[str] = "coupons"
RESPONSE_MODEL: ClassVar[Type[CouponResponse]] = CouponResponse
ROOT_NAME: ClassVar[str] = 'coupon'
ROOT_NAME: ClassVar[str] = "coupon"


class AppliedCouponClient(CreateCommandMixin[AppliedCouponResponse], FindAllCommandMixin[AppliedCouponResponse], BaseClient):
API_RESOURCE: ClassVar[str] = 'applied_coupons'
class AppliedCouponClient(
CreateCommandMixin[AppliedCouponResponse],
FindAllCommandMixin[AppliedCouponResponse],
BaseClient,
):
API_RESOURCE: ClassVar[str] = "applied_coupons"
RESPONSE_MODEL: ClassVar[Type[AppliedCouponResponse]] = AppliedCouponResponse
ROOT_NAME: ClassVar[str] = 'applied_coupon'
ROOT_NAME: ClassVar[str] = "applied_coupon"

def destroy(self, external_customer_id: str, applied_coupon_id: str) -> AppliedCouponResponse:
api_response: Response = send_delete_request(
url=make_url(
origin=self.base_url,
path_parts=('customers', external_customer_id, self.API_RESOURCE, applied_coupon_id),
path_parts=(
"customers",
external_customer_id,
self.API_RESOURCE,
applied_coupon_id,
),
),
headers=make_headers(api_key=self.api_key),
)
Expand Down
42 changes: 29 additions & 13 deletions lago_python_client/credit_notes/clients.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
from typing import ClassVar, Optional, Type, Union
from typing import ClassVar, Optional, Type

from ..base_client import BaseClient
from ..mixins import CreateCommandMixin, FindAllCommandMixin, FindCommandMixin, UpdateCommandMixin
from ..models.credit_note import CreditNoteResponse, CreditNoteEstimatedResponse, CreditNoteEstimate
from ..mixins import (
CreateCommandMixin,
FindAllCommandMixin,
FindCommandMixin,
UpdateCommandMixin,
)
from ..models.credit_note import (
CreditNoteResponse,
CreditNoteEstimatedResponse,
CreditNoteEstimate,
)
from ..services.json import to_json
from ..services.request import make_headers, make_url, send_post_request, send_put_request
from ..services.request import (
make_headers,
make_url,
send_post_request,
send_put_request,
)
from ..services.response import get_response_data, prepare_object_response, Response


Expand All @@ -15,16 +29,16 @@ class CreditNoteClient(
UpdateCommandMixin[CreditNoteResponse],
BaseClient,
):
API_RESOURCE: ClassVar[str] = 'credit_notes'
ESTIMATE_API_RESOURCE: ClassVar[str] = 'estimated_credit_note'
API_RESOURCE: ClassVar[str] = "credit_notes"
ESTIMATE_API_RESOURCE: ClassVar[str] = "estimated_credit_note"
RESPONSE_MODEL: ClassVar[Type[CreditNoteResponse]] = CreditNoteResponse
ROOT_NAME: ClassVar[str] = 'credit_note'
ROOT_NAME: ClassVar[str] = "credit_note"

def download(self, resource_id: str) -> Optional[CreditNoteResponse]:
api_response: Response = send_post_request(
url=make_url(
origin=self.base_url,
path_parts=(self.API_RESOURCE, resource_id, 'download'),
path_parts=(self.API_RESOURCE, resource_id, "download"),
),
headers=make_headers(api_key=self.api_key),
)
Expand All @@ -42,7 +56,7 @@ def void(self, resource_id: str) -> CreditNoteResponse:
api_response: Response = send_put_request(
url=make_url(
origin=self.base_url,
path_parts=(self.API_RESOURCE, resource_id, 'void'),
path_parts=(self.API_RESOURCE, resource_id, "void"),
),
headers=make_headers(api_key=self.api_key),
)
Expand All @@ -56,11 +70,13 @@ def estimate(self, input_object: CreditNoteEstimate) -> CreditNoteEstimatedRespo
api_response: Response = send_post_request(
url=make_url(
origin=self.base_url,
path_parts=(self.API_RESOURCE, 'estimate'),
path_parts=(self.API_RESOURCE, "estimate"),
),
content=to_json(
{
self.ROOT_NAME: input_object.dict(),
}
),
content=to_json({
self.ROOT_NAME: input_object.dict(),
}),
headers=make_headers(api_key=self.api_key),
)

Expand Down
53 changes: 36 additions & 17 deletions lago_python_client/customers/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@
from typing import Any, Mapping, ClassVar, Type, Union

from ..base_client import BaseClient
from ..mixins import CreateCommandMixin, DestroyCommandMixin, FindAllCommandMixin, FindCommandMixin
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, send_post_request
from ..services.response import get_response_data, prepare_index_response, prepare_object_response, Response
from ..services.request import (
make_headers,
make_url,
send_get_request,
send_post_request,
)
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
Expand All @@ -21,67 +36,71 @@ class CustomerClient(
FindCommandMixin[CustomerResponse],
BaseClient,
):
API_RESOURCE: ClassVar[str] = 'customers'
API_RESOURCE: ClassVar[str] = "customers"
RESPONSE_MODEL: ClassVar[Type[CustomerResponse]] = CustomerResponse
ROOT_NAME: ClassVar[str] = 'customer'
ROOT_NAME: ClassVar[str] = "customer"

def current_usage(self, resource_id: str, external_subscription_id: str) -> CustomerUsageResponse:
api_response: Response = send_get_request(
url=make_url(
origin=self.base_url,
path_parts=(self.API_RESOURCE, resource_id, 'current_usage'),
path_parts=(self.API_RESOURCE, resource_id, "current_usage"),
query_pairs={
'external_subscription_id': external_subscription_id,
"external_subscription_id": external_subscription_id,
},
),
headers=make_headers(api_key=self.api_key),
)

return prepare_object_response(
response_model=CustomerUsageResponse,
data=get_response_data(response=api_response, key='customer_usage'),
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]:
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'),
path_parts=(self.API_RESOURCE, resource_id, "past_usage"),
query_pairs={
'external_subscription_id': external_subscription_id,
"external_subscription_id": external_subscription_id,
**options,
},
),
headers=make_headers(api_key=self.api_key),
)

return prepare_index_response(
api_resource='usage_periods',
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(
origin=self.base_url,
path_parts=(self.API_RESOURCE, resource_id, 'portal_url'),
path_parts=(self.API_RESOURCE, resource_id, "portal_url"),
),
headers=make_headers(api_key=self.api_key),
)

response_data = get_response_data(response=api_response, key=self.ROOT_NAME)
return response_data.get('portal_url', '') if isinstance(response_data, Mapping) else ''
return response_data.get("portal_url", "") if isinstance(response_data, Mapping) else ""

def checkout_url(self, resource_id: str) -> str:
api_response: Response = send_post_request(
url=make_url(
origin=self.base_url,
path_parts=(self.API_RESOURCE, resource_id, 'checkout_url'),
path_parts=(self.API_RESOURCE, resource_id, "checkout_url"),
),
headers=make_headers(api_key=self.api_key),
)

response_data = get_response_data(response=api_response, key=self.ROOT_NAME)
return response_data.get('checkout_url', '') if isinstance(response_data, Mapping) else ''
return response_data.get("checkout_url", "") if isinstance(response_data, Mapping) else ""
Loading
Loading