lago-python-client 1.45.0__tar.gz → 1.46.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.45.0 → lago_python_client-1.46.0}/PKG-INFO +1 -1
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/base_client.py +10 -2
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/client.py +59 -42
- lago_python_client-1.46.0/lago_python_client/exceptions.py +69 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/mixins.py +18 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/fee.py +2 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice_custom_section.py +11 -10
- lago_python_client-1.46.0/lago_python_client/services/rate_limit.py +126 -0
- lago_python_client-1.46.0/lago_python_client/services/request.py +122 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/services/response.py +10 -1
- lago_python_client-1.46.0/lago_python_client/version.py +1 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/PKG-INFO +1 -1
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/SOURCES.txt +4 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallet_client.py +15 -0
- lago_python_client-1.46.0/tests/test_rate_limit.py +320 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_request_services.py +8 -10
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_subscription_client.py +3 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_wallet_client.py +15 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_wallet_transaction_client.py +4 -0
- lago_python_client-1.45.0/lago_python_client/exceptions.py +0 -26
- lago_python_client-1.45.0/lago_python_client/services/request.py +0 -55
- lago_python_client-1.45.0/lago_python_client/version.py +0 -1
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/LICENSE +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/README.md +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/activity_logs/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/activity_logs/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/add_ons/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/add_ons/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/api_logs/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/api_logs/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/base_model.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billable_metrics/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billable_metrics/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billing_entities/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billing_entities/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/coupons/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/coupons/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/credit_notes/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/credit_notes/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/applied_coupons_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/credit_notes_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/invoices_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/payment_methods_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/payment_requests_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/payments_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/subscriptions_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallet_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets/alert_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets/metadata_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/events/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/events/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/fees/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/fees/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/functools_ext.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/gross_revenues/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/gross_revenues/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoice_collections/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoice_collections/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoiced_usages/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoiced_usages/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoices/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoices/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/activity_log.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/add_on.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/alert.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/api_log.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/applied_coupon.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/billable_metric.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/billing_entity.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/billing_period.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/charge.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/coupon.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/credit.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/credit_note.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/customer.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/customer_projected_usage.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/customer_usage.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/error_detail.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/event.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/fixed_charge.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/gross_revenue.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice_collection.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice_item.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoiced_usage.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/lifetime_usage.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/minimum_commitment.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/mrr.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/organization.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/overdue_balance.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment_method.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment_receipt.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment_request.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/plan.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/subscription.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/tax.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/usage.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/usage_threshold.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/wallet.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/wallet_transaction.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/webhook_endpoint.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/mrrs/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/mrrs/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/organizations/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/organizations/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/overdue_balances/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/overdue_balances/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_receipts/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_receipts/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_requests/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_requests/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payments/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payments/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/charges_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/fixed_charges_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/py.typed +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/services/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/services/json.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/charges_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/fixed_charges_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/taxes/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/taxes/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/usages/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/usages/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/wallets/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/wallets/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhook_endpoints/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhook_endpoints/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhooks/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhooks/clients.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/dependency_links.txt +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/requires.txt +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/top_level.txt +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/pyproject.toml +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/setup.cfg +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_activity_logs_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_add_on_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_api_logs_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_applied_coupon_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_billable_metric_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_billing_entity_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_coupon_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_credit_note_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_applied_coupons_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_credit_notes_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_invoices_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_payment_methods_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_payment_requests_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_payments_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_sbscriptions_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallet_alert_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallet_metadata_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallets_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_event_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_fee_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_functools_ext.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_gross_revenue_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_invoice_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_invoice_collection_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_invoiced_usage_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_json_services.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_mrr_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_organization_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_overdue_balance_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_payment_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_payment_receipt_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_payment_request_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_plan_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_response_services.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_tax_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_usage_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_webhook_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_webhook_endpoint_client.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/utils/__init__.py +0 -0
- {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/utils/mixin.py +0 -0
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Type
|
|
2
|
+
from typing import Optional, Type
|
|
3
3
|
|
|
4
4
|
from lago_python_client.base_model import BaseModel
|
|
5
|
+
from lago_python_client.services.rate_limit import RateLimitRetryConfig
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class BaseClient(ABC):
|
|
8
9
|
"""The base class used for each collection client."""
|
|
9
10
|
|
|
10
|
-
def __init__(
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
base_url: str,
|
|
14
|
+
api_key: str,
|
|
15
|
+
base_ingest_url: str = "",
|
|
16
|
+
rate_limit_retry_config: Optional[RateLimitRetryConfig] = None,
|
|
17
|
+
):
|
|
11
18
|
self.base_url = base_url
|
|
12
19
|
self.api_key = api_key
|
|
13
20
|
self.base_ingest_url = base_ingest_url
|
|
21
|
+
self.rate_limit_retry_config = rate_limit_retry_config or RateLimitRetryConfig()
|
|
14
22
|
|
|
15
23
|
@property
|
|
16
24
|
@classmethod
|
|
@@ -32,6 +32,7 @@ from .payments.clients import PaymentClient
|
|
|
32
32
|
from .plans.charges_client import PlanChargesClient
|
|
33
33
|
from .plans.clients import PlanClient
|
|
34
34
|
from .plans.fixed_charges_client import PlanFixedChargesClient
|
|
35
|
+
from .services.rate_limit import RateLimitRetryConfig
|
|
35
36
|
from .subscriptions.charges_client import SubscriptionChargesClient
|
|
36
37
|
from .subscriptions.clients import SubscriptionClient
|
|
37
38
|
from .subscriptions.fixed_charges_client import SubscriptionFixedChargesClient
|
|
@@ -53,12 +54,22 @@ class Client:
|
|
|
53
54
|
API_PATH: Final[str] = "api/v1/"
|
|
54
55
|
|
|
55
56
|
def __init__(
|
|
56
|
-
self,
|
|
57
|
+
self,
|
|
58
|
+
api_key: str = "",
|
|
59
|
+
api_url: str = "",
|
|
60
|
+
use_ingest_service: bool = False,
|
|
61
|
+
ingest_api_url: str = "",
|
|
62
|
+
max_retries: int = 3,
|
|
63
|
+
retry_on_rate_limit: bool = True,
|
|
57
64
|
) -> None:
|
|
58
65
|
self.api_key: str = api_key
|
|
59
66
|
self.api_url: str = api_url
|
|
60
67
|
self.use_ingest_service: bool = use_ingest_service
|
|
61
68
|
self.ingest_api_url: str = ingest_api_url
|
|
69
|
+
self.rate_limit_retry_config: RateLimitRetryConfig = RateLimitRetryConfig(
|
|
70
|
+
max_retries=max_retries,
|
|
71
|
+
retry_on_rate_limit=retry_on_rate_limit,
|
|
72
|
+
)
|
|
62
73
|
|
|
63
74
|
@property
|
|
64
75
|
def base_api_url(self) -> str:
|
|
@@ -76,166 +87,172 @@ class Client:
|
|
|
76
87
|
|
|
77
88
|
return urljoin(ingest_url, Client.API_PATH)
|
|
78
89
|
|
|
90
|
+
def _create_client(self, client_class, base_url, api_key, base_ingest_url=""):
|
|
91
|
+
"""Create a client instance with rate limit config."""
|
|
92
|
+
if base_ingest_url:
|
|
93
|
+
return client_class(base_url, api_key, base_ingest_url, self.rate_limit_retry_config)
|
|
94
|
+
return client_class(base_url, api_key, rate_limit_retry_config=self.rate_limit_retry_config)
|
|
95
|
+
|
|
79
96
|
@callable_cached_property
|
|
80
97
|
def add_ons(self) -> AddOnClient:
|
|
81
|
-
return AddOnClient
|
|
98
|
+
return self._create_client(AddOnClient, self.base_api_url, self.api_key)
|
|
82
99
|
|
|
83
100
|
@callable_cached_property
|
|
84
101
|
def billable_metrics(self) -> BillableMetricClient:
|
|
85
|
-
return BillableMetricClient
|
|
102
|
+
return self._create_client(BillableMetricClient, self.base_api_url, self.api_key)
|
|
86
103
|
|
|
87
104
|
@callable_cached_property
|
|
88
105
|
def billing_entities(self) -> BillingEntityClient:
|
|
89
|
-
return BillingEntityClient
|
|
106
|
+
return self._create_client(BillingEntityClient, self.base_api_url, self.api_key)
|
|
90
107
|
|
|
91
108
|
@callable_cached_property
|
|
92
109
|
def coupons(self) -> CouponClient:
|
|
93
|
-
return CouponClient
|
|
110
|
+
return self._create_client(CouponClient, self.base_api_url, self.api_key)
|
|
94
111
|
|
|
95
112
|
@callable_cached_property
|
|
96
113
|
def applied_coupons(self) -> AppliedCouponClient:
|
|
97
|
-
return AppliedCouponClient
|
|
114
|
+
return self._create_client(AppliedCouponClient, self.base_api_url, self.api_key)
|
|
98
115
|
|
|
99
116
|
@callable_cached_property
|
|
100
117
|
def credit_notes(self) -> CreditNoteClient:
|
|
101
|
-
return CreditNoteClient
|
|
118
|
+
return self._create_client(CreditNoteClient, self.base_api_url, self.api_key)
|
|
102
119
|
|
|
103
120
|
@callable_cached_property
|
|
104
121
|
def customers(self) -> CustomerClient:
|
|
105
|
-
return CustomerClient
|
|
122
|
+
return self._create_client(CustomerClient, self.base_api_url, self.api_key)
|
|
106
123
|
|
|
107
124
|
@callable_cached_property
|
|
108
125
|
def customer_applied_coupons(self) -> CustomerAppliedCouponsClient:
|
|
109
|
-
return CustomerAppliedCouponsClient
|
|
126
|
+
return self._create_client(CustomerAppliedCouponsClient, self.base_api_url, self.api_key)
|
|
110
127
|
|
|
111
128
|
@callable_cached_property
|
|
112
129
|
def customer_credit_notes(self) -> CustomerCreditNotesClient:
|
|
113
|
-
return CustomerCreditNotesClient
|
|
130
|
+
return self._create_client(CustomerCreditNotesClient, self.base_api_url, self.api_key)
|
|
114
131
|
|
|
115
132
|
@callable_cached_property
|
|
116
133
|
def customer_invoices(self) -> CustomerInvoicesClient:
|
|
117
|
-
return CustomerInvoicesClient
|
|
134
|
+
return self._create_client(CustomerInvoicesClient, self.base_api_url, self.api_key)
|
|
118
135
|
|
|
119
136
|
@callable_cached_property
|
|
120
137
|
def customer_payment_methods(self) -> CustomerPaymentMethodsClient:
|
|
121
|
-
return CustomerPaymentMethodsClient
|
|
138
|
+
return self._create_client(CustomerPaymentMethodsClient, self.base_api_url, self.api_key)
|
|
122
139
|
|
|
123
140
|
@callable_cached_property
|
|
124
141
|
def customer_payments(self) -> CustomerPaymentsClient:
|
|
125
|
-
return CustomerPaymentsClient
|
|
142
|
+
return self._create_client(CustomerPaymentsClient, self.base_api_url, self.api_key)
|
|
126
143
|
|
|
127
144
|
@callable_cached_property
|
|
128
145
|
def customer_payment_requests(self) -> CustomerPaymentRequestsClient:
|
|
129
|
-
return CustomerPaymentRequestsClient
|
|
146
|
+
return self._create_client(CustomerPaymentRequestsClient, self.base_api_url, self.api_key)
|
|
130
147
|
|
|
131
148
|
@callable_cached_property
|
|
132
149
|
def customer_subscriptions(self) -> CustomerSubscriptionsClient:
|
|
133
|
-
return CustomerSubscriptionsClient
|
|
150
|
+
return self._create_client(CustomerSubscriptionsClient, self.base_api_url, self.api_key)
|
|
134
151
|
|
|
135
152
|
@callable_cached_property
|
|
136
153
|
def customer_wallets(self) -> CustomerWalletsClient:
|
|
137
|
-
return CustomerWalletsClient
|
|
154
|
+
return self._create_client(CustomerWalletsClient, self.base_api_url, self.api_key)
|
|
138
155
|
|
|
139
156
|
@callable_cached_property
|
|
140
157
|
def events(self) -> EventClient:
|
|
141
|
-
return EventClient
|
|
158
|
+
return self._create_client(EventClient, self.base_api_url, self.api_key, self.base_ingest_api_url)
|
|
142
159
|
|
|
143
160
|
@callable_cached_property
|
|
144
161
|
def fees(self) -> FeeClient:
|
|
145
|
-
return FeeClient
|
|
162
|
+
return self._create_client(FeeClient, self.base_api_url, self.api_key)
|
|
146
163
|
|
|
147
164
|
@callable_cached_property
|
|
148
165
|
def gross_revenues(self) -> GrossRevenueClient:
|
|
149
|
-
return GrossRevenueClient
|
|
166
|
+
return self._create_client(GrossRevenueClient, self.base_api_url, self.api_key)
|
|
150
167
|
|
|
151
168
|
@callable_cached_property
|
|
152
169
|
def invoices(self) -> InvoiceClient:
|
|
153
|
-
return InvoiceClient
|
|
170
|
+
return self._create_client(InvoiceClient, self.base_api_url, self.api_key)
|
|
154
171
|
|
|
155
172
|
@callable_cached_property
|
|
156
173
|
def invoiced_usages(self) -> InvoicedUsageClient:
|
|
157
|
-
return InvoicedUsageClient
|
|
174
|
+
return self._create_client(InvoicedUsageClient, self.base_api_url, self.api_key)
|
|
158
175
|
|
|
159
176
|
@callable_cached_property
|
|
160
177
|
def mrrs(self) -> MrrClient:
|
|
161
|
-
return MrrClient
|
|
178
|
+
return self._create_client(MrrClient, self.base_api_url, self.api_key)
|
|
162
179
|
|
|
163
180
|
@callable_cached_property
|
|
164
181
|
def organizations(self) -> OrganizationClient:
|
|
165
|
-
return OrganizationClient
|
|
182
|
+
return self._create_client(OrganizationClient, self.base_api_url, self.api_key)
|
|
166
183
|
|
|
167
184
|
@callable_cached_property
|
|
168
185
|
def overdue_balances(self) -> OverdueBalanceClient:
|
|
169
|
-
return OverdueBalanceClient
|
|
186
|
+
return self._create_client(OverdueBalanceClient, self.base_api_url, self.api_key)
|
|
170
187
|
|
|
171
188
|
@callable_cached_property
|
|
172
189
|
def payment_receipts(self) -> PaymentReceiptClient:
|
|
173
|
-
return PaymentReceiptClient
|
|
190
|
+
return self._create_client(PaymentReceiptClient, self.base_api_url, self.api_key)
|
|
174
191
|
|
|
175
192
|
@callable_cached_property
|
|
176
193
|
def payment_requests(self) -> PaymentRequestClient:
|
|
177
|
-
return PaymentRequestClient
|
|
194
|
+
return self._create_client(PaymentRequestClient, self.base_api_url, self.api_key)
|
|
178
195
|
|
|
179
196
|
@callable_cached_property
|
|
180
197
|
def payments(self) -> PaymentClient:
|
|
181
|
-
return PaymentClient
|
|
198
|
+
return self._create_client(PaymentClient, self.base_api_url, self.api_key)
|
|
182
199
|
|
|
183
200
|
@callable_cached_property
|
|
184
201
|
def invoice_collections(self) -> InvoiceCollectionClient:
|
|
185
|
-
return InvoiceCollectionClient
|
|
202
|
+
return self._create_client(InvoiceCollectionClient, self.base_api_url, self.api_key)
|
|
186
203
|
|
|
187
204
|
@callable_cached_property
|
|
188
205
|
def plans(self) -> PlanClient:
|
|
189
|
-
return PlanClient
|
|
206
|
+
return self._create_client(PlanClient, self.base_api_url, self.api_key)
|
|
190
207
|
|
|
191
208
|
@callable_cached_property
|
|
192
209
|
def plan_charges(self) -> PlanChargesClient:
|
|
193
|
-
return PlanChargesClient
|
|
210
|
+
return self._create_client(PlanChargesClient, self.base_api_url, self.api_key)
|
|
194
211
|
|
|
195
212
|
@callable_cached_property
|
|
196
213
|
def plan_fixed_charges(self) -> PlanFixedChargesClient:
|
|
197
|
-
return PlanFixedChargesClient
|
|
214
|
+
return self._create_client(PlanFixedChargesClient, self.base_api_url, self.api_key)
|
|
198
215
|
|
|
199
216
|
@callable_cached_property
|
|
200
217
|
def subscriptions(self) -> SubscriptionClient:
|
|
201
|
-
return SubscriptionClient
|
|
218
|
+
return self._create_client(SubscriptionClient, self.base_api_url, self.api_key)
|
|
202
219
|
|
|
203
220
|
@callable_cached_property
|
|
204
221
|
def subscription_charges(self) -> SubscriptionChargesClient:
|
|
205
|
-
return SubscriptionChargesClient
|
|
222
|
+
return self._create_client(SubscriptionChargesClient, self.base_api_url, self.api_key)
|
|
206
223
|
|
|
207
224
|
@callable_cached_property
|
|
208
225
|
def subscription_fixed_charges(self) -> SubscriptionFixedChargesClient:
|
|
209
|
-
return SubscriptionFixedChargesClient
|
|
226
|
+
return self._create_client(SubscriptionFixedChargesClient, self.base_api_url, self.api_key)
|
|
210
227
|
|
|
211
228
|
@callable_cached_property
|
|
212
229
|
def taxes(self) -> TaxClient:
|
|
213
|
-
return TaxClient
|
|
230
|
+
return self._create_client(TaxClient, self.base_api_url, self.api_key)
|
|
214
231
|
|
|
215
232
|
@callable_cached_property
|
|
216
233
|
def usages(self) -> UsageClient:
|
|
217
|
-
return UsageClient
|
|
234
|
+
return self._create_client(UsageClient, self.base_api_url, self.api_key)
|
|
218
235
|
|
|
219
236
|
@callable_cached_property
|
|
220
237
|
def wallets(self) -> WalletClient:
|
|
221
|
-
return WalletClient
|
|
238
|
+
return self._create_client(WalletClient, self.base_api_url, self.api_key)
|
|
222
239
|
|
|
223
240
|
@callable_cached_property
|
|
224
241
|
def wallet_transactions(self) -> WalletTransactionClient:
|
|
225
|
-
return WalletTransactionClient
|
|
242
|
+
return self._create_client(WalletTransactionClient, self.base_api_url, self.api_key)
|
|
226
243
|
|
|
227
244
|
@callable_cached_property
|
|
228
245
|
def webhooks(self) -> WebhookClient:
|
|
229
|
-
return WebhookClient
|
|
246
|
+
return self._create_client(WebhookClient, self.base_api_url, self.api_key)
|
|
230
247
|
|
|
231
248
|
@callable_cached_property
|
|
232
249
|
def webhook_endpoints(self) -> WebhookEndpointClient:
|
|
233
|
-
return WebhookEndpointClient
|
|
250
|
+
return self._create_client(WebhookEndpointClient, self.base_api_url, self.api_key)
|
|
234
251
|
|
|
235
252
|
@callable_cached_property
|
|
236
253
|
def activity_logs(self) -> ActivityLogClient:
|
|
237
|
-
return ActivityLogClient
|
|
254
|
+
return self._create_client(ActivityLogClient, self.base_api_url, self.api_key)
|
|
238
255
|
|
|
239
256
|
@callable_cached_property
|
|
240
257
|
def api_logs(self) -> ApiLogClient:
|
|
241
|
-
return ApiLogClient
|
|
258
|
+
return self._create_client(ApiLogClient, self.base_api_url, self.api_key)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from collections.abc import MutableMapping
|
|
2
|
+
from http import HTTPStatus
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LagoApiError(Exception):
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
status_code: int,
|
|
10
|
+
url: Optional[str],
|
|
11
|
+
response: Any,
|
|
12
|
+
detail: Optional[str] = None,
|
|
13
|
+
headers: Optional[MutableMapping[str, str]] = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
if detail is None:
|
|
16
|
+
detail = HTTPStatus(status_code).phrase
|
|
17
|
+
self.status_code = status_code
|
|
18
|
+
self.url = url
|
|
19
|
+
self.response = response
|
|
20
|
+
self.detail = detail
|
|
21
|
+
self.headers = headers
|
|
22
|
+
super().__init__(self.response)
|
|
23
|
+
|
|
24
|
+
def __repr__(self) -> str:
|
|
25
|
+
class_name = self.__class__.__name__
|
|
26
|
+
return f"{class_name}(response={self.response!r}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LagoRateLimitError(LagoApiError):
|
|
30
|
+
"""Exception raised when rate limit is exceeded (HTTP 429)."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
status_code: int,
|
|
35
|
+
url: Optional[str],
|
|
36
|
+
response: Any,
|
|
37
|
+
detail: Optional[str] = None,
|
|
38
|
+
headers: Optional[MutableMapping[str, str]] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
super().__init__(
|
|
41
|
+
status_code=status_code,
|
|
42
|
+
url=url,
|
|
43
|
+
response=response,
|
|
44
|
+
detail=detail,
|
|
45
|
+
headers=headers,
|
|
46
|
+
)
|
|
47
|
+
# Parse rate limit headers
|
|
48
|
+
self.limit: Optional[int] = None
|
|
49
|
+
self.remaining: Optional[int] = None
|
|
50
|
+
self.reset: Optional[int] = None
|
|
51
|
+
|
|
52
|
+
if headers:
|
|
53
|
+
try:
|
|
54
|
+
self.limit = int(headers["x-ratelimit-limit"])
|
|
55
|
+
except (KeyError, ValueError, TypeError):
|
|
56
|
+
pass
|
|
57
|
+
try:
|
|
58
|
+
self.remaining = int(headers["x-ratelimit-remaining"])
|
|
59
|
+
except (KeyError, ValueError, TypeError):
|
|
60
|
+
pass
|
|
61
|
+
try:
|
|
62
|
+
self.reset = int(headers["x-ratelimit-reset"])
|
|
63
|
+
except (KeyError, ValueError, TypeError):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
return (
|
|
68
|
+
f"LagoRateLimitError(limit={self.limit}, remaining={self.remaining}, reset={self.reset}, url={self.url!r})"
|
|
69
|
+
)
|
|
@@ -10,6 +10,7 @@ except ImportError: # Python 3.7
|
|
|
10
10
|
from collections.abc import Mapping
|
|
11
11
|
|
|
12
12
|
from lago_python_client.base_model import BaseModel
|
|
13
|
+
from lago_python_client.services.rate_limit import RateLimitRetryConfig
|
|
13
14
|
|
|
14
15
|
from .services.json import to_json
|
|
15
16
|
from .services.request import (
|
|
@@ -47,6 +48,8 @@ class _ClientMixin(Protocol[_PM_co]):
|
|
|
47
48
|
def api_key(self) -> str: ...
|
|
48
49
|
@property
|
|
49
50
|
def base_url(self) -> str: ...
|
|
51
|
+
@property
|
|
52
|
+
def rate_limit_retry_config(self) -> RateLimitRetryConfig: ...
|
|
50
53
|
|
|
51
54
|
|
|
52
55
|
class CreateCommandMixin(Generic[_M]):
|
|
@@ -71,6 +74,7 @@ class CreateCommandMixin(Generic[_M]):
|
|
|
71
74
|
),
|
|
72
75
|
headers=make_headers(api_key=self.api_key),
|
|
73
76
|
timeout=timeout,
|
|
77
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
74
78
|
)
|
|
75
79
|
|
|
76
80
|
# Process response data
|
|
@@ -105,6 +109,7 @@ class DestroyCommandMixin(Generic[_M]):
|
|
|
105
109
|
),
|
|
106
110
|
headers=make_headers(api_key=self.api_key),
|
|
107
111
|
timeout=timeout,
|
|
112
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
108
113
|
)
|
|
109
114
|
|
|
110
115
|
# Process response data
|
|
@@ -134,6 +139,7 @@ class FindAllCommandMixin(Generic[_M]):
|
|
|
134
139
|
),
|
|
135
140
|
headers=make_headers(api_key=self.api_key),
|
|
136
141
|
timeout=timeout,
|
|
142
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
137
143
|
)
|
|
138
144
|
|
|
139
145
|
# Process response data
|
|
@@ -167,6 +173,7 @@ class FindAllChildrenCommandMixin(Generic[_M]):
|
|
|
167
173
|
),
|
|
168
174
|
query_pairs=options,
|
|
169
175
|
),
|
|
176
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
170
177
|
)
|
|
171
178
|
|
|
172
179
|
return prepare_index_response(
|
|
@@ -200,6 +207,7 @@ class FindChildCommandMixin(Generic[_M]):
|
|
|
200
207
|
query_pairs=options,
|
|
201
208
|
),
|
|
202
209
|
headers=make_headers(api_key=self.api_key),
|
|
210
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
203
211
|
)
|
|
204
212
|
|
|
205
213
|
return prepare_object_response(
|
|
@@ -232,6 +240,7 @@ class CreateChildCommandMixin(Generic[_M]):
|
|
|
232
240
|
),
|
|
233
241
|
content=to_json({self.ROOT_NAME: input_object.dict(exclude_none=True)}),
|
|
234
242
|
headers=make_headers(api_key=self.api_key),
|
|
243
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
235
244
|
)
|
|
236
245
|
|
|
237
246
|
return prepare_object_response(
|
|
@@ -266,6 +275,7 @@ class UpdateChildCommandMixin(Generic[_M]):
|
|
|
266
275
|
),
|
|
267
276
|
content=to_json({self.ROOT_NAME: input_object.dict(exclude_none=True)}),
|
|
268
277
|
headers=make_headers(api_key=self.api_key),
|
|
278
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
269
279
|
)
|
|
270
280
|
|
|
271
281
|
return prepare_object_response(
|
|
@@ -298,6 +308,7 @@ class DestroyChildCommandMixin(Generic[_M]):
|
|
|
298
308
|
query_pairs=options,
|
|
299
309
|
),
|
|
300
310
|
headers=make_headers(api_key=self.api_key),
|
|
311
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
301
312
|
)
|
|
302
313
|
|
|
303
314
|
return prepare_object_response(
|
|
@@ -327,6 +338,7 @@ class FindCommandMixin(Generic[_M]):
|
|
|
327
338
|
),
|
|
328
339
|
headers=make_headers(api_key=self.api_key),
|
|
329
340
|
timeout=timeout,
|
|
341
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
330
342
|
)
|
|
331
343
|
|
|
332
344
|
# Process response data
|
|
@@ -359,6 +371,7 @@ class UpdateCommandMixin(Generic[_M]):
|
|
|
359
371
|
),
|
|
360
372
|
headers=make_headers(api_key=self.api_key),
|
|
361
373
|
timeout=timeout,
|
|
374
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
362
375
|
)
|
|
363
376
|
|
|
364
377
|
# Process response data
|
|
@@ -389,6 +402,7 @@ class NestedCreateCommandMixin(Generic[_M]):
|
|
|
389
402
|
),
|
|
390
403
|
headers=make_headers(api_key=self.api_key),
|
|
391
404
|
timeout=timeout,
|
|
405
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
392
406
|
)
|
|
393
407
|
|
|
394
408
|
# Process response data
|
|
@@ -426,6 +440,7 @@ class NestedUpdateCommandMixin(Generic[_M]):
|
|
|
426
440
|
),
|
|
427
441
|
headers=make_headers(api_key=self.api_key),
|
|
428
442
|
timeout=timeout,
|
|
443
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
429
444
|
)
|
|
430
445
|
|
|
431
446
|
# Process response data
|
|
@@ -454,6 +469,7 @@ class NestedDestroyCommandMixin(Generic[_M]):
|
|
|
454
469
|
),
|
|
455
470
|
headers=make_headers(api_key=self.api_key),
|
|
456
471
|
timeout=timeout,
|
|
472
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
457
473
|
)
|
|
458
474
|
|
|
459
475
|
# Process response data
|
|
@@ -482,6 +498,7 @@ class NestedFindCommandMixin(Generic[_M]):
|
|
|
482
498
|
),
|
|
483
499
|
headers=make_headers(api_key=self.api_key),
|
|
484
500
|
timeout=timeout,
|
|
501
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
485
502
|
)
|
|
486
503
|
|
|
487
504
|
# Process response data
|
|
@@ -510,6 +527,7 @@ class NestedFindAllCommandMixin(Generic[_M]):
|
|
|
510
527
|
),
|
|
511
528
|
headers=make_headers(api_key=self.api_key),
|
|
512
529
|
timeout=timeout,
|
|
530
|
+
rate_limit_retry_config=self.rate_limit_retry_config,
|
|
513
531
|
)
|
|
514
532
|
|
|
515
533
|
# Process response data
|
|
@@ -53,6 +53,8 @@ class FeeResponse(BaseResponseModel):
|
|
|
53
53
|
total_amount_cents: Optional[int]
|
|
54
54
|
unit_amount_cents: Optional[int] # deprecated
|
|
55
55
|
precise_unit_amount: Optional[str]
|
|
56
|
+
sub_total_excluding_taxes_amount_cents: Optional[int]
|
|
57
|
+
sub_total_excluding_taxes_precise_amount_cents: Optional[str]
|
|
56
58
|
precise_amount: Optional[str]
|
|
57
59
|
precise_total_amount: Optional[str]
|
|
58
60
|
taxes_precise_amount: Optional[str]
|
|
@@ -10,16 +10,6 @@ class InvoiceCustomSectionInput(BaseModel):
|
|
|
10
10
|
invoice_custom_section_codes: Optional[List[str]]
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class AppliedInvoiceCustomSection(BaseResponseModel):
|
|
14
|
-
lago_id: Optional[str]
|
|
15
|
-
invoice_custom_section_id: Optional[str]
|
|
16
|
-
created_at: Optional[str]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class AppliedInvoiceCustomSections(BaseResponseModel):
|
|
20
|
-
__root__: List[AppliedInvoiceCustomSection]
|
|
21
|
-
|
|
22
|
-
|
|
23
13
|
class InvoiceCustomSectionResponse(BaseResponseModel):
|
|
24
14
|
lago_id: Optional[str]
|
|
25
15
|
code: Optional[str]
|
|
@@ -30,5 +20,16 @@ class InvoiceCustomSectionResponse(BaseResponseModel):
|
|
|
30
20
|
applied_to_organization: Optional[bool] # Deprecated
|
|
31
21
|
|
|
32
22
|
|
|
23
|
+
class AppliedInvoiceCustomSection(BaseResponseModel):
|
|
24
|
+
lago_id: Optional[str]
|
|
25
|
+
invoice_custom_section_id: Optional[str]
|
|
26
|
+
created_at: Optional[str]
|
|
27
|
+
invoice_custom_section: Optional[InvoiceCustomSectionResponse]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AppliedInvoiceCustomSections(BaseResponseModel):
|
|
31
|
+
__root__: List[AppliedInvoiceCustomSection]
|
|
32
|
+
|
|
33
|
+
|
|
33
34
|
class InvoiceCustomSectionsResponseList(BaseResponseModel):
|
|
34
35
|
__root__: List[InvoiceCustomSectionResponse]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from httpx import Response
|
|
6
|
+
|
|
7
|
+
from ..exceptions import LagoRateLimitError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RateLimitRetryConfig:
|
|
11
|
+
"""Configuration for rate limit retry behavior."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
max_retries: int = 3,
|
|
16
|
+
retry_on_rate_limit: bool = True,
|
|
17
|
+
base_backoff_seconds: float = 1.0,
|
|
18
|
+
backoff_multiplier: float = 2.0,
|
|
19
|
+
max_retry_delay: float = 20.0,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Initialize rate limit retry configuration.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
max_retries: Maximum number of retry attempts (0 means no retries).
|
|
26
|
+
retry_on_rate_limit: Whether to automatically retry on 429 responses.
|
|
27
|
+
base_backoff_seconds: Initial backoff duration in seconds for exponential backoff.
|
|
28
|
+
backoff_multiplier: Multiplier for exponential backoff between retries.
|
|
29
|
+
max_retry_delay: Maximum delay in seconds before a retry (default: 20).
|
|
30
|
+
"""
|
|
31
|
+
self.max_retries = max_retries
|
|
32
|
+
self.retry_on_rate_limit = retry_on_rate_limit
|
|
33
|
+
self.base_backoff_seconds = base_backoff_seconds
|
|
34
|
+
self.backoff_multiplier = backoff_multiplier
|
|
35
|
+
self.max_retry_delay = max_retry_delay
|
|
36
|
+
|
|
37
|
+
def calculate_backoff(self, retry_attempt: int) -> float:
|
|
38
|
+
"""
|
|
39
|
+
Calculate backoff duration for exponential backoff strategy.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
retry_attempt: Current retry attempt number (0-based).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Duration in seconds to wait before retrying.
|
|
46
|
+
"""
|
|
47
|
+
return self.base_backoff_seconds * (self.backoff_multiplier**retry_attempt)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def handle_rate_limit_response(
|
|
51
|
+
response: "Response",
|
|
52
|
+
config: RateLimitRetryConfig,
|
|
53
|
+
retry_attempt: int,
|
|
54
|
+
) -> float:
|
|
55
|
+
"""
|
|
56
|
+
Determine if and how long to wait before retrying a rate-limited request.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
response: The HTTP response object (should be 429).
|
|
60
|
+
config: Rate limit retry configuration.
|
|
61
|
+
retry_attempt: Current retry attempt number (0-based).
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Duration in seconds to wait before retrying, or raises LagoRateLimitError.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
LagoRateLimitError: If retry is not configured or max retries exceeded.
|
|
68
|
+
"""
|
|
69
|
+
# Check if retries are enabled
|
|
70
|
+
if not config.retry_on_rate_limit:
|
|
71
|
+
raise LagoRateLimitError(
|
|
72
|
+
status_code=response.status_code,
|
|
73
|
+
url=str(response.request.url),
|
|
74
|
+
response=None,
|
|
75
|
+
headers=response.headers,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Check if we've exceeded max retries
|
|
79
|
+
if retry_attempt >= config.max_retries:
|
|
80
|
+
raise LagoRateLimitError(
|
|
81
|
+
status_code=response.status_code,
|
|
82
|
+
url=str(response.request.url),
|
|
83
|
+
response=None,
|
|
84
|
+
headers=response.headers,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Try to get reset time from header
|
|
88
|
+
reset_seconds: Optional[int] = None
|
|
89
|
+
if "x-ratelimit-reset" in response.headers:
|
|
90
|
+
try:
|
|
91
|
+
reset_seconds = int(response.headers["x-ratelimit-reset"])
|
|
92
|
+
except (ValueError, TypeError):
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# Use header value if available, otherwise use exponential backoff
|
|
96
|
+
if reset_seconds is not None:
|
|
97
|
+
delay = float(reset_seconds)
|
|
98
|
+
else:
|
|
99
|
+
delay = config.calculate_backoff(retry_attempt)
|
|
100
|
+
|
|
101
|
+
# Cap at max_retry_delay
|
|
102
|
+
return min(delay, config.max_retry_delay)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def is_rate_limit_response(response: "Response") -> bool:
|
|
106
|
+
"""
|
|
107
|
+
Check if a response indicates rate limit exceeded.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
response: The HTTP response to check.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
True if response is a 429 Too Many Requests, False otherwise.
|
|
114
|
+
"""
|
|
115
|
+
return response.status_code == 429
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def wait_for_retry(duration: float) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Sleep for the specified duration before retrying.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
duration: Number of seconds to wait.
|
|
124
|
+
"""
|
|
125
|
+
if duration > 0:
|
|
126
|
+
time.sleep(duration)
|