From 0a651fceefb515bd7faf20bb32a0dcf36947b689 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 17 Feb 2023 23:36:50 +0000 Subject: [PATCH] feat(api): add events, event subscriptions, and associated cursor page --- .stats.yml | 2 +- api.md | 37 ++ src/lithic/_client.py | 4 + src/lithic/pagination.py | 64 ++- src/lithic/resources/__init__.py | 3 + .../resources/auth_stream_enrollment.py | 4 - src/lithic/resources/events/__init__.py | 6 + src/lithic/resources/events/events.py | 218 ++++++++ src/lithic/resources/events/subscriptions.py | 509 ++++++++++++++++++ src/lithic/resources/transactions.py | 34 +- src/lithic/types/__init__.py | 4 + src/lithic/types/event.py | 28 + src/lithic/types/event_list_params.py | 39 ++ src/lithic/types/event_resend_params.py | 40 ++ src/lithic/types/event_subscription.py | 23 + src/lithic/types/events/__init__.py | 14 + .../events/subscription_create_params.py | 25 + .../types/events/subscription_list_params.py | 24 + .../subscription_retrieve_secret_response.py | 11 + .../events/subscription_update_params.py | 25 + src/lithic/types/transaction.py | 52 +- tests/api_resources/events/__init__.py | 1 + .../events/test_subscriptions.py | 217 ++++++++ .../test_auth_stream_enrollment.py | 18 +- tests/api_resources/test_events.py | 88 +++ 25 files changed, 1391 insertions(+), 99 deletions(-) create mode 100644 src/lithic/resources/events/__init__.py create mode 100644 src/lithic/resources/events/events.py create mode 100644 src/lithic/resources/events/subscriptions.py create mode 100644 src/lithic/types/event.py create mode 100644 src/lithic/types/event_list_params.py create mode 100644 src/lithic/types/event_resend_params.py create mode 100644 src/lithic/types/event_subscription.py create mode 100644 src/lithic/types/events/__init__.py create mode 100644 src/lithic/types/events/subscription_create_params.py create mode 100644 src/lithic/types/events/subscription_list_params.py create mode 100644 src/lithic/types/events/subscription_retrieve_secret_response.py create mode 100644 src/lithic/types/events/subscription_update_params.py create mode 100644 tests/api_resources/events/__init__.py create mode 100644 tests/api_resources/events/test_subscriptions.py create mode 100644 tests/api_resources/test_events.py diff --git a/.stats.yml b/.stats.yml index 76e9cb43..284caebf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 40 +configured_endpoints: 52 diff --git a/api.md b/api.md index 10d13cfe..c3afeb1d 100644 --- a/api.md +++ b/api.md @@ -122,6 +122,43 @@ Custom Methods: - `get_embed_html` - `get_embed_url` +# Events + +Types: + +```python +from lithic.types import Event, EventSubscription +``` + +Methods: + +- client.events.retrieve(event_token) -> Event +- client.events.list(\*\*params) -> SyncCursorPage[Event] + +Custom Methods: + +- `resend` + +## Subscriptions + +Types: + +```python +from lithic.types.events import SubscriptionRetrieveSecretResponse +``` + +Methods: + +- client.events.subscriptions.create(\*\*params) -> EventSubscription +- client.events.subscriptions.retrieve(event_subscription_token) -> EventSubscription +- client.events.subscriptions.update(event_subscription_token, \*\*params) -> EventSubscription +- client.events.subscriptions.list(\*\*params) -> SyncCursorPage[EventSubscription] +- client.events.subscriptions.delete(event_subscription_token) -> None +- client.events.subscriptions.recover(event_subscription_token) -> None +- client.events.subscriptions.replay_missing(event_subscription_token) -> None +- client.events.subscriptions.retrieve_secret(event_subscription_token) -> SubscriptionRetrieveSecretResponse +- client.events.subscriptions.rotate_secret(event_subscription_token) -> None + # FundingSources Types: diff --git a/src/lithic/_client.py b/src/lithic/_client.py index f9904152..4041401f 100644 --- a/src/lithic/_client.py +++ b/src/lithic/_client.py @@ -54,6 +54,7 @@ class Lithic(SyncAPIClient): auth_rules: resources.AuthRules auth_stream_enrollment: resources.AuthStreamEnrollmentResource cards: resources.Cards + events: resources.Events funding_sources: resources.FundingSources transactions: resources.Transactions @@ -123,6 +124,7 @@ def __init__( self.auth_rules = resources.AuthRules(self) self.auth_stream_enrollment = resources.AuthStreamEnrollmentResource(self) self.cards = resources.Cards(self) + self.events = resources.Events(self) self.funding_sources = resources.FundingSources(self) self.transactions = resources.Transactions(self) @@ -209,6 +211,7 @@ class AsyncLithic(AsyncAPIClient): auth_rules: resources.AsyncAuthRules auth_stream_enrollment: resources.AsyncAuthStreamEnrollmentResource cards: resources.AsyncCards + events: resources.AsyncEvents funding_sources: resources.AsyncFundingSources transactions: resources.AsyncTransactions @@ -278,6 +281,7 @@ def __init__( self.auth_rules = resources.AsyncAuthRules(self) self.auth_stream_enrollment = resources.AsyncAuthStreamEnrollmentResource(self) self.cards = resources.AsyncCards(self) + self.events = resources.AsyncEvents(self) self.funding_sources = resources.AsyncFundingSources(self) self.transactions = resources.AsyncTransactions(self) diff --git a/src/lithic/pagination.py b/src/lithic/pagination.py index 049d0ba5..0ce74e1f 100644 --- a/src/lithic/pagination.py +++ b/src/lithic/pagination.py @@ -1,16 +1,22 @@ # File generated from our OpenAPI spec by Stainless. -from typing import List, Generic, TypeVar, Optional +from typing import Any, List, Generic, TypeVar, Optional, cast +from typing_extensions import Protocol, runtime_checkable from ._types import ModelT from ._models import BaseModel from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage -__all__ = ["SyncPage", "AsyncPage"] +__all__ = ["SyncPage", "AsyncPage", "SyncCursorPage", "AsyncCursorPage"] _BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) +@runtime_checkable +class CursorPageItem(Protocol): + token: str + + class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): data: List[ModelT] page: int @@ -41,3 +47,57 @@ def next_page_info(self) -> Optional[PageInfo]: if not current_page < self.total_pages: return None return PageInfo(params={"page": current_page + 1}) + + +class SyncCursorPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + data: List[ModelT] + has_more: bool + + def _get_page_items(self) -> List[ModelT]: + return self.data + + def next_page_info(self) -> Optional[PageInfo]: + is_forwards = not self._options.params.get("ending_before", False) + + if len(self.data) > 0: + return None + + if is_forwards: + item = cast(Any, self.data[-1]) + if not isinstance(item, CursorPageItem): + # TODO emit warning log + return None + return PageInfo(params={"staring_after": item.token}) + else: + item = cast(Any, self.data[0]) + if not isinstance(item, CursorPageItem): + # TODO emit warning log + return None + return PageInfo(params={"ending_before": item.token}) + + +class AsyncCursorPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + data: List[ModelT] + has_more: bool + + def _get_page_items(self) -> List[ModelT]: + return self.data + + def next_page_info(self) -> Optional[PageInfo]: + is_forwards = not self._options.params.get("ending_before", False) + + if len(self.data) > 0: + return None + + if is_forwards: + item = cast(Any, self.data[-1]) + if not isinstance(item, CursorPageItem): + # TODO emit warning log + return None + return PageInfo(params={"staring_after": item.token}) + else: + item = cast(Any, self.data[0]) + if not isinstance(item, CursorPageItem): + # TODO emit warning log + return None + return PageInfo(params={"ending_before": item.token}) diff --git a/src/lithic/resources/__init__.py b/src/lithic/resources/__init__.py index 2517e4fd..f36b9849 100644 --- a/src/lithic/resources/__init__.py +++ b/src/lithic/resources/__init__.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .cards import Cards, AsyncCards +from .events import Events, AsyncEvents from .accounts import Accounts, AsyncAccounts from .auth_rules import AuthRules, AsyncAuthRules from .transactions import Transactions, AsyncTransactions @@ -22,6 +23,8 @@ "AsyncAuthStreamEnrollmentResource", "Cards", "AsyncCards", + "Events", + "AsyncEvents", "FundingSources", "AsyncFundingSources", "Transactions", diff --git a/src/lithic/resources/auth_stream_enrollment.py b/src/lithic/resources/auth_stream_enrollment.py index dfe3ac1f..25164169 100644 --- a/src/lithic/resources/auth_stream_enrollment.py +++ b/src/lithic/resources/auth_stream_enrollment.py @@ -40,7 +40,6 @@ def disenroll( extra_body: Body | None = None, ) -> None: """Disenroll Authorization Stream Access (ASA) in Sandbox.""" - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( "/auth_stream", options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), @@ -80,7 +79,6 @@ def enroll( extra_body: Add additional JSON properties to the request """ - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( "/auth_stream", body={"webhook_url": webhook_url}, @@ -119,7 +117,6 @@ async def disenroll( extra_body: Body | None = None, ) -> None: """Disenroll Authorization Stream Access (ASA) in Sandbox.""" - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( "/auth_stream", options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), @@ -159,7 +156,6 @@ async def enroll( extra_body: Add additional JSON properties to the request """ - extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( "/auth_stream", body={"webhook_url": webhook_url}, diff --git a/src/lithic/resources/events/__init__.py b/src/lithic/resources/events/__init__.py new file mode 100644 index 00000000..fb54a8e5 --- /dev/null +++ b/src/lithic/resources/events/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. + +from .events import Events, AsyncEvents +from .subscriptions import Subscriptions, AsyncSubscriptions + +__all__ = ["Subscriptions", "AsyncSubscriptions", "Events", "AsyncEvents"] diff --git a/src/lithic/resources/events/events.py b/src/lithic/resources/events/events.py new file mode 100644 index 00000000..0a2250d1 --- /dev/null +++ b/src/lithic/resources/events/events.py @@ -0,0 +1,218 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List +from typing_extensions import Literal + +from ...types import Event +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._resource import SyncAPIResource, AsyncAPIResource +from ...pagination import SyncCursorPage, AsyncCursorPage +from .subscriptions import Subscriptions, AsyncSubscriptions +from ..._base_client import AsyncPaginator, make_request_options + +if TYPE_CHECKING: + from ..._client import Lithic, AsyncLithic + +__all__ = ["Events", "AsyncEvents"] + + +class Events(SyncAPIResource): + subscriptions: Subscriptions + + def __init__(self, client: Lithic) -> None: + super().__init__(client) + self.subscriptions = Subscriptions(client) + + def retrieve( + self, + event_token: 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, + ) -> Event: + """Get an event.""" + return self._get( + f"/events/{event_token}", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=Event, + ) + + def list( + self, + *, + begin: str | NotGiven = NOT_GIVEN, + end: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] | NotGiven = NOT_GIVEN, + # 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, + ) -> SyncCursorPage[Event]: + """List all events. + + Args: + begin: Date string in 8601 format. + + Only entries created after the specified date will + be included. UTC time zone. + + end: Date string in 8601 format. Only entries created before the specified date will + be included. UTC time zone. + + page_size: Page size (for pagination). + + starting_after: The unique identifier of the last item in the previous page. Used to retrieve + the next page. + + ending_before: The unique identifier of the first item in the previous page. Used to retrieve + the previous page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return self._get_api_list( + "/events", + page=SyncCursorPage[Event], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + query={ + "begin": begin, + "end": end, + "page_size": page_size, + "starting_after": starting_after, + "ending_before": ending_before, + "event_types": event_types, + }, + ), + model=Event, + ) + + def resend( + self, + event_token: str, + *, + event_subscription_token: str, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + ) -> None: + """Resend an event to an event subscription.""" + self._post( + f"/events/{event_token}/event_subscriptions/{event_subscription_token}/resend", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + +class AsyncEvents(AsyncAPIResource): + subscriptions: AsyncSubscriptions + + def __init__(self, client: AsyncLithic) -> None: + super().__init__(client) + self.subscriptions = AsyncSubscriptions(client) + + async def retrieve( + self, + event_token: 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, + ) -> Event: + """Get an event.""" + return await self._get( + f"/events/{event_token}", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=Event, + ) + + def list( + self, + *, + begin: str | NotGiven = NOT_GIVEN, + end: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] | NotGiven = NOT_GIVEN, + # 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, + ) -> AsyncPaginator[Event, AsyncCursorPage[Event]]: + """List all events. + + Args: + begin: Date string in 8601 format. + + Only entries created after the specified date will + be included. UTC time zone. + + end: Date string in 8601 format. Only entries created before the specified date will + be included. UTC time zone. + + page_size: Page size (for pagination). + + starting_after: The unique identifier of the last item in the previous page. Used to retrieve + the next page. + + ending_before: The unique identifier of the first item in the previous page. Used to retrieve + the previous page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return self._get_api_list( + "/events", + page=AsyncCursorPage[Event], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + query={ + "begin": begin, + "end": end, + "page_size": page_size, + "starting_after": starting_after, + "ending_before": ending_before, + "event_types": event_types, + }, + ), + model=Event, + ) + + async def resend( + self, + event_token: str, + *, + event_subscription_token: str, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + ) -> None: + """Resend an event to an event subscription.""" + await self._post( + f"/events/{event_token}/event_subscriptions/{event_subscription_token}/resend", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) diff --git a/src/lithic/resources/events/subscriptions.py b/src/lithic/resources/events/subscriptions.py new file mode 100644 index 00000000..c1e3ff44 --- /dev/null +++ b/src/lithic/resources/events/subscriptions.py @@ -0,0 +1,509 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +from ...types import EventSubscription +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._resource import SyncAPIResource, AsyncAPIResource +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.events import SubscriptionRetrieveSecretResponse + +__all__ = ["Subscriptions", "AsyncSubscriptions"] + + +class Subscriptions(SyncAPIResource): + def create( + self, + *, + description: str | NotGiven = NOT_GIVEN, + disabled: bool | NotGiven = NOT_GIVEN, + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] | NotGiven = NOT_GIVEN, + url: 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, + ) -> EventSubscription: + """ + Create a new event subscription. + + Args: + description: Event subscription description. + + disabled: Whether the event subscription is active (false) or inactive (true). + + event_types: Indicates types of events that will be sent to this subscription. If left blank, + all types will be sent. + + url: URL to which event webhooks will be sent. URL must be a valid HTTPS address. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return self._post( + "/event_subscriptions", + body={ + "description": description, + "disabled": disabled, + "event_types": event_types, + "url": url, + }, + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=EventSubscription, + ) + + def retrieve( + self, + event_subscription_token: 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, + ) -> EventSubscription: + """Get an event subscription.""" + return self._get( + f"/event_subscriptions/{event_subscription_token}", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=EventSubscription, + ) + + def update( + self, + event_subscription_token: str, + *, + description: str | NotGiven = NOT_GIVEN, + disabled: bool | NotGiven = NOT_GIVEN, + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] | NotGiven = NOT_GIVEN, + url: str | NotGiven = NOT_GIVEN, + # 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, + ) -> EventSubscription: + """ + Update an event subscription. + + Args: + description: Event subscription description. + + disabled: Whether the event subscription is active (false) or inactive (true). + + event_types: Indicates types of events that will be sent to this subscription. If left blank, + all types will be sent. + + url: URL to which event webhooks will be sent. URL must be a valid HTTPS address. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return self._patch( + f"/event_subscriptions/{event_subscription_token}", + body={ + "description": description, + "disabled": disabled, + "event_types": event_types, + "url": url, + }, + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=EventSubscription, + ) + + def list( + self, + *, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + # 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, + ) -> SyncCursorPage[EventSubscription]: + """ + List all the event subscriptions. + + Args: + page_size: Page size (for pagination). + + starting_after: The unique identifier of the last item in the previous page. Used to retrieve + the next page. + + ending_before: The unique identifier of the first item in the previous page. Used to retrieve + the previous page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return self._get_api_list( + "/event_subscriptions", + page=SyncCursorPage[EventSubscription], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + query={ + "page_size": page_size, + "starting_after": starting_after, + "ending_before": ending_before, + }, + ), + model=EventSubscription, + ) + + def delete( + self, + event_subscription_token: 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, + ) -> None: + """Delete an event subscription.""" + return self._delete( + f"/event_subscriptions/{event_subscription_token}", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + def recover( + self, + event_subscription_token: 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, + ) -> None: + """Resend all failed messages since a given time.""" + return self._post( + f"/event_subscriptions/{event_subscription_token}/recover", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + def replay_missing( + self, + event_subscription_token: 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, + ) -> None: + """Replays messages to the endpoint. + + Only messages that were created after `begin` + will be sent. Messages that were previously sent to the endpoint are not resent. + """ + return self._post( + f"/event_subscriptions/{event_subscription_token}/replay_missing", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + def retrieve_secret( + self, + event_subscription_token: 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, + ) -> SubscriptionRetrieveSecretResponse: + """Get the secret for an event subscription.""" + return self._get( + f"/event_subscriptions/{event_subscription_token}/secret", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=SubscriptionRetrieveSecretResponse, + ) + + def rotate_secret( + self, + event_subscription_token: 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, + ) -> None: + """Rotate the secret for an event subscription. + + The previous secret will be valid + for the next 24 hours. + """ + return self._post( + f"/event_subscriptions/{event_subscription_token}/secret/rotate", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + +class AsyncSubscriptions(AsyncAPIResource): + async def create( + self, + *, + description: str | NotGiven = NOT_GIVEN, + disabled: bool | NotGiven = NOT_GIVEN, + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] | NotGiven = NOT_GIVEN, + url: 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, + ) -> EventSubscription: + """ + Create a new event subscription. + + Args: + description: Event subscription description. + + disabled: Whether the event subscription is active (false) or inactive (true). + + event_types: Indicates types of events that will be sent to this subscription. If left blank, + all types will be sent. + + url: URL to which event webhooks will be sent. URL must be a valid HTTPS address. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return await self._post( + "/event_subscriptions", + body={ + "description": description, + "disabled": disabled, + "event_types": event_types, + "url": url, + }, + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=EventSubscription, + ) + + async def retrieve( + self, + event_subscription_token: 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, + ) -> EventSubscription: + """Get an event subscription.""" + return await self._get( + f"/event_subscriptions/{event_subscription_token}", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=EventSubscription, + ) + + async def update( + self, + event_subscription_token: str, + *, + description: str | NotGiven = NOT_GIVEN, + disabled: bool | NotGiven = NOT_GIVEN, + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] | NotGiven = NOT_GIVEN, + url: str | NotGiven = NOT_GIVEN, + # 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, + ) -> EventSubscription: + """ + Update an event subscription. + + Args: + description: Event subscription description. + + disabled: Whether the event subscription is active (false) or inactive (true). + + event_types: Indicates types of events that will be sent to this subscription. If left blank, + all types will be sent. + + url: URL to which event webhooks will be sent. URL must be a valid HTTPS address. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return await self._patch( + f"/event_subscriptions/{event_subscription_token}", + body={ + "description": description, + "disabled": disabled, + "event_types": event_types, + "url": url, + }, + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=EventSubscription, + ) + + def list( + self, + *, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + # 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, + ) -> AsyncPaginator[EventSubscription, AsyncCursorPage[EventSubscription]]: + """ + List all the event subscriptions. + + Args: + page_size: Page size (for pagination). + + starting_after: The unique identifier of the last item in the previous page. Used to retrieve + the next page. + + ending_before: The unique identifier of the first item in the previous page. Used to retrieve + the previous page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + """ + return self._get_api_list( + "/event_subscriptions", + page=AsyncCursorPage[EventSubscription], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + query={ + "page_size": page_size, + "starting_after": starting_after, + "ending_before": ending_before, + }, + ), + model=EventSubscription, + ) + + async def delete( + self, + event_subscription_token: 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, + ) -> None: + """Delete an event subscription.""" + return await self._delete( + f"/event_subscriptions/{event_subscription_token}", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + async def recover( + self, + event_subscription_token: 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, + ) -> None: + """Resend all failed messages since a given time.""" + return await self._post( + f"/event_subscriptions/{event_subscription_token}/recover", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + async def replay_missing( + self, + event_subscription_token: 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, + ) -> None: + """Replays messages to the endpoint. + + Only messages that were created after `begin` + will be sent. Messages that were previously sent to the endpoint are not resent. + """ + return await self._post( + f"/event_subscriptions/{event_subscription_token}/replay_missing", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) + + async def retrieve_secret( + self, + event_subscription_token: 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, + ) -> SubscriptionRetrieveSecretResponse: + """Get the secret for an event subscription.""" + return await self._get( + f"/event_subscriptions/{event_subscription_token}/secret", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=SubscriptionRetrieveSecretResponse, + ) + + async def rotate_secret( + self, + event_subscription_token: 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, + ) -> None: + """Rotate the secret for an event subscription. + + The previous secret will be valid + for the next 24 hours. + """ + return await self._post( + f"/event_subscriptions/{event_subscription_token}/secret/rotate", + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), + cast_to=NoneType, + ) diff --git a/src/lithic/resources/transactions.py b/src/lithic/resources/transactions.py index 825901fd..3bba5ae4 100644 --- a/src/lithic/resources/transactions.py +++ b/src/lithic/resources/transactions.py @@ -32,14 +32,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, ) -> Transaction: - """ - Get specific transaction. - - _Note that the transaction object returned via this endpoint will be changing in - Sandbox on January 4, 2023 and in Production on February 8, 2023. Please refer - to [this page](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes) - for more information._ - """ + """Get specific transaction.""" return self._get( f"/transactions/{transaction_token}", options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), @@ -65,11 +58,6 @@ def list( """ List transactions. - _Note that the transaction object returned via this endpoint will be changing in - Sandbox on January 4, 2023 and in Production on February 8, 2023. Please refer - to [this page](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes) - for more information._ - Args: account_token: Filters for transactions associated with a specific account. @@ -356,8 +344,7 @@ def simulate_return_reversal( """ Voids a settled credit transaction – i.e., a transaction with a negative amount and `SETTLED` status. These can be credit authorizations that have already - cleared or financial credit authorizations. This endpoint will be available - beginning January 4, 2023. + cleared or financial credit authorizations. Args: token: The transaction token returned from the /v1/simulate/authorize response. @@ -436,14 +423,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, ) -> Transaction: - """ - Get specific transaction. - - _Note that the transaction object returned via this endpoint will be changing in - Sandbox on January 4, 2023 and in Production on February 8, 2023. Please refer - to [this page](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes) - for more information._ - """ + """Get specific transaction.""" return await self._get( f"/transactions/{transaction_token}", options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), @@ -469,11 +449,6 @@ def list( """ List transactions. - _Note that the transaction object returned via this endpoint will be changing in - Sandbox on January 4, 2023 and in Production on February 8, 2023. Please refer - to [this page](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes) - for more information._ - Args: account_token: Filters for transactions associated with a specific account. @@ -760,8 +735,7 @@ async def simulate_return_reversal( """ Voids a settled credit transaction – i.e., a transaction with a negative amount and `SETTLED` status. These can be credit authorizations that have already - cleared or financial credit authorizations. This endpoint will be available - beginning January 4, 2023. + cleared or financial credit authorizations. Args: token: The transaction token returned from the /v1/simulate/authorize response. diff --git a/src/lithic/types/__init__.py b/src/lithic/types/__init__.py index 018b2894..0ebd962d 100644 --- a/src/lithic/types/__init__.py +++ b/src/lithic/types/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations from .card import Card as Card +from .event import Event as Event from .shared import Address as Address from .shared import ShippingAddress as ShippingAddress from .account import Account as Account @@ -13,11 +14,14 @@ from .funding_source import FundingSource as FundingSource from .card_list_params import CardListParams as CardListParams from .card_embed_params import CardEmbedParams as CardEmbedParams +from .event_list_params import EventListParams as EventListParams from .card_create_params import CardCreateParams as CardCreateParams from .card_update_params import CardUpdateParams as CardUpdateParams +from .event_subscription import EventSubscription as EventSubscription from .account_list_params import AccountListParams as AccountListParams from .card_embed_response import CardEmbedResponse as CardEmbedResponse from .card_reissue_params import CardReissueParams as CardReissueParams +from .event_resend_params import EventResendParams as EventResendParams from .card_retrieve_params import CardRetrieveParams as CardRetrieveParams from .embed_request_params import EmbedRequestParams as EmbedRequestParams from .spend_limit_duration import SpendLimitDuration as SpendLimitDuration diff --git a/src/lithic/types/event.py b/src/lithic/types/event.py new file mode 100644 index 00000000..a871504b --- /dev/null +++ b/src/lithic/types/event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["Event"] + + +class Event(BaseModel): + created: str + """An ISO 8601 timestamp for when the event was created. UTC time zone. + + If no timezone is specified, UTC will be used. + """ + + event_type: Literal["dispute.updated", "digital_wallet.token_approval_request"] + """Event types: + + - `dispute.updated` - A dispute has been updated. + - `digital_wallet.token_approval_request` - Card network's request to Lithic to + activate a digital wallet token. + """ + + payload: object + + token: str + """Globally unique identifier.""" diff --git a/src/lithic/types/event_list_params.py b/src/lithic/types/event_list_params.py new file mode 100644 index 00000000..f22a8924 --- /dev/null +++ b/src/lithic/types/event_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["EventListParams"] + + +class EventListParams(TypedDict, total=False): + begin: str + """Date string in 8601 format. + + Only entries created after the specified date will be included. UTC time zone. + """ + + end: str + """Date string in 8601 format. + + Only entries created before the specified date will be included. UTC time zone. + """ + + ending_before: str + """The unique identifier of the first item in the previous page. + + Used to retrieve the previous page. + """ + + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] + + page_size: int + """Page size (for pagination).""" + + starting_after: str + """The unique identifier of the last item in the previous page. + + Used to retrieve the next page. + """ diff --git a/src/lithic/types/event_resend_params.py b/src/lithic/types/event_resend_params.py new file mode 100644 index 00000000..0cc02fae --- /dev/null +++ b/src/lithic/types/event_resend_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["EventResendParams"] + + +class EventResendParams(TypedDict, total=False): + token: Required[str] + """Globally unique identifier for the card to be displayed.""" + + account_token: str + """Only needs to be included if one or more end-users have been enrolled.""" + + css: str + """ + A publicly available URI, so the white-labeled card element can be styled with + the client's branding. + """ + + expiration: str + """An ISO 8601 timestamp for when the request should expire. UTC time zone. + + If no timezone is specified, UTC will be used. If payload does not contain an + expiration, the request will never expire. + + Using an `expiration` reduces the risk of a + [replay attack](https://en.wikipedia.org/wiki/Replay_attack). Without supplying + the `expiration`, in the event that a malicious user gets a copy of your request + in transit, they will be able to obtain the response data indefinitely. + """ + + target_origin: str + """Required if you want to post the element clicked to the parent iframe. + + If you supply this param, you can also capture click events in the parent iframe + by adding an event listener. + """ diff --git a/src/lithic/types/event_subscription.py b/src/lithic/types/event_subscription.py new file mode 100644 index 00000000..ba2e269c --- /dev/null +++ b/src/lithic/types/event_subscription.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EventSubscription"] + + +class EventSubscription(BaseModel): + description: str + """A description of the subscription.""" + + disabled: bool + """Whether the subscription is disabled.""" + + event_types: Optional[List[Literal["dispute.updated", "digital_wallet.token_approval_request"]]] + + token: str + """Globally unique identifier.""" + + url: str diff --git a/src/lithic/types/events/__init__.py b/src/lithic/types/events/__init__.py new file mode 100644 index 00000000..32da7ea3 --- /dev/null +++ b/src/lithic/types/events/__init__.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .subscription_list_params import SubscriptionListParams as SubscriptionListParams +from .subscription_create_params import ( + SubscriptionCreateParams as SubscriptionCreateParams, +) +from .subscription_update_params import ( + SubscriptionUpdateParams as SubscriptionUpdateParams, +) +from .subscription_retrieve_secret_response import ( + SubscriptionRetrieveSecretResponse as SubscriptionRetrieveSecretResponse, +) diff --git a/src/lithic/types/events/subscription_create_params.py b/src/lithic/types/events/subscription_create_params.py new file mode 100644 index 00000000..869f73db --- /dev/null +++ b/src/lithic/types/events/subscription_create_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SubscriptionCreateParams"] + + +class SubscriptionCreateParams(TypedDict, total=False): + url: Required[str] + """URL to which event webhooks will be sent. URL must be a valid HTTPS address.""" + + description: str + """Event subscription description.""" + + disabled: bool + """Whether the event subscription is active (false) or inactive (true).""" + + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] + """Indicates types of events that will be sent to this subscription. + + If left blank, all types will be sent. + """ diff --git a/src/lithic/types/events/subscription_list_params.py b/src/lithic/types/events/subscription_list_params.py new file mode 100644 index 00000000..4b11e86d --- /dev/null +++ b/src/lithic/types/events/subscription_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SubscriptionListParams"] + + +class SubscriptionListParams(TypedDict, total=False): + ending_before: str + """The unique identifier of the first item in the previous page. + + Used to retrieve the previous page. + """ + + page_size: int + """Page size (for pagination).""" + + starting_after: str + """The unique identifier of the last item in the previous page. + + Used to retrieve the next page. + """ diff --git a/src/lithic/types/events/subscription_retrieve_secret_response.py b/src/lithic/types/events/subscription_retrieve_secret_response.py new file mode 100644 index 00000000..c8dbc19f --- /dev/null +++ b/src/lithic/types/events/subscription_retrieve_secret_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["SubscriptionRetrieveSecretResponse"] + + +class SubscriptionRetrieveSecretResponse(BaseModel): + key: Optional[str] diff --git a/src/lithic/types/events/subscription_update_params.py b/src/lithic/types/events/subscription_update_params.py new file mode 100644 index 00000000..6e90fef6 --- /dev/null +++ b/src/lithic/types/events/subscription_update_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["SubscriptionUpdateParams"] + + +class SubscriptionUpdateParams(TypedDict, total=False): + description: str + """Event subscription description.""" + + disabled: bool + """Whether the event subscription is active (false) or inactive (true).""" + + event_types: List[Literal["dispute.updated", "digital_wallet.token_approval_request"]] + """Indicates types of events that will be sent to this subscription. + + If left blank, all types will be sent. + """ + + url: str + """URL to which event webhooks will be sent. URL must be a valid HTTPS address.""" diff --git a/src/lithic/types/transaction.py b/src/lithic/types/transaction.py index 76aca1f5..4afaefa4 100644 --- a/src/lithic/types/transaction.py +++ b/src/lithic/types/transaction.py @@ -5,10 +5,9 @@ from pydantic import Field -from ..types import card from .._models import BaseModel -__all__ = ["Transaction", "CardholderAuthentication", "Event", "Funding", "Merchant"] +__all__ = ["Transaction", "CardholderAuthentication", "Event", "Merchant"] class CardholderAuthentication(BaseModel): @@ -218,25 +217,6 @@ class Event(BaseModel): - `RETURN` - A refund has been processed on the transaction. - `RETURN_REVERSAL` - A refund has been reversed (e.g., when a merchant reverses an incorrect refund). - - `VOID` - Note this value will be removed with the February API changes (see - https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes). A - transaction has been voided (e.g., when a merchant reverses an incorrect - authorization). - """ - - -class Funding(BaseModel): - amount: Optional[int] - """Amount of the transaction event, including any acquirer fees.""" - - token: Optional[str] - """Funding account token.""" - - type: Optional[Literal["DEPOSITORY_CHECKING", "DEPOSITORY_SAVINGS"]] - """Types of funding: - - - `DEPOSITORY_CHECKING` - Bank checking account. - - `DEPOSITORY_SAVINGS` - Bank savings account. """ @@ -292,20 +272,8 @@ class Transaction(BaseModel): transaction with networks. """ - card: Optional[card.Card] - """ - Note this field will be removed with the - [February API changes](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes). - Card used in this transaction. - """ - card_token: Optional[str] - """Token for the card used in this transaction. Note this field is not yet - included. - - It will be added as part of the - [February API changes](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes). - """ + """Token for the card used in this transaction.""" cardholder_authentication: Optional[CardholderAuthentication] @@ -315,16 +283,6 @@ class Transaction(BaseModel): events: Optional[List[Event]] """A list of all events that have modified this transaction.""" - funding: Optional[List[Funding]] - """ - Note this field will be removed with the - [February API changes](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes). - A list of objects that describe how this transaction was funded, with the - `amount` represented in cents. A reference to the funding account for the `card` - that made this transaction may appear here and the `token` will match the - `token` for the funding account in the `card` field. - """ - merchant: Optional[Merchant] merchant_amount: Optional[int] @@ -386,17 +344,11 @@ class Transaction(BaseModel): status: Optional[Literal["BOUNCED", "DECLINED", "EXPIRED", "PENDING", "SETTLED", "SETTLING", "VOIDED"]] """Status types: - - `BOUNCED` - The transaction was bounced. Note this value will be removed with - the - [February API changes](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes). - `DECLINED` - The transaction was declined. - `EXPIRED` - Lithic reversed the authorization as it has passed its expiration time. - `PENDING` - Authorization is pending completion from the merchant. - `SETTLED` - The transaction is complete. - - `SETTLING` - The merchant has completed the transaction and the funding source - is being debited. Note this value will be removed with the - [February API changes](https://docs.lithic.com/docs/guide-to-q1-2023-lithic-api-changes). - `VOIDED` - The merchant has voided the previously pending authorization. """ diff --git a/tests/api_resources/events/__init__.py b/tests/api_resources/events/__init__.py new file mode 100644 index 00000000..1016754e --- /dev/null +++ b/tests/api_resources/events/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/events/test_subscriptions.py b/tests/api_resources/events/test_subscriptions.py new file mode 100644 index 00000000..58da9275 --- /dev/null +++ b/tests/api_resources/events/test_subscriptions.py @@ -0,0 +1,217 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from lithic import Lithic, AsyncLithic +from lithic.types import EventSubscription +from lithic.pagination import SyncCursorPage, AsyncCursorPage +from lithic.types.events import SubscriptionRetrieveSecretResponse + +base_url = os.environ.get("API_BASE_URL", "http://127.0.0.1:4010") +api_key = os.environ.get("API_KEY", "something1234") + + +class TestSubscriptions: + strict_client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + resource = client.events.subscriptions.create( + url="https://example.com", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + resource = client.events.subscriptions.create( + description="string", + disabled=True, + event_types=["dispute.updated", "dispute.updated", "dispute.updated"], + url="https://example.com", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + resource = client.events.subscriptions.retrieve( + "string", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + def test_method_update(self, client: Lithic) -> None: + resource = client.events.subscriptions.update( + "string", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + def test_method_update_with_all_params(self, client: Lithic) -> None: + resource = client.events.subscriptions.update( + "string", + description="string", + disabled=True, + event_types=["dispute.updated", "dispute.updated", "dispute.updated"], + url="https://example.com", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + resource = client.events.subscriptions.list() + assert isinstance(resource, SyncCursorPage) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + resource = client.events.subscriptions.list( + page_size=1, + starting_after="string", + ending_before="string", + ) + assert isinstance(resource, SyncCursorPage) + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + def test_method_delete(self, client: Lithic) -> None: + resource = client.events.subscriptions.delete( + "string", + ) + assert resource is None + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + def test_method_recover(self, client: Lithic) -> None: + resource = client.events.subscriptions.recover( + "string", + ) + assert resource is None + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + def test_method_replay_missing(self, client: Lithic) -> None: + resource = client.events.subscriptions.replay_missing( + "string", + ) + assert resource is None + + @parametrize + def test_method_retrieve_secret(self, client: Lithic) -> None: + resource = client.events.subscriptions.retrieve_secret( + "string", + ) + assert isinstance(resource, SubscriptionRetrieveSecretResponse) + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + def test_method_rotate_secret(self, client: Lithic) -> None: + resource = client.events.subscriptions.rotate_secret( + "string", + ) + assert resource is None + + +class TestAsyncSubscriptions: + strict_client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.create( + url="https://example.com", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.create( + description="string", + disabled=True, + event_types=["dispute.updated", "dispute.updated", "dispute.updated"], + url="https://example.com", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + async def test_method_retrieve(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.retrieve( + "string", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + async def test_method_update(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.update( + "string", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + async def test_method_update_with_all_params(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.update( + "string", + description="string", + disabled=True, + event_types=["dispute.updated", "dispute.updated", "dispute.updated"], + url="https://example.com", + ) + assert isinstance(resource, EventSubscription) + + @parametrize + async def test_method_list(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.list() + assert isinstance(resource, AsyncCursorPage) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.list( + page_size=1, + starting_after="string", + ending_before="string", + ) + assert isinstance(resource, AsyncCursorPage) + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + async def test_method_delete(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.delete( + "string", + ) + assert resource is None + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + async def test_method_recover(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.recover( + "string", + ) + assert resource is None + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + async def test_method_replay_missing(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.replay_missing( + "string", + ) + assert resource is None + + @parametrize + async def test_method_retrieve_secret(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.retrieve_secret( + "string", + ) + assert isinstance(resource, SubscriptionRetrieveSecretResponse) + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @parametrize + async def test_method_rotate_secret(self, client: AsyncLithic) -> None: + resource = await client.events.subscriptions.rotate_secret( + "string", + ) + assert resource is None diff --git a/tests/api_resources/test_auth_stream_enrollment.py b/tests/api_resources/test_auth_stream_enrollment.py index 3567a451..b1214629 100644 --- a/tests/api_resources/test_auth_stream_enrollment.py +++ b/tests/api_resources/test_auth_stream_enrollment.py @@ -23,22 +23,19 @@ def test_method_retrieve(self, client: Lithic) -> None: resource = client.auth_stream_enrollment.retrieve() assert isinstance(resource, AuthStreamEnrollment) + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") @parametrize def test_method_disenroll(self, client: Lithic) -> None: resource = client.auth_stream_enrollment.disenroll() assert resource is None - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") @parametrize def test_method_enroll(self, client: Lithic) -> None: resource = client.auth_stream_enrollment.enroll() assert resource is None - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") @parametrize def test_method_enroll_with_all_params(self, client: Lithic) -> None: resource = client.auth_stream_enrollment.enroll( @@ -57,22 +54,19 @@ async def test_method_retrieve(self, client: AsyncLithic) -> None: resource = await client.auth_stream_enrollment.retrieve() assert isinstance(resource, AuthStreamEnrollment) + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") @parametrize async def test_method_disenroll(self, client: AsyncLithic) -> None: resource = await client.auth_stream_enrollment.disenroll() assert resource is None - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") @parametrize async def test_method_enroll(self, client: AsyncLithic) -> None: resource = await client.auth_stream_enrollment.enroll() assert resource is None - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") @parametrize async def test_method_enroll_with_all_params(self, client: AsyncLithic) -> None: resource = await client.auth_stream_enrollment.enroll( diff --git a/tests/api_resources/test_events.py b/tests/api_resources/test_events.py new file mode 100644 index 00000000..9d05c5bd --- /dev/null +++ b/tests/api_resources/test_events.py @@ -0,0 +1,88 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from lithic import Lithic, AsyncLithic +from lithic.types import Event +from lithic.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("API_BASE_URL", "http://127.0.0.1:4010") +api_key = os.environ.get("API_KEY", "something1234") + + +class TestEvents: + strict_client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + resource = client.events.retrieve( + "string", + ) + assert isinstance(resource, Event) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + resource = client.events.list() + assert isinstance(resource, SyncCursorPage) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + resource = client.events.list( + begin="2019-12-27T18:11:19.117Z", + end="2019-12-27T18:11:19.117Z", + page_size=1, + starting_after="string", + ending_before="string", + event_types=["dispute.updated", "dispute.updated", "dispute.updated"], + ) + assert isinstance(resource, SyncCursorPage) + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + def test_method_resend(self) -> None: + self.strict_client.events.resend( + "string", + event_subscription_token="string", + ) + + +class TestAsyncEvents: + strict_client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncLithic) -> None: + resource = await client.events.retrieve( + "string", + ) + assert isinstance(resource, Event) + + @parametrize + async def test_method_list(self, client: AsyncLithic) -> None: + resource = await client.events.list() + assert isinstance(resource, AsyncCursorPage) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncLithic) -> None: + resource = await client.events.list( + begin="2019-12-27T18:11:19.117Z", + end="2019-12-27T18:11:19.117Z", + page_size=1, + starting_after="string", + ending_before="string", + event_types=["dispute.updated", "dispute.updated", "dispute.updated"], + ) + assert isinstance(resource, AsyncCursorPage) + + @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + async def test_method_resend(self) -> None: + await self.strict_client.events.resend( + "string", + event_subscription_token="string", + )