lago-python-client 1.46.0__tar.gz → 1.48.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/PKG-INFO +1 -1
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/client.py +4 -1
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/clients.py +26 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/__init__.py +6 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer_projected_usage.py +7 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer_usage.py +12 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/fee.py +2 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/organization.py +1 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/subscription.py +2 -0
- lago_python_client-1.48.0/lago_python_client/observability.py +67 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/rate_limit.py +110 -1
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/request.py +10 -1
- lago_python_client-1.48.0/lago_python_client/version.py +1 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/PKG-INFO +1 -1
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/SOURCES.txt +2 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_client.py +44 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_fee_client.py +2 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_plan_client.py +27 -2
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_rate_limit.py +283 -1
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_subscription_client.py +5 -0
- lago_python_client-1.46.0/lago_python_client/version.py +0 -1
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/LICENSE +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/README.md +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/activity_logs/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/activity_logs/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/add_ons/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/add_ons/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/api_logs/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/api_logs/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/base_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/base_model.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billable_metrics/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billable_metrics/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billing_entities/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billing_entities/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/coupons/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/coupons/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/credit_notes/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/credit_notes/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/applied_coupons_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/credit_notes_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/invoices_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/payment_methods_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/payment_requests_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/payments_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/subscriptions_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallet_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets/alert_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets/metadata_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/events/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/events/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/exceptions.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/fees/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/fees/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/functools_ext.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/gross_revenues/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/gross_revenues/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoice_collections/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoice_collections/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoiced_usages/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoiced_usages/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoices/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoices/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/mixins.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/activity_log.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/add_on.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/alert.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/api_log.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/applied_coupon.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/billable_metric.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/billing_entity.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/billing_period.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/charge.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/coupon.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/credit.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/credit_note.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/error_detail.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/event.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/fixed_charge.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/gross_revenue.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice_collection.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice_custom_section.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice_item.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoiced_usage.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/lifetime_usage.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/minimum_commitment.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/mrr.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/overdue_balance.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment_method.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment_receipt.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment_request.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/plan.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/tax.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/usage.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/usage_threshold.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/wallet.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/wallet_transaction.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/webhook_endpoint.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/mrrs/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/mrrs/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/organizations/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/organizations/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/overdue_balances/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/overdue_balances/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_receipts/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_receipts/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_requests/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_requests/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payments/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payments/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/charges_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/fixed_charges_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/py.typed +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/json.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/response.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/charges_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/fixed_charges_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/taxes/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/taxes/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/usages/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/usages/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/wallets/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/wallets/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhook_endpoints/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhook_endpoints/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhooks/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhooks/clients.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/dependency_links.txt +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/requires.txt +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/top_level.txt +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/pyproject.toml +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/setup.cfg +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_activity_logs_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_add_on_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_api_logs_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_applied_coupon_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_billable_metric_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_billing_entity_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_coupon_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_credit_note_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_applied_coupons_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_credit_notes_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_invoices_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_payment_methods_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_payment_requests_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_payments_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_sbscriptions_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallet_alert_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallet_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallet_metadata_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallets_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_event_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_functools_ext.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_gross_revenue_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_invoice_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_invoice_collection_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_invoiced_usage_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_json_services.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_mrr_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_organization_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_overdue_balance_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_payment_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_payment_receipt_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_payment_request_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_request_services.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_response_services.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_tax_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_usage_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_wallet_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_wallet_transaction_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_webhook_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_webhook_endpoint_client.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/utils/__init__.py +0 -0
- {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/utils/mixin.py +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from typing import Optional
|
|
1
2
|
from urllib.parse import urljoin
|
|
2
3
|
|
|
3
4
|
from .activity_logs.clients import ActivityLogClient
|
|
@@ -32,7 +33,7 @@ from .payments.clients import PaymentClient
|
|
|
32
33
|
from .plans.charges_client import PlanChargesClient
|
|
33
34
|
from .plans.clients import PlanClient
|
|
34
35
|
from .plans.fixed_charges_client import PlanFixedChargesClient
|
|
35
|
-
from .services.rate_limit import RateLimitRetryConfig
|
|
36
|
+
from .services.rate_limit import RateLimitCallback, RateLimitRetryConfig
|
|
36
37
|
from .subscriptions.charges_client import SubscriptionChargesClient
|
|
37
38
|
from .subscriptions.clients import SubscriptionClient
|
|
38
39
|
from .subscriptions.fixed_charges_client import SubscriptionFixedChargesClient
|
|
@@ -61,6 +62,7 @@ class Client:
|
|
|
61
62
|
ingest_api_url: str = "",
|
|
62
63
|
max_retries: int = 3,
|
|
63
64
|
retry_on_rate_limit: bool = True,
|
|
65
|
+
on_rate_limit_info: Optional[RateLimitCallback] = None,
|
|
64
66
|
) -> None:
|
|
65
67
|
self.api_key: str = api_key
|
|
66
68
|
self.api_url: str = api_url
|
|
@@ -69,6 +71,7 @@ class Client:
|
|
|
69
71
|
self.rate_limit_retry_config: RateLimitRetryConfig = RateLimitRetryConfig(
|
|
70
72
|
max_retries=max_retries,
|
|
71
73
|
retry_on_rate_limit=retry_on_rate_limit,
|
|
74
|
+
on_rate_limit_info=on_rate_limit_info,
|
|
72
75
|
)
|
|
73
76
|
|
|
74
77
|
@property
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/clients.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import warnings
|
|
2
3
|
from collections.abc import Mapping
|
|
3
4
|
from typing import Any, ClassVar, Optional, Type, Union
|
|
4
5
|
|
|
@@ -46,17 +47,42 @@ class CustomerClient(
|
|
|
46
47
|
filter_by_charge_id: Optional[str] = None,
|
|
47
48
|
filter_by_charge_code: Optional[str] = None,
|
|
48
49
|
filter_by_group: Optional[dict] = None,
|
|
50
|
+
filter_by_presentation: Optional[Union[str, list[str]]] = None,
|
|
49
51
|
full_usage: Optional[bool] = None,
|
|
52
|
+
charge_id: Optional[str] = None,
|
|
53
|
+
charge_code: Optional[str] = None,
|
|
54
|
+
billable_metric_code: Optional[str] = None,
|
|
55
|
+
group: Optional[dict] = None,
|
|
50
56
|
) -> CustomerUsageResponse:
|
|
51
57
|
query_params: dict[str, Union[str, bool]] = {"external_subscription_id": external_subscription_id}
|
|
52
58
|
if apply_taxes is not None:
|
|
53
59
|
query_params["apply_taxes"] = apply_taxes
|
|
60
|
+
if charge_id is not None:
|
|
61
|
+
query_params["charge_id"] = charge_id
|
|
62
|
+
if charge_code is not None:
|
|
63
|
+
query_params["charge_code"] = charge_code
|
|
64
|
+
if billable_metric_code is not None:
|
|
65
|
+
query_params["billable_metric_code"] = billable_metric_code
|
|
66
|
+
if group is not None:
|
|
67
|
+
for k, v in group.items():
|
|
68
|
+
query_params[f"group[{k}]"] = v
|
|
54
69
|
if filter_by_charge_id is not None:
|
|
70
|
+
warnings.warn("filter_by_charge_id is deprecated, use charge_id instead", DeprecationWarning, stacklevel=2)
|
|
55
71
|
query_params["filter_by_charge_id"] = filter_by_charge_id
|
|
56
72
|
if filter_by_charge_code is not None:
|
|
73
|
+
warnings.warn(
|
|
74
|
+
"filter_by_charge_code is deprecated, use charge_code instead", DeprecationWarning, stacklevel=2
|
|
75
|
+
)
|
|
57
76
|
query_params["filter_by_charge_code"] = filter_by_charge_code
|
|
58
77
|
if filter_by_group is not None:
|
|
78
|
+
warnings.warn("filter_by_group is deprecated, use group instead", DeprecationWarning, stacklevel=2)
|
|
59
79
|
query_params["filter_by_group"] = json.dumps(filter_by_group)
|
|
80
|
+
if filter_by_presentation is not None:
|
|
81
|
+
query_params["filter_by_presentation"] = (
|
|
82
|
+
filter_by_presentation
|
|
83
|
+
if isinstance(filter_by_presentation, str)
|
|
84
|
+
else json.dumps(filter_by_presentation)
|
|
85
|
+
)
|
|
60
86
|
if full_usage is not None:
|
|
61
87
|
query_params["full_usage"] = str(full_usage).lower()
|
|
62
88
|
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/__init__.py
RENAMED
|
@@ -138,6 +138,12 @@ from .customer_usage import (
|
|
|
138
138
|
from .customer_usage import (
|
|
139
139
|
Metric as Metric,
|
|
140
140
|
)
|
|
141
|
+
from .customer_usage import (
|
|
142
|
+
PresentationBreakdown as PresentationBreakdown,
|
|
143
|
+
)
|
|
144
|
+
from .customer_usage import (
|
|
145
|
+
PresentationBreakdowns as PresentationBreakdowns,
|
|
146
|
+
)
|
|
141
147
|
from .event import BatchEvent as BatchEvent
|
|
142
148
|
from .event import Event as Event
|
|
143
149
|
from .fee import Fee as Fee
|
|
@@ -3,6 +3,7 @@ from typing import Dict, List, Optional
|
|
|
3
3
|
from lago_python_client.base_model import BaseModel
|
|
4
4
|
|
|
5
5
|
from ..base_model import BaseResponseModel
|
|
6
|
+
from .customer_usage import PresentationBreakdowns
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class Metric(BaseModel):
|
|
@@ -27,6 +28,8 @@ class ProjectedChargeFilterUsage(BaseModel):
|
|
|
27
28
|
projected_amount_cents: int
|
|
28
29
|
events_count: int
|
|
29
30
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
31
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
32
|
+
projected_presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class ChargeObject(BaseModel):
|
|
@@ -44,6 +47,8 @@ class ProjectedGroupedUsage(BaseModel):
|
|
|
44
47
|
grouped_by: Dict[str, Optional[str]]
|
|
45
48
|
filters: List[ProjectedChargeFilterUsage]
|
|
46
49
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
50
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
51
|
+
projected_presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
class ProjectedChargeUsage(BaseModel):
|
|
@@ -58,6 +63,8 @@ class ProjectedChargeUsage(BaseModel):
|
|
|
58
63
|
filters: List[ProjectedChargeFilterUsage]
|
|
59
64
|
grouped_usage: Optional[List[ProjectedGroupedUsage]]
|
|
60
65
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
66
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
67
|
+
projected_presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
61
68
|
|
|
62
69
|
|
|
63
70
|
class CustomerProjectedUsageResponse(BaseResponseModel):
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer_usage.py
RENAMED
|
@@ -18,6 +18,15 @@ class PricingUnitDetails(BaseModel):
|
|
|
18
18
|
conversion_rate: float
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
class PresentationBreakdown(BaseModel):
|
|
22
|
+
presentation_by: Dict[str, str]
|
|
23
|
+
units: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PresentationBreakdowns(BaseModel):
|
|
27
|
+
__root__: List[PresentationBreakdown]
|
|
28
|
+
|
|
29
|
+
|
|
21
30
|
class ChargeFilterUsage(BaseModel):
|
|
22
31
|
invoice_display_name: Optional[str]
|
|
23
32
|
values: Optional[Dict[str, List[str]]]
|
|
@@ -26,6 +35,7 @@ class ChargeFilterUsage(BaseModel):
|
|
|
26
35
|
amount_cents: int
|
|
27
36
|
events_count: int
|
|
28
37
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
38
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
29
39
|
|
|
30
40
|
|
|
31
41
|
class ChargeObject(BaseModel):
|
|
@@ -42,6 +52,7 @@ class GroupedUsage(BaseModel):
|
|
|
42
52
|
grouped_by: Dict[str, Optional[str]]
|
|
43
53
|
filters: List[ChargeFilterUsage]
|
|
44
54
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
55
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
45
56
|
|
|
46
57
|
|
|
47
58
|
class ChargeUsage(BaseModel):
|
|
@@ -55,6 +66,7 @@ class ChargeUsage(BaseModel):
|
|
|
55
66
|
filters: List[ChargeFilterUsage]
|
|
56
67
|
grouped_usage: Optional[List[GroupedUsage]]
|
|
57
68
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
69
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
58
70
|
|
|
59
71
|
|
|
60
72
|
class CustomerUsageResponse(BaseResponseModel):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any, Dict, List, Optional
|
|
2
2
|
|
|
3
3
|
from ..base_model import BaseModel, BaseResponseModel
|
|
4
|
+
from .customer_usage import PresentationBreakdowns
|
|
4
5
|
from .invoice_item import InvoiceItemResponse
|
|
5
6
|
|
|
6
7
|
|
|
@@ -75,6 +76,7 @@ class FeeResponse(BaseResponseModel):
|
|
|
75
76
|
to_date: Optional[str]
|
|
76
77
|
amount_details: Optional[Dict[str, Any]]
|
|
77
78
|
pricing_unit_details: Optional[PricingUnitDetails]
|
|
79
|
+
presentation_breakdowns: Optional[PresentationBreakdowns]
|
|
78
80
|
billing_entity_code: Optional[str]
|
|
79
81
|
|
|
80
82
|
item: Optional[InvoiceItemResponse]
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/subscription.py
RENAMED
|
@@ -19,6 +19,7 @@ class Subscription(BaseModel):
|
|
|
19
19
|
plan_overrides: Optional[PlanOverrides]
|
|
20
20
|
payment_method: Optional[PaymentMethod]
|
|
21
21
|
invoice_custom_section: Optional[InvoiceCustomSectionInput]
|
|
22
|
+
consolidate_invoice: Optional[bool]
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class Subscriptions(BaseModel):
|
|
@@ -55,6 +56,7 @@ class SubscriptionResponse(BaseResponseModel):
|
|
|
55
56
|
on_termination_invoice: Optional[str]
|
|
56
57
|
payment_method: Optional[PaymentMethod]
|
|
57
58
|
applied_invoice_custom_sections: Optional[AppliedInvoiceCustomSections]
|
|
59
|
+
consolidate_invoice: Optional[bool]
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
class SubscriptionsResponse(BaseResponseModel):
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Optional observability helpers for Lago rate limits.
|
|
3
|
+
|
|
4
|
+
Provides ready-to-use ``on_rate_limit_info`` callbacks so callers can opt into
|
|
5
|
+
rate limit observability without writing one themselves.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
from lago_python_client import Client
|
|
9
|
+
from lago_python_client.observability import LoggingRateLimitObserver
|
|
10
|
+
|
|
11
|
+
client = Client(
|
|
12
|
+
api_key="...",
|
|
13
|
+
on_rate_limit_info=LoggingRateLimitObserver(),
|
|
14
|
+
)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from typing import Optional, Sequence
|
|
19
|
+
|
|
20
|
+
from .services.rate_limit import RateLimitInfo
|
|
21
|
+
|
|
22
|
+
DEFAULT_THRESHOLDS: Sequence[float] = (0.80, 0.90, 0.95)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class LoggingRateLimitObserver:
|
|
26
|
+
"""
|
|
27
|
+
Emits a log line each time rate limit usage crosses a configured threshold.
|
|
28
|
+
|
|
29
|
+
Stateless across calls: every response that lands above any threshold logs.
|
|
30
|
+
If you want one-shot crossings, wrap it with your own state.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
thresholds: Usage fractions (0.0 - 1.0) that should produce a log line.
|
|
34
|
+
Defaults to 80%, 90%, 95%.
|
|
35
|
+
logger: Logger to emit on. Defaults to ``lago_python_client.rate_limit``.
|
|
36
|
+
level: Log level used when a threshold is crossed. Defaults to WARNING.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
thresholds: Sequence[float] = DEFAULT_THRESHOLDS,
|
|
42
|
+
logger: Optional[logging.Logger] = None,
|
|
43
|
+
level: int = logging.WARNING,
|
|
44
|
+
) -> None:
|
|
45
|
+
# Sort descending so we report the highest matching threshold first.
|
|
46
|
+
self.thresholds = tuple(sorted(thresholds, reverse=True))
|
|
47
|
+
self.logger = logger or logging.getLogger("lago_python_client.rate_limit")
|
|
48
|
+
self.level = level
|
|
49
|
+
|
|
50
|
+
def __call__(self, info: RateLimitInfo) -> None:
|
|
51
|
+
pct = info.usage_pct
|
|
52
|
+
if pct is None:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
for threshold in self.thresholds:
|
|
56
|
+
if pct >= threshold:
|
|
57
|
+
self.logger.log(
|
|
58
|
+
self.level,
|
|
59
|
+
"Lago rate limit at %.0f%% (limit=%s, remaining=%s, reset=%ss, %s %s)",
|
|
60
|
+
pct * 100,
|
|
61
|
+
info.limit,
|
|
62
|
+
info.remaining,
|
|
63
|
+
info.reset,
|
|
64
|
+
info.method,
|
|
65
|
+
info.url,
|
|
66
|
+
)
|
|
67
|
+
return
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/rate_limit.py
RENAMED
|
@@ -1,11 +1,87 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import time
|
|
2
|
-
from
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Callable, Optional
|
|
3
5
|
|
|
4
6
|
if TYPE_CHECKING:
|
|
5
7
|
from httpx import Response
|
|
6
8
|
|
|
7
9
|
from ..exceptions import LagoRateLimitError
|
|
8
10
|
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class RateLimitInfo:
|
|
16
|
+
"""
|
|
17
|
+
Parsed rate limit headers from a Lago API response.
|
|
18
|
+
|
|
19
|
+
Provided to ``on_rate_limit_info`` callbacks after every successful request
|
|
20
|
+
so callers can build their own observability (metrics, logs, alerts).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
limit: Optional[int]
|
|
24
|
+
remaining: Optional[int]
|
|
25
|
+
reset: Optional[int]
|
|
26
|
+
url: str
|
|
27
|
+
method: str
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def usage_pct(self) -> Optional[float]:
|
|
31
|
+
"""
|
|
32
|
+
Fraction of the rate limit currently used, between 0.0 and 1.0.
|
|
33
|
+
|
|
34
|
+
Returns None when the headers aren't usable (missing, zero limit, etc.).
|
|
35
|
+
"""
|
|
36
|
+
if self.limit is None or self.remaining is None or self.limit <= 0:
|
|
37
|
+
return None
|
|
38
|
+
return 1.0 - (self.remaining / self.limit)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
RateLimitCallback = Callable[[RateLimitInfo], None]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def parse_rate_limit_info(
|
|
45
|
+
response: "Response",
|
|
46
|
+
method: str,
|
|
47
|
+
url: str,
|
|
48
|
+
) -> Optional[RateLimitInfo]:
|
|
49
|
+
"""
|
|
50
|
+
Parse ``x-ratelimit-*`` headers from a response into a RateLimitInfo.
|
|
51
|
+
|
|
52
|
+
Returns ``None`` when no rate limit headers are present (for example, on a
|
|
53
|
+
self-hosted Lago instance that doesn't enforce limits) so callers can skip
|
|
54
|
+
emission entirely.
|
|
55
|
+
"""
|
|
56
|
+
headers = response.headers
|
|
57
|
+
limit: Optional[int] = None
|
|
58
|
+
remaining: Optional[int] = None
|
|
59
|
+
reset: Optional[int] = None
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
limit = int(headers["x-ratelimit-limit"])
|
|
63
|
+
except (KeyError, ValueError, TypeError):
|
|
64
|
+
pass
|
|
65
|
+
try:
|
|
66
|
+
remaining = int(headers["x-ratelimit-remaining"])
|
|
67
|
+
except (KeyError, ValueError, TypeError):
|
|
68
|
+
pass
|
|
69
|
+
try:
|
|
70
|
+
reset = int(headers["x-ratelimit-reset"])
|
|
71
|
+
except (KeyError, ValueError, TypeError):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
if limit is None and remaining is None and reset is None:
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
return RateLimitInfo(
|
|
78
|
+
limit=limit,
|
|
79
|
+
remaining=remaining,
|
|
80
|
+
reset=reset,
|
|
81
|
+
url=url,
|
|
82
|
+
method=method,
|
|
83
|
+
)
|
|
84
|
+
|
|
9
85
|
|
|
10
86
|
class RateLimitRetryConfig:
|
|
11
87
|
"""Configuration for rate limit retry behavior."""
|
|
@@ -17,6 +93,7 @@ class RateLimitRetryConfig:
|
|
|
17
93
|
base_backoff_seconds: float = 1.0,
|
|
18
94
|
backoff_multiplier: float = 2.0,
|
|
19
95
|
max_retry_delay: float = 20.0,
|
|
96
|
+
on_rate_limit_info: Optional[RateLimitCallback] = None,
|
|
20
97
|
):
|
|
21
98
|
"""
|
|
22
99
|
Initialize rate limit retry configuration.
|
|
@@ -27,12 +104,18 @@ class RateLimitRetryConfig:
|
|
|
27
104
|
base_backoff_seconds: Initial backoff duration in seconds for exponential backoff.
|
|
28
105
|
backoff_multiplier: Multiplier for exponential backoff between retries.
|
|
29
106
|
max_retry_delay: Maximum delay in seconds before a retry (default: 20).
|
|
107
|
+
on_rate_limit_info: Optional callback invoked after every successful
|
|
108
|
+
response with parsed rate limit headers. Use this to build
|
|
109
|
+
observability around the rate limit (warn at 80%, emit metrics,
|
|
110
|
+
etc.). Exceptions raised from the callback are logged and
|
|
111
|
+
swallowed so they never break the request flow.
|
|
30
112
|
"""
|
|
31
113
|
self.max_retries = max_retries
|
|
32
114
|
self.retry_on_rate_limit = retry_on_rate_limit
|
|
33
115
|
self.base_backoff_seconds = base_backoff_seconds
|
|
34
116
|
self.backoff_multiplier = backoff_multiplier
|
|
35
117
|
self.max_retry_delay = max_retry_delay
|
|
118
|
+
self.on_rate_limit_info = on_rate_limit_info
|
|
36
119
|
|
|
37
120
|
def calculate_backoff(self, retry_attempt: int) -> float:
|
|
38
121
|
"""
|
|
@@ -47,6 +130,32 @@ class RateLimitRetryConfig:
|
|
|
47
130
|
return self.base_backoff_seconds * (self.backoff_multiplier**retry_attempt)
|
|
48
131
|
|
|
49
132
|
|
|
133
|
+
def emit_rate_limit_info(
|
|
134
|
+
response: "Response",
|
|
135
|
+
config: "RateLimitRetryConfig",
|
|
136
|
+
method: str,
|
|
137
|
+
url: str,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Invoke the ``on_rate_limit_info`` callback if configured.
|
|
141
|
+
|
|
142
|
+
Parses rate limit headers off the response and hands them to the user's
|
|
143
|
+
callback. Any exception raised from the callback is logged and swallowed so
|
|
144
|
+
a buggy observer never breaks the underlying request.
|
|
145
|
+
"""
|
|
146
|
+
if config.on_rate_limit_info is None:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
info = parse_rate_limit_info(response, method=method, url=url)
|
|
150
|
+
if info is None:
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
config.on_rate_limit_info(info)
|
|
155
|
+
except Exception:
|
|
156
|
+
logger.exception("on_rate_limit_info callback raised; suppressing")
|
|
157
|
+
|
|
158
|
+
|
|
50
159
|
def handle_rate_limit_response(
|
|
51
160
|
response: "Response",
|
|
52
161
|
config: RateLimitRetryConfig,
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/request.py
RENAMED
|
@@ -12,6 +12,7 @@ import httpx
|
|
|
12
12
|
from ..version import LAGO_VERSION
|
|
13
13
|
from .rate_limit import (
|
|
14
14
|
RateLimitRetryConfig,
|
|
15
|
+
emit_rate_limit_info,
|
|
15
16
|
handle_rate_limit_response,
|
|
16
17
|
is_rate_limit_response,
|
|
17
18
|
wait_for_retry,
|
|
@@ -92,12 +93,20 @@ def _create_retry_wrapper(
|
|
|
92
93
|
if rate_limit_retry_config is None:
|
|
93
94
|
rate_limit_retry_config = RateLimitRetryConfig()
|
|
94
95
|
|
|
96
|
+
method_name = http_method.__name__.upper()
|
|
97
|
+
|
|
95
98
|
retry_attempt = 0
|
|
96
99
|
while True:
|
|
97
100
|
response = http_method(url, **kwargs)
|
|
98
101
|
|
|
99
|
-
# If not rate limited, return the response
|
|
102
|
+
# If not rate limited, emit observability info and return the response
|
|
100
103
|
if not is_rate_limit_response(response):
|
|
104
|
+
emit_rate_limit_info(
|
|
105
|
+
response,
|
|
106
|
+
rate_limit_retry_config,
|
|
107
|
+
method=method_name,
|
|
108
|
+
url=url,
|
|
109
|
+
)
|
|
101
110
|
return response
|
|
102
111
|
|
|
103
112
|
# Handle rate limit - may raise LagoRateLimitError or return wait duration
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
LAGO_VERSION = "1.48.0"
|
{lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/SOURCES.txt
RENAMED
|
@@ -9,6 +9,7 @@ setup.cfg
|
|
|
9
9
|
./lago_python_client/exceptions.py
|
|
10
10
|
./lago_python_client/functools_ext.py
|
|
11
11
|
./lago_python_client/mixins.py
|
|
12
|
+
./lago_python_client/observability.py
|
|
12
13
|
./lago_python_client/py.typed
|
|
13
14
|
./lago_python_client/version.py
|
|
14
15
|
./lago_python_client/activity_logs/__init__.py
|
|
@@ -185,6 +186,7 @@ lago_python_client/client.py
|
|
|
185
186
|
lago_python_client/exceptions.py
|
|
186
187
|
lago_python_client/functools_ext.py
|
|
187
188
|
lago_python_client/mixins.py
|
|
189
|
+
lago_python_client/observability.py
|
|
188
190
|
lago_python_client/py.typed
|
|
189
191
|
lago_python_client/version.py
|
|
190
192
|
lago_python_client.egg-info/PKG-INFO
|
|
@@ -134,6 +134,11 @@ def test_valid_current_usage(httpx_mock: HTTPXMock):
|
|
|
134
134
|
assert response.charges_usage[0].units == "1.0"
|
|
135
135
|
assert len(response.charges_usage[0].filters) == 1
|
|
136
136
|
assert response.charges_usage[0].filters[0].values["country"] == ["france"]
|
|
137
|
+
charge_usage = response.charges_usage[0]
|
|
138
|
+
assert charge_usage.presentation_breakdowns.__root__[0].presentation_by["team"] == "engineering"
|
|
139
|
+
assert charge_usage.presentation_breakdowns.__root__[0].units == "3.0"
|
|
140
|
+
assert charge_usage.filters[0].presentation_breakdowns.__root__[0].units == "2.0"
|
|
141
|
+
assert charge_usage.grouped_usage[0].presentation_breakdowns.__root__[0].units == "1.0"
|
|
137
142
|
|
|
138
143
|
|
|
139
144
|
def test_valid_projected_usage(httpx_mock: HTTPXMock):
|
|
@@ -157,6 +162,11 @@ def test_valid_projected_usage(httpx_mock: HTTPXMock):
|
|
|
157
162
|
assert response.charges_usage[0].charge.charge_model == "graduated"
|
|
158
163
|
assert response.charges_usage[0].charge.invoice_display_name == "add_on_invoice_display_name"
|
|
159
164
|
assert response.charges_usage[0].billable_metric.lago_id == "99a6094e-199b-4101-896a-54e927ce7bd7"
|
|
165
|
+
charge_usage = response.charges_usage[0]
|
|
166
|
+
assert charge_usage.presentation_breakdowns.__root__[0].units == "3.0"
|
|
167
|
+
assert charge_usage.projected_presentation_breakdowns.__root__[0].units == "6.0"
|
|
168
|
+
assert charge_usage.filters[0].projected_presentation_breakdowns.__root__[0].units == "4.0"
|
|
169
|
+
assert charge_usage.grouped_usage[0].projected_presentation_breakdowns.__root__[0].units == "2.0"
|
|
160
170
|
|
|
161
171
|
|
|
162
172
|
def test_valid_current_usage_without_taxes(httpx_mock: HTTPXMock):
|
|
@@ -204,6 +214,38 @@ def test_valid_current_usage_with_filters(httpx_mock: HTTPXMock):
|
|
|
204
214
|
assert len(response.charges_usage) == 1
|
|
205
215
|
|
|
206
216
|
|
|
217
|
+
def test_valid_current_usage_with_new_filters(httpx_mock: HTTPXMock):
|
|
218
|
+
client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
|
|
219
|
+
charge_id = "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
220
|
+
|
|
221
|
+
httpx_mock.add_response(
|
|
222
|
+
method="GET",
|
|
223
|
+
url=(
|
|
224
|
+
"https://api.getlago.com/api/v1/customers/external_customer_id/current_usage"
|
|
225
|
+
f"?external_subscription_id=123&charge_id={charge_id}"
|
|
226
|
+
"&charge_code=storage"
|
|
227
|
+
"&billable_metric_code=storage"
|
|
228
|
+
"&group%5Bcloud%5D=aws"
|
|
229
|
+
"&filter_by_presentation=%5B%22engineering%22%2C+%22operations%22%5D"
|
|
230
|
+
"&full_usage=true"
|
|
231
|
+
),
|
|
232
|
+
content=mock_response("customer_usage"),
|
|
233
|
+
)
|
|
234
|
+
response = client.customers.current_usage(
|
|
235
|
+
"external_customer_id",
|
|
236
|
+
"123",
|
|
237
|
+
charge_id=charge_id,
|
|
238
|
+
charge_code="storage",
|
|
239
|
+
billable_metric_code="storage",
|
|
240
|
+
group={"cloud": "aws"},
|
|
241
|
+
filter_by_presentation=["engineering", "operations"],
|
|
242
|
+
full_usage=True,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
assert response.from_datetime == "2022-07-01T00:00:00Z"
|
|
246
|
+
assert len(response.charges_usage) == 1
|
|
247
|
+
|
|
248
|
+
|
|
207
249
|
def test_invalid_current_usage(httpx_mock: HTTPXMock):
|
|
208
250
|
client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
|
|
209
251
|
|
|
@@ -233,6 +275,8 @@ def test_valid_past_usage(httpx_mock: HTTPXMock):
|
|
|
233
275
|
assert len(response["usage_periods"][0].charges_usage) == 1
|
|
234
276
|
assert response["usage_periods"][0].charges_usage[0].units == "1.0"
|
|
235
277
|
assert len(response["usage_periods"][0].charges_usage[0].filters) == 1
|
|
278
|
+
past_charge_usage = response["usage_periods"][0].charges_usage[0]
|
|
279
|
+
assert past_charge_usage.presentation_breakdowns.__root__[0].units == "3.0"
|
|
236
280
|
|
|
237
281
|
|
|
238
282
|
def test_invalid_past_usage(httpx_mock: HTTPXMock):
|
|
@@ -29,6 +29,8 @@ def test_valid_find_fee_request(httpx_mock: HTTPXMock):
|
|
|
29
29
|
assert response.lago_id == identifier
|
|
30
30
|
assert response.invoice_display_name == fee_invoice_display_name
|
|
31
31
|
assert response.item.invoice_display_name == charge_invoice_display_name
|
|
32
|
+
assert response.presentation_breakdowns.__root__[0].presentation_by["team"] == "engineering"
|
|
33
|
+
assert response.presentation_breakdowns.__root__[0].units == "10.0"
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
def test_fee_response_with_fixed_charge_id():
|
|
@@ -25,7 +25,14 @@ def plan_object():
|
|
|
25
25
|
id=None,
|
|
26
26
|
invoice_display_name=None,
|
|
27
27
|
regroup_paid_fees=None,
|
|
28
|
-
properties=
|
|
28
|
+
properties={
|
|
29
|
+
"presentation_group_keys": [
|
|
30
|
+
{
|
|
31
|
+
"value": "region",
|
|
32
|
+
"options": {"display_in_invoice": True},
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
29
36
|
tax_codes=None,
|
|
30
37
|
billable_metric_id="id",
|
|
31
38
|
charge_model="standard",
|
|
@@ -194,6 +201,8 @@ def test_valid_create_plan_request(httpx_mock: HTTPXMock):
|
|
|
194
201
|
assert response.code == "plan_code"
|
|
195
202
|
assert response.invoice_display_name == "test plan 1"
|
|
196
203
|
assert response.charges.__root__[0].invoice_display_name == "Setup"
|
|
204
|
+
charge = response.charges.__root__[0]
|
|
205
|
+
assert charge.properties["presentation_group_keys"][0]["value"] == "region"
|
|
197
206
|
assert response.minimum_commitment.invoice_display_name == "Minimum commitment (C1)"
|
|
198
207
|
assert response.usage_thresholds.__root__[0].threshold_display_name == "Threshold 1"
|
|
199
208
|
assert response.metadata == {"key1": "value1", "key2": None}
|
|
@@ -276,6 +285,8 @@ def test_valid_find_plan_request(httpx_mock: HTTPXMock):
|
|
|
276
285
|
assert response.invoice_display_name == "test plan 1"
|
|
277
286
|
assert response.charges.__root__[0].charge_model == "standard"
|
|
278
287
|
assert response.charges.__root__[0].min_amount_cents == 0
|
|
288
|
+
charge = response.charges.__root__[0]
|
|
289
|
+
assert charge.properties["presentation_group_keys"][0]["value"] == "region"
|
|
279
290
|
assert response.minimum_commitment.amount_cents == 1000
|
|
280
291
|
|
|
281
292
|
|
|
@@ -338,6 +349,8 @@ def test_valid_find_all_plan_request(httpx_mock: HTTPXMock):
|
|
|
338
349
|
assert response["plans"][0].invoice_display_name == "test plan 1"
|
|
339
350
|
assert response["plans"][0].minimum_commitment.invoice_display_name == "Minimum commitment (C2)"
|
|
340
351
|
assert response["plans"][0].charges.__root__[0].lago_id == "51c1e851-5be6-4343-a0ee-39a81d8b4ee1"
|
|
352
|
+
plan_charge = response["plans"][0].charges.__root__[0]
|
|
353
|
+
assert plan_charge.properties["presentation_group_keys"][0]["value"] == "region"
|
|
341
354
|
assert response["plans"][0].charges.__root__[0].filters.__root__[0].properties["amount"] == "0.22"
|
|
342
355
|
assert response["plans"][0].charges.__root__[0].filters.__root__[0].invoice_display_name == "Europe"
|
|
343
356
|
assert response["meta"]["current_page"] == 1
|
|
@@ -506,6 +519,9 @@ def test_valid_find_charge_request(httpx_mock: HTTPXMock):
|
|
|
506
519
|
assert response.code == "charge_code"
|
|
507
520
|
assert response.charge_model == "standard"
|
|
508
521
|
assert response.invoice_display_name == "Setup"
|
|
522
|
+
presentation_group_key = response.properties["presentation_group_keys"][0]
|
|
523
|
+
assert presentation_group_key["value"] == "region"
|
|
524
|
+
assert presentation_group_key["options"]["display_in_invoice"] is True
|
|
509
525
|
|
|
510
526
|
|
|
511
527
|
def test_valid_create_charge_request(httpx_mock: HTTPXMock):
|
|
@@ -522,12 +538,21 @@ def test_valid_create_charge_request(httpx_mock: HTTPXMock):
|
|
|
522
538
|
charge_model="standard",
|
|
523
539
|
pay_in_advance=True,
|
|
524
540
|
invoiceable=True,
|
|
525
|
-
properties={
|
|
541
|
+
properties={
|
|
542
|
+
"amount": "0.22",
|
|
543
|
+
"presentation_group_keys": [
|
|
544
|
+
{
|
|
545
|
+
"value": "region",
|
|
546
|
+
"options": {"display_in_invoice": True},
|
|
547
|
+
}
|
|
548
|
+
],
|
|
549
|
+
},
|
|
526
550
|
)
|
|
527
551
|
response = client.plan_charges.create(plan_code, charge)
|
|
528
552
|
|
|
529
553
|
assert response.lago_id == "51c1e851-5be6-4343-a0ee-39a81d8b4ee1"
|
|
530
554
|
assert response.charge_model == "standard"
|
|
555
|
+
assert charge.dict()["properties"]["presentation_group_keys"][0]["value"] == "region"
|
|
531
556
|
|
|
532
557
|
|
|
533
558
|
def test_valid_update_charge_request(httpx_mock: HTTPXMock):
|