From 15852c155fa49397b3184cd648603070fbf59a87 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 12:47:48 +0000 Subject: [PATCH 1/3] chore(internal): minor formatting changes (#509) --- .github/workflows/ci.yml | 3 +-- scripts/bootstrap | 2 +- scripts/lint | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40293964..c8a8a4f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,6 @@ jobs: lint: name: lint runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 @@ -30,6 +29,7 @@ jobs: - name: Run lints run: ./scripts/lint + test: name: test runs-on: ubuntu-latest @@ -50,4 +50,3 @@ jobs: - name: Run tests run: ./scripts/test - diff --git a/scripts/bootstrap b/scripts/bootstrap index 8c5c60eb..e84fe62c 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then brew bundle check >/dev/null 2>&1 || { echo "==> Installing Homebrew dependencies…" brew bundle diff --git a/scripts/lint b/scripts/lint index 446e36fd..38b82fa1 100755 --- a/scripts/lint +++ b/scripts/lint @@ -9,4 +9,3 @@ rye run lint echo "==> Making sure it imports" rye run python -c 'import orb' - From 052d15d9822356db28a4a42ea085133a29b183aa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:31:07 +0000 Subject: [PATCH 2/3] feat(api): api update (#511) --- .stats.yml | 4 +- api.md | 2 + src/orb/resources/customers/customers.py | 212 ++++++++++++++++++ src/orb/types/invoice.py | 7 +- .../types/invoice_fetch_upcoming_response.py | 7 +- tests/api_resources/test_customers.py | 162 +++++++++++++ 6 files changed, 388 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index efeca04e..ae0299a7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 101 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-7fca89ba5a0b4997358c25e6cdfb616a1d8b93a6820e25078f3fa5f61110bfe6.yml +configured_endpoints: 103 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-326205df28a52e9ad57c34d7ed1ec85fadd67f9a041df2882ebaa65f6de09930.yml diff --git a/api.md b/api.md index c6fdb8fa..be3ecaf3 100644 --- a/api.md +++ b/api.md @@ -75,6 +75,8 @@ Methods: - client.customers.delete(customer_id) -> None - client.customers.fetch(customer_id) -> Customer - client.customers.fetch_by_external_id(external_customer_id) -> Customer +- client.customers.sync_payment_methods_from_gateway(external_customer_id) -> None +- client.customers.sync_payment_methods_from_gateway_by_external_customer_id(customer_id) -> None - client.customers.update_by_external_id(id, \*\*params) -> Customer ## Costs diff --git a/src/orb/resources/customers/customers.py b/src/orb/resources/customers/customers.py index b02118f5..f131d20c 100644 --- a/src/orb/resources/customers/customers.py +++ b/src/orb/resources/customers/customers.py @@ -732,6 +732,100 @@ def fetch_by_external_id( cast_to=Customer, ) + def sync_payment_methods_from_gateway( + self, + external_customer_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> None: + """ + Sync Orb's payment methods for the customer with their gateway. + + This method can be called before taking an action that may cause the customer to + be charged, ensuring that the most up-to-date payment method is charged. + + **Note**: This functionality is currently only available for Stripe. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not external_customer_id: + raise ValueError( + f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" + ) + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/customers/external_customer_id/{external_customer_id}/sync_payment_methods_from_gateway", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=NoneType, + ) + + def sync_payment_methods_from_gateway_by_external_customer_id( + self, + customer_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> None: + """ + Sync Orb's payment methods for the customer with their gateway. + + This method can be called before taking an action that may cause the customer to + be charged, ensuring that the most up-to-date payment method is charged. + + **Note**: This functionality is currently only available for Stripe. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not customer_id: + raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/customers/{customer_id}/sync_payment_methods_from_gateway", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=NoneType, + ) + def update_by_external_id( self, id: str, @@ -1632,6 +1726,100 @@ async def fetch_by_external_id( cast_to=Customer, ) + async def sync_payment_methods_from_gateway( + self, + external_customer_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> None: + """ + Sync Orb's payment methods for the customer with their gateway. + + This method can be called before taking an action that may cause the customer to + be charged, ensuring that the most up-to-date payment method is charged. + + **Note**: This functionality is currently only available for Stripe. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not external_customer_id: + raise ValueError( + f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" + ) + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/customers/external_customer_id/{external_customer_id}/sync_payment_methods_from_gateway", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=NoneType, + ) + + async def sync_payment_methods_from_gateway_by_external_customer_id( + self, + customer_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> None: + """ + Sync Orb's payment methods for the customer with their gateway. + + This method can be called before taking an action that may cause the customer to + be charged, ensuring that the most up-to-date payment method is charged. + + **Note**: This functionality is currently only available for Stripe. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not customer_id: + raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/customers/{customer_id}/sync_payment_methods_from_gateway", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=NoneType, + ) + async def update_by_external_id( self, id: str, @@ -1876,6 +2064,12 @@ def __init__(self, customers: Customers) -> None: self.fetch_by_external_id = _legacy_response.to_raw_response_wrapper( customers.fetch_by_external_id, ) + self.sync_payment_methods_from_gateway = _legacy_response.to_raw_response_wrapper( + customers.sync_payment_methods_from_gateway, + ) + self.sync_payment_methods_from_gateway_by_external_customer_id = _legacy_response.to_raw_response_wrapper( + customers.sync_payment_methods_from_gateway_by_external_customer_id, + ) self.update_by_external_id = _legacy_response.to_raw_response_wrapper( customers.update_by_external_id, ) @@ -1915,6 +2109,12 @@ def __init__(self, customers: AsyncCustomers) -> None: self.fetch_by_external_id = _legacy_response.async_to_raw_response_wrapper( customers.fetch_by_external_id, ) + self.sync_payment_methods_from_gateway = _legacy_response.async_to_raw_response_wrapper( + customers.sync_payment_methods_from_gateway, + ) + self.sync_payment_methods_from_gateway_by_external_customer_id = _legacy_response.async_to_raw_response_wrapper( + customers.sync_payment_methods_from_gateway_by_external_customer_id, + ) self.update_by_external_id = _legacy_response.async_to_raw_response_wrapper( customers.update_by_external_id, ) @@ -1954,6 +2154,12 @@ def __init__(self, customers: Customers) -> None: self.fetch_by_external_id = to_streamed_response_wrapper( customers.fetch_by_external_id, ) + self.sync_payment_methods_from_gateway = to_streamed_response_wrapper( + customers.sync_payment_methods_from_gateway, + ) + self.sync_payment_methods_from_gateway_by_external_customer_id = to_streamed_response_wrapper( + customers.sync_payment_methods_from_gateway_by_external_customer_id, + ) self.update_by_external_id = to_streamed_response_wrapper( customers.update_by_external_id, ) @@ -1993,6 +2199,12 @@ def __init__(self, customers: AsyncCustomers) -> None: self.fetch_by_external_id = async_to_streamed_response_wrapper( customers.fetch_by_external_id, ) + self.sync_payment_methods_from_gateway = async_to_streamed_response_wrapper( + customers.sync_payment_methods_from_gateway, + ) + self.sync_payment_methods_from_gateway_by_external_customer_id = async_to_streamed_response_wrapper( + customers.sync_payment_methods_from_gateway_by_external_customer_id, + ) self.update_by_external_id = async_to_streamed_response_wrapper( customers.update_by_external_id, ) diff --git a/src/orb/types/invoice.py b/src/orb/types/invoice.py index 6703f65b..e3c51e72 100644 --- a/src/orb/types/invoice.py +++ b/src/orb/types/invoice.py @@ -706,8 +706,11 @@ class Invoice(BaseModel): discounts: List[InvoiceLevelDiscount] - due_date: datetime - """When the invoice payment is due.""" + due_date: Optional[datetime] = None + """When the invoice payment is due. + + The due date is null if the invoice is not yet finalized. + """ eligible_to_issue_at: Optional[datetime] = None """ diff --git a/src/orb/types/invoice_fetch_upcoming_response.py b/src/orb/types/invoice_fetch_upcoming_response.py index 4b576e98..eafb79b5 100644 --- a/src/orb/types/invoice_fetch_upcoming_response.py +++ b/src/orb/types/invoice_fetch_upcoming_response.py @@ -706,8 +706,11 @@ class InvoiceFetchUpcomingResponse(BaseModel): discounts: List[InvoiceLevelDiscount] - due_date: datetime - """When the invoice payment is due.""" + due_date: Optional[datetime] = None + """When the invoice payment is due. + + The due date is null if the invoice is not yet finalized. + """ eligible_to_issue_at: Optional[datetime] = None """ diff --git a/tests/api_resources/test_customers.py b/tests/api_resources/test_customers.py index 38419de3..abedef0b 100644 --- a/tests/api_resources/test_customers.py +++ b/tests/api_resources/test_customers.py @@ -350,6 +350,82 @@ def test_path_params_fetch_by_external_id(self, client: Orb) -> None: "", ) + @parametrize + def test_method_sync_payment_methods_from_gateway(self, client: Orb) -> None: + customer = client.customers.sync_payment_methods_from_gateway( + "external_customer_id", + ) + assert customer is None + + @parametrize + def test_raw_response_sync_payment_methods_from_gateway(self, client: Orb) -> None: + response = client.customers.with_raw_response.sync_payment_methods_from_gateway( + "external_customer_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + customer = response.parse() + assert customer is None + + @parametrize + def test_streaming_response_sync_payment_methods_from_gateway(self, client: Orb) -> None: + with client.customers.with_streaming_response.sync_payment_methods_from_gateway( + "external_customer_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + customer = response.parse() + assert customer is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_sync_payment_methods_from_gateway(self, client: Orb) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_customer_id` but received ''"): + client.customers.with_raw_response.sync_payment_methods_from_gateway( + "", + ) + + @parametrize + def test_method_sync_payment_methods_from_gateway_by_external_customer_id(self, client: Orb) -> None: + customer = client.customers.sync_payment_methods_from_gateway_by_external_customer_id( + "customer_id", + ) + assert customer is None + + @parametrize + def test_raw_response_sync_payment_methods_from_gateway_by_external_customer_id(self, client: Orb) -> None: + response = client.customers.with_raw_response.sync_payment_methods_from_gateway_by_external_customer_id( + "customer_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + customer = response.parse() + assert customer is None + + @parametrize + def test_streaming_response_sync_payment_methods_from_gateway_by_external_customer_id(self, client: Orb) -> None: + with client.customers.with_streaming_response.sync_payment_methods_from_gateway_by_external_customer_id( + "customer_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + customer = response.parse() + assert customer is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_sync_payment_methods_from_gateway_by_external_customer_id(self, client: Orb) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `customer_id` but received ''"): + client.customers.with_raw_response.sync_payment_methods_from_gateway_by_external_customer_id( + "", + ) + @parametrize def test_method_update_by_external_id(self, client: Orb) -> None: customer = client.customers.update_by_external_id( @@ -775,6 +851,92 @@ async def test_path_params_fetch_by_external_id(self, async_client: AsyncOrb) -> "", ) + @parametrize + async def test_method_sync_payment_methods_from_gateway(self, async_client: AsyncOrb) -> None: + customer = await async_client.customers.sync_payment_methods_from_gateway( + "external_customer_id", + ) + assert customer is None + + @parametrize + async def test_raw_response_sync_payment_methods_from_gateway(self, async_client: AsyncOrb) -> None: + response = await async_client.customers.with_raw_response.sync_payment_methods_from_gateway( + "external_customer_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + customer = response.parse() + assert customer is None + + @parametrize + async def test_streaming_response_sync_payment_methods_from_gateway(self, async_client: AsyncOrb) -> None: + async with async_client.customers.with_streaming_response.sync_payment_methods_from_gateway( + "external_customer_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + customer = await response.parse() + assert customer is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_sync_payment_methods_from_gateway(self, async_client: AsyncOrb) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_customer_id` but received ''"): + await async_client.customers.with_raw_response.sync_payment_methods_from_gateway( + "", + ) + + @parametrize + async def test_method_sync_payment_methods_from_gateway_by_external_customer_id( + self, async_client: AsyncOrb + ) -> None: + customer = await async_client.customers.sync_payment_methods_from_gateway_by_external_customer_id( + "customer_id", + ) + assert customer is None + + @parametrize + async def test_raw_response_sync_payment_methods_from_gateway_by_external_customer_id( + self, async_client: AsyncOrb + ) -> None: + response = ( + await async_client.customers.with_raw_response.sync_payment_methods_from_gateway_by_external_customer_id( + "customer_id", + ) + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + customer = response.parse() + assert customer is None + + @parametrize + async def test_streaming_response_sync_payment_methods_from_gateway_by_external_customer_id( + self, async_client: AsyncOrb + ) -> None: + async with async_client.customers.with_streaming_response.sync_payment_methods_from_gateway_by_external_customer_id( + "customer_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + customer = await response.parse() + assert customer is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_sync_payment_methods_from_gateway_by_external_customer_id( + self, async_client: AsyncOrb + ) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `customer_id` but received ''"): + await async_client.customers.with_raw_response.sync_payment_methods_from_gateway_by_external_customer_id( + "", + ) + @parametrize async def test_method_update_by_external_id(self, async_client: AsyncOrb) -> None: customer = await async_client.customers.update_by_external_id( From 1d5c95ad515f73f840d5a2b98e682ec32dbeec46 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:31:29 +0000 Subject: [PATCH 3/3] release: 2.25.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/orb/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 288087c1..c5fe8ab6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.24.0" + ".": "2.25.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c1983c92..1e0b2a97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.25.0 (2025-01-23) + +Full Changelog: [v2.24.0...v2.25.0](https://github.com/orbcorp/orb-python/compare/v2.24.0...v2.25.0) + +### Features + +* **api:** api update ([#511](https://github.com/orbcorp/orb-python/issues/511)) ([052d15d](https://github.com/orbcorp/orb-python/commit/052d15d9822356db28a4a42ea085133a29b183aa)) + + +### Chores + +* **internal:** minor formatting changes ([#509](https://github.com/orbcorp/orb-python/issues/509)) ([15852c1](https://github.com/orbcorp/orb-python/commit/15852c155fa49397b3184cd648603070fbf59a87)) + ## 2.24.0 (2025-01-21) Full Changelog: [v2.23.1...v2.24.0](https://github.com/orbcorp/orb-python/compare/v2.23.1...v2.24.0) diff --git a/pyproject.toml b/pyproject.toml index 6cd22fef..c8851608 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "orb-billing" -version = "2.24.0" +version = "2.25.0" description = "The official Python library for the orb API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/orb/_version.py b/src/orb/_version.py index 46f0c987..4ce2f17c 100644 --- a/src/orb/_version.py +++ b/src/orb/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "orb" -__version__ = "2.24.0" # x-release-please-version +__version__ = "2.25.0" # x-release-please-version