diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7325798..fbd9082 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.4.1" + ".": "1.5.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 56f5a81..49508b3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 17 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-56b0f699c5437d9e5326626d35dfc972c17d01f12cb416c7f4854c8ea6d0e95e.yml -openapi_spec_hash: 158f405c1880706266d83e6ff16b9d2f -config_hash: 41c337f5cda03b13880617490f82bad0 +configured_endpoints: 21 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-d9763d006969b49a1473851069fdfa429eb13133b64103a62963bb70ddb22305.yml +openapi_spec_hash: 6aee689b7a759b12c85c088c15e29bc0 +config_hash: 4ab3e1ee76a463e0ed214541260ee12e diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d9a73..1fe0243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 1.5.0 (2026-02-23) + +Full Changelog: [v1.4.1...v1.5.0](https://github.com/CASParser/cas-parser-python/compare/v1.4.1...v1.5.0) + +### Features + +* **api:** api update ([8a5eeaa](https://github.com/CASParser/cas-parser-python/commit/8a5eeaa3334343c0a5e8268d06c0c25d7dfdef00)) +* **api:** api update ([478ce0c](https://github.com/CASParser/cas-parser-python/commit/478ce0c2535de083e5c6f9248d0df77e6bafb410)) +* **api:** api update ([dcf866c](https://github.com/CASParser/cas-parser-python/commit/dcf866c9577a219abb273e5b34e2c95bda3404f2)) +* **api:** manual updates ([a4d6336](https://github.com/CASParser/cas-parser-python/commit/a4d6336829387e982ac938935252129ff5131f65)) + ## 1.4.1 (2026-02-20) Full Changelog: [v1.4.0...v1.4.1](https://github.com/CASParser/cas-parser-python/compare/v1.4.0...v1.4.1) diff --git a/api.md b/api.md index d92590b..164b61d 100644 --- a/api.md +++ b/api.md @@ -8,7 +8,7 @@ from cas_parser.types import CreditCheckResponse Methods: -- client.credits.check() -> CreditCheckResponse +- client.credits.check() -> CreditCheckResponse # Logs @@ -20,8 +20,8 @@ from cas_parser.types import LogCreateResponse, LogGetSummaryResponse Methods: -- client.logs.create(\*\*params) -> LogCreateResponse -- client.logs.get_summary(\*\*params) -> LogGetSummaryResponse +- client.logs.create(\*\*params) -> LogCreateResponse +- client.logs.get_summary(\*\*params) -> LogGetSummaryResponse # AccessToken @@ -33,7 +33,7 @@ from cas_parser.types import AccessTokenCreateResponse Methods: -- client.access_token.create(\*\*params) -> AccessTokenCreateResponse +- client.access_token.create(\*\*params) -> AccessTokenCreateResponse # VerifyToken @@ -45,7 +45,7 @@ from cas_parser.types import VerifyTokenVerifyResponse Methods: -- client.verify_token.verify() -> VerifyTokenVerifyResponse +- client.verify_token.verify() -> VerifyTokenVerifyResponse # CamsKfintech @@ -133,3 +133,23 @@ Methods: Methods: - client.smart.parse_cas_pdf(\*\*params) -> UnifiedResponse + +# InboundEmail + +Types: + +```python +from cas_parser.types import ( + InboundEmailCreateResponse, + InboundEmailRetrieveResponse, + InboundEmailListResponse, + InboundEmailDeleteResponse, +) +``` + +Methods: + +- client.inbound_email.create(\*\*params) -> InboundEmailCreateResponse +- client.inbound_email.retrieve(inbound_email_id) -> InboundEmailRetrieveResponse +- client.inbound_email.list(\*\*params) -> InboundEmailListResponse +- client.inbound_email.delete(inbound_email_id) -> InboundEmailDeleteResponse diff --git a/pyproject.toml b/pyproject.toml index e9c0aea..4e79dc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cas-parser-python" -version = "1.4.1" +version = "1.5.0" description = "The official Python library for the cas-parser API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/cas_parser/_client.py b/src/cas_parser/_client.py index 02dc18a..e63f97f 100644 --- a/src/cas_parser/_client.py +++ b/src/cas_parser/_client.py @@ -43,6 +43,7 @@ verify_token, cams_kfintech, contract_note, + inbound_email, ) from .resources.logs import LogsResource, AsyncLogsResource from .resources.nsdl import NsdlResource, AsyncNsdlResource @@ -55,6 +56,7 @@ from .resources.verify_token import VerifyTokenResource, AsyncVerifyTokenResource from .resources.cams_kfintech import CamsKfintechResource, AsyncCamsKfintechResource from .resources.contract_note import ContractNoteResource, AsyncContractNoteResource + from .resources.inbound_email import InboundEmailResource, AsyncInboundEmailResource __all__ = [ "ENVIRONMENTS", @@ -220,6 +222,12 @@ def smart(self) -> SmartResource: return SmartResource(self) + @cached_property + def inbound_email(self) -> InboundEmailResource: + from .resources.inbound_email import InboundEmailResource + + return InboundEmailResource(self) + @cached_property def with_raw_response(self) -> CasParserWithRawResponse: return CasParserWithRawResponse(self) @@ -480,6 +488,12 @@ def smart(self) -> AsyncSmartResource: return AsyncSmartResource(self) + @cached_property + def inbound_email(self) -> AsyncInboundEmailResource: + from .resources.inbound_email import AsyncInboundEmailResource + + return AsyncInboundEmailResource(self) + @cached_property def with_raw_response(self) -> AsyncCasParserWithRawResponse: return AsyncCasParserWithRawResponse(self) @@ -667,6 +681,12 @@ def smart(self) -> smart.SmartResourceWithRawResponse: return SmartResourceWithRawResponse(self._client.smart) + @cached_property + def inbound_email(self) -> inbound_email.InboundEmailResourceWithRawResponse: + from .resources.inbound_email import InboundEmailResourceWithRawResponse + + return InboundEmailResourceWithRawResponse(self._client.inbound_email) + class AsyncCasParserWithRawResponse: _client: AsyncCasParser @@ -740,6 +760,12 @@ def smart(self) -> smart.AsyncSmartResourceWithRawResponse: return AsyncSmartResourceWithRawResponse(self._client.smart) + @cached_property + def inbound_email(self) -> inbound_email.AsyncInboundEmailResourceWithRawResponse: + from .resources.inbound_email import AsyncInboundEmailResourceWithRawResponse + + return AsyncInboundEmailResourceWithRawResponse(self._client.inbound_email) + class CasParserWithStreamedResponse: _client: CasParser @@ -813,6 +839,12 @@ def smart(self) -> smart.SmartResourceWithStreamingResponse: return SmartResourceWithStreamingResponse(self._client.smart) + @cached_property + def inbound_email(self) -> inbound_email.InboundEmailResourceWithStreamingResponse: + from .resources.inbound_email import InboundEmailResourceWithStreamingResponse + + return InboundEmailResourceWithStreamingResponse(self._client.inbound_email) + class AsyncCasParserWithStreamedResponse: _client: AsyncCasParser @@ -886,6 +918,12 @@ def smart(self) -> smart.AsyncSmartResourceWithStreamingResponse: return AsyncSmartResourceWithStreamingResponse(self._client.smart) + @cached_property + def inbound_email(self) -> inbound_email.AsyncInboundEmailResourceWithStreamingResponse: + from .resources.inbound_email import AsyncInboundEmailResourceWithStreamingResponse + + return AsyncInboundEmailResourceWithStreamingResponse(self._client.inbound_email) + Client = CasParser diff --git a/src/cas_parser/_version.py b/src/cas_parser/_version.py index eaa6152..e73e451 100644 --- a/src/cas_parser/_version.py +++ b/src/cas_parser/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "cas_parser" -__version__ = "1.4.1" # x-release-please-version +__version__ = "1.5.0" # x-release-please-version diff --git a/src/cas_parser/resources/__init__.py b/src/cas_parser/resources/__init__.py index ac91596..34f05f7 100644 --- a/src/cas_parser/resources/__init__.py +++ b/src/cas_parser/resources/__init__.py @@ -88,6 +88,14 @@ ContractNoteResourceWithStreamingResponse, AsyncContractNoteResourceWithStreamingResponse, ) +from .inbound_email import ( + InboundEmailResource, + AsyncInboundEmailResource, + InboundEmailResourceWithRawResponse, + AsyncInboundEmailResourceWithRawResponse, + InboundEmailResourceWithStreamingResponse, + AsyncInboundEmailResourceWithStreamingResponse, +) __all__ = [ "CreditsResource", @@ -156,4 +164,10 @@ "AsyncSmartResourceWithRawResponse", "SmartResourceWithStreamingResponse", "AsyncSmartResourceWithStreamingResponse", + "InboundEmailResource", + "AsyncInboundEmailResource", + "InboundEmailResourceWithRawResponse", + "AsyncInboundEmailResourceWithRawResponse", + "InboundEmailResourceWithStreamingResponse", + "AsyncInboundEmailResourceWithStreamingResponse", ] diff --git a/src/cas_parser/resources/access_token.py b/src/cas_parser/resources/access_token.py index aa92680..bfda93b 100644 --- a/src/cas_parser/resources/access_token.py +++ b/src/cas_parser/resources/access_token.py @@ -58,6 +58,8 @@ def create( **Use this endpoint from your backend** to create tokens that can be safely passed to frontend/SDK. + **Legacy path:** `/v1/access-token` (still supported) + Access tokens: - Are prefixed with `at_` for easy identification @@ -77,7 +79,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/v1/access-token", + "/v1/token", body=maybe_transform( {"expiry_minutes": expiry_minutes}, access_token_create_params.AccessTokenCreateParams ), @@ -125,6 +127,8 @@ async def create( **Use this endpoint from your backend** to create tokens that can be safely passed to frontend/SDK. + **Legacy path:** `/v1/access-token` (still supported) + Access tokens: - Are prefixed with `at_` for easy identification @@ -144,7 +148,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/v1/access-token", + "/v1/token", body=await async_maybe_transform( {"expiry_minutes": expiry_minutes}, access_token_create_params.AccessTokenCreateParams ), diff --git a/src/cas_parser/resources/credits.py b/src/cas_parser/resources/credits.py index 4a86474..264693d 100644 --- a/src/cas_parser/resources/credits.py +++ b/src/cas_parser/resources/credits.py @@ -61,7 +61,7 @@ def check( Credits reset at the start of each billing period. """ return self._post( - "/credits", + "/v1/credits", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -111,7 +111,7 @@ async def check( Credits reset at the start of each billing period. """ return await self._post( - "/credits", + "/v1/credits", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/cas_parser/resources/inbound_email.py b/src/cas_parser/resources/inbound_email.py new file mode 100644 index 0000000..e20ae7b --- /dev/null +++ b/src/cas_parser/resources/inbound_email.py @@ -0,0 +1,563 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List +from typing_extensions import Literal + +import httpx + +from ..types import inbound_email_list_params, inbound_email_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.inbound_email_list_response import InboundEmailListResponse +from ..types.inbound_email_create_response import InboundEmailCreateResponse +from ..types.inbound_email_delete_response import InboundEmailDeleteResponse +from ..types.inbound_email_retrieve_response import InboundEmailRetrieveResponse + +__all__ = ["InboundEmailResource", "AsyncInboundEmailResource"] + + +class InboundEmailResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InboundEmailResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/CASParser/cas-parser-python#accessing-raw-response-data-eg-headers + """ + return InboundEmailResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InboundEmailResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/CASParser/cas-parser-python#with_streaming_response + """ + return InboundEmailResourceWithStreamingResponse(self) + + def create( + self, + *, + callback_url: str, + alias: str | Omit = omit, + allowed_sources: List[Literal["cdsl", "nsdl", "cams", "kfintech"]] | Omit = omit, + metadata: Dict[str, str] | Omit = omit, + reference: str | Omit = omit, + # 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, + ) -> InboundEmailCreateResponse: + """ + Create a dedicated inbound email address for collecting CAS statements via email + forwarding. + + **How it works:** + + 1. Create an inbound email with your webhook URL + 2. Display the email address to your user (e.g., "Forward your CAS to + ie_xxx@import.casparser.in") + 3. When an investor forwards a CAS email, we verify the sender and deliver to + your webhook + + **Webhook Delivery:** + + - We POST to your `callback_url` with JSON body containing files (matching + EmailCASFile schema) + - Failed deliveries are retried automatically with exponential backoff + + **Inactivity:** + + - Inbound emails with no activity in 30 days are marked inactive + - Active inbound emails remain operational indefinitely + + Args: + callback_url: Webhook URL where we POST email notifications. Must be HTTPS in production (HTTP + allowed for localhost during development). + + alias: Optional custom email prefix for user-friendly addresses. + + - Must be 3-32 characters + - Alphanumeric + hyphens only + - Must start and end with letter/number + - Example: `john-portfolio@import.casparser.in` + - If omitted, generates random ID like `ie_abc123xyz@import.casparser.in` + + allowed_sources: Filter emails by CAS provider. If omitted, accepts all providers. + + - `cdsl` → eCAS@cdslstatement.com + - `nsdl` → NSDL-CAS@nsdl.co.in + - `cams` → donotreply@camsonline.com + - `kfintech` → samfS@kfintech.com + + metadata: Optional key-value pairs (max 10) to include in webhook payload. Useful for + passing context like plan_type, campaign_id, etc. + + reference: Your internal identifier (e.g., user_id, account_id). Returned in webhook + payload for correlation. + + 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 + """ + return self._post( + "/v4/inbound-email", + body=maybe_transform( + { + "callback_url": callback_url, + "alias": alias, + "allowed_sources": allowed_sources, + "metadata": metadata, + "reference": reference, + }, + inbound_email_create_params.InboundEmailCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InboundEmailCreateResponse, + ) + + def retrieve( + self, + inbound_email_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, + ) -> InboundEmailRetrieveResponse: + """ + Retrieve details of a specific mailbox including statistics. + + 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 + """ + if not inbound_email_id: + raise ValueError(f"Expected a non-empty value for `inbound_email_id` but received {inbound_email_id!r}") + return self._get( + f"/v4/inbound-email/{inbound_email_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InboundEmailRetrieveResponse, + ) + + def list( + self, + *, + limit: int | Omit = omit, + offset: int | Omit = omit, + status: Literal["active", "paused", "all"] | Omit = omit, + # 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, + ) -> InboundEmailListResponse: + """List all mailboxes associated with your API key. + + Returns active and inactive + mailboxes (deleted mailboxes are excluded). + + Args: + limit: Maximum number of inbound emails to return + + offset: Pagination offset + + status: Filter by status + + 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 + """ + return self._get( + "/v4/inbound-email", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + "status": status, + }, + inbound_email_list_params.InboundEmailListParams, + ), + ), + cast_to=InboundEmailListResponse, + ) + + def delete( + self, + inbound_email_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, + ) -> InboundEmailDeleteResponse: + """Permanently delete an inbound email address. + + It will stop accepting emails. + + **Note:** Deletion is immediate and cannot be undone. Any emails received after + deletion will be rejected. + + 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 + """ + if not inbound_email_id: + raise ValueError(f"Expected a non-empty value for `inbound_email_id` but received {inbound_email_id!r}") + return self._delete( + f"/v4/inbound-email/{inbound_email_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InboundEmailDeleteResponse, + ) + + +class AsyncInboundEmailResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInboundEmailResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/CASParser/cas-parser-python#accessing-raw-response-data-eg-headers + """ + return AsyncInboundEmailResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInboundEmailResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/CASParser/cas-parser-python#with_streaming_response + """ + return AsyncInboundEmailResourceWithStreamingResponse(self) + + async def create( + self, + *, + callback_url: str, + alias: str | Omit = omit, + allowed_sources: List[Literal["cdsl", "nsdl", "cams", "kfintech"]] | Omit = omit, + metadata: Dict[str, str] | Omit = omit, + reference: str | Omit = omit, + # 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, + ) -> InboundEmailCreateResponse: + """ + Create a dedicated inbound email address for collecting CAS statements via email + forwarding. + + **How it works:** + + 1. Create an inbound email with your webhook URL + 2. Display the email address to your user (e.g., "Forward your CAS to + ie_xxx@import.casparser.in") + 3. When an investor forwards a CAS email, we verify the sender and deliver to + your webhook + + **Webhook Delivery:** + + - We POST to your `callback_url` with JSON body containing files (matching + EmailCASFile schema) + - Failed deliveries are retried automatically with exponential backoff + + **Inactivity:** + + - Inbound emails with no activity in 30 days are marked inactive + - Active inbound emails remain operational indefinitely + + Args: + callback_url: Webhook URL where we POST email notifications. Must be HTTPS in production (HTTP + allowed for localhost during development). + + alias: Optional custom email prefix for user-friendly addresses. + + - Must be 3-32 characters + - Alphanumeric + hyphens only + - Must start and end with letter/number + - Example: `john-portfolio@import.casparser.in` + - If omitted, generates random ID like `ie_abc123xyz@import.casparser.in` + + allowed_sources: Filter emails by CAS provider. If omitted, accepts all providers. + + - `cdsl` → eCAS@cdslstatement.com + - `nsdl` → NSDL-CAS@nsdl.co.in + - `cams` → donotreply@camsonline.com + - `kfintech` → samfS@kfintech.com + + metadata: Optional key-value pairs (max 10) to include in webhook payload. Useful for + passing context like plan_type, campaign_id, etc. + + reference: Your internal identifier (e.g., user_id, account_id). Returned in webhook + payload for correlation. + + 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 + """ + return await self._post( + "/v4/inbound-email", + body=await async_maybe_transform( + { + "callback_url": callback_url, + "alias": alias, + "allowed_sources": allowed_sources, + "metadata": metadata, + "reference": reference, + }, + inbound_email_create_params.InboundEmailCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InboundEmailCreateResponse, + ) + + async def retrieve( + self, + inbound_email_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, + ) -> InboundEmailRetrieveResponse: + """ + Retrieve details of a specific mailbox including statistics. + + 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 + """ + if not inbound_email_id: + raise ValueError(f"Expected a non-empty value for `inbound_email_id` but received {inbound_email_id!r}") + return await self._get( + f"/v4/inbound-email/{inbound_email_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InboundEmailRetrieveResponse, + ) + + async def list( + self, + *, + limit: int | Omit = omit, + offset: int | Omit = omit, + status: Literal["active", "paused", "all"] | Omit = omit, + # 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, + ) -> InboundEmailListResponse: + """List all mailboxes associated with your API key. + + Returns active and inactive + mailboxes (deleted mailboxes are excluded). + + Args: + limit: Maximum number of inbound emails to return + + offset: Pagination offset + + status: Filter by status + + 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 + """ + return await self._get( + "/v4/inbound-email", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "limit": limit, + "offset": offset, + "status": status, + }, + inbound_email_list_params.InboundEmailListParams, + ), + ), + cast_to=InboundEmailListResponse, + ) + + async def delete( + self, + inbound_email_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, + ) -> InboundEmailDeleteResponse: + """Permanently delete an inbound email address. + + It will stop accepting emails. + + **Note:** Deletion is immediate and cannot be undone. Any emails received after + deletion will be rejected. + + 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 + """ + if not inbound_email_id: + raise ValueError(f"Expected a non-empty value for `inbound_email_id` but received {inbound_email_id!r}") + return await self._delete( + f"/v4/inbound-email/{inbound_email_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InboundEmailDeleteResponse, + ) + + +class InboundEmailResourceWithRawResponse: + def __init__(self, inbound_email: InboundEmailResource) -> None: + self._inbound_email = inbound_email + + self.create = to_raw_response_wrapper( + inbound_email.create, + ) + self.retrieve = to_raw_response_wrapper( + inbound_email.retrieve, + ) + self.list = to_raw_response_wrapper( + inbound_email.list, + ) + self.delete = to_raw_response_wrapper( + inbound_email.delete, + ) + + +class AsyncInboundEmailResourceWithRawResponse: + def __init__(self, inbound_email: AsyncInboundEmailResource) -> None: + self._inbound_email = inbound_email + + self.create = async_to_raw_response_wrapper( + inbound_email.create, + ) + self.retrieve = async_to_raw_response_wrapper( + inbound_email.retrieve, + ) + self.list = async_to_raw_response_wrapper( + inbound_email.list, + ) + self.delete = async_to_raw_response_wrapper( + inbound_email.delete, + ) + + +class InboundEmailResourceWithStreamingResponse: + def __init__(self, inbound_email: InboundEmailResource) -> None: + self._inbound_email = inbound_email + + self.create = to_streamed_response_wrapper( + inbound_email.create, + ) + self.retrieve = to_streamed_response_wrapper( + inbound_email.retrieve, + ) + self.list = to_streamed_response_wrapper( + inbound_email.list, + ) + self.delete = to_streamed_response_wrapper( + inbound_email.delete, + ) + + +class AsyncInboundEmailResourceWithStreamingResponse: + def __init__(self, inbound_email: AsyncInboundEmailResource) -> None: + self._inbound_email = inbound_email + + self.create = async_to_streamed_response_wrapper( + inbound_email.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + inbound_email.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + inbound_email.list, + ) + self.delete = async_to_streamed_response_wrapper( + inbound_email.delete, + ) diff --git a/src/cas_parser/resources/logs.py b/src/cas_parser/resources/logs.py index bd6ec9b..45ba056 100644 --- a/src/cas_parser/resources/logs.py +++ b/src/cas_parser/resources/logs.py @@ -64,6 +64,8 @@ def create( Returns a list of API calls with timestamps, features used, status codes, and credits consumed. Useful for monitoring usage patterns and debugging. + **Legacy path:** `/logs` (still supported) + Args: end_time: End time filter (ISO 8601). Defaults to now. @@ -80,7 +82,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/logs", + "/v1/usage", body=maybe_transform( { "end_time": end_time, @@ -113,6 +115,8 @@ def get_summary( Useful for understanding which API features are being used most and tracking usage trends. + **Legacy path:** `/logs/summary` (still supported) + Args: end_time: End time filter (ISO 8601). Defaults to now. @@ -127,7 +131,7 @@ def get_summary( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/logs/summary", + "/v1/usage/summary", body=maybe_transform( { "end_time": end_time, @@ -181,6 +185,8 @@ async def create( Returns a list of API calls with timestamps, features used, status codes, and credits consumed. Useful for monitoring usage patterns and debugging. + **Legacy path:** `/logs` (still supported) + Args: end_time: End time filter (ISO 8601). Defaults to now. @@ -197,7 +203,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/logs", + "/v1/usage", body=await async_maybe_transform( { "end_time": end_time, @@ -230,6 +236,8 @@ async def get_summary( Useful for understanding which API features are being used most and tracking usage trends. + **Legacy path:** `/logs/summary` (still supported) + Args: end_time: End time filter (ISO 8601). Defaults to now. @@ -244,7 +252,7 @@ async def get_summary( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/logs/summary", + "/v1/usage/summary", body=await async_maybe_transform( { "end_time": end_time, diff --git a/src/cas_parser/resources/verify_token.py b/src/cas_parser/resources/verify_token.py index 273eb62..06981d4 100644 --- a/src/cas_parser/resources/verify_token.py +++ b/src/cas_parser/resources/verify_token.py @@ -55,7 +55,7 @@ def verify( issues. """ return self._post( - "/v1/verify-token", + "/v1/token/verify", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -99,7 +99,7 @@ async def verify( issues. """ return await self._post( - "/v1/verify-token", + "/v1/token/verify", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/cas_parser/types/__init__.py b/src/cas_parser/types/__init__.py index d72939a..269b048 100644 --- a/src/cas_parser/types/__init__.py +++ b/src/cas_parser/types/__init__.py @@ -12,19 +12,25 @@ from .credit_check_response import CreditCheckResponse as CreditCheckResponse from .log_get_summary_params import LogGetSummaryParams as LogGetSummaryParams from .log_get_summary_response import LogGetSummaryResponse as LogGetSummaryResponse +from .inbound_email_list_params import InboundEmailListParams as InboundEmailListParams from .access_token_create_params import AccessTokenCreateParams as AccessTokenCreateParams from .cams_kfintech_parse_params import CamsKfintechParseParams as CamsKfintechParseParams from .contract_note_parse_params import ContractNoteParseParams as ContractNoteParseParams from .inbox_connect_email_params import InboxConnectEmailParams as InboxConnectEmailParams from .smart_parse_cas_pdf_params import SmartParseCasPdfParams as SmartParseCasPdfParams +from .inbound_email_create_params import InboundEmailCreateParams as InboundEmailCreateParams +from .inbound_email_list_response import InboundEmailListResponse as InboundEmailListResponse from .inbox_list_cas_files_params import InboxListCasFilesParams as InboxListCasFilesParams from .access_token_create_response import AccessTokenCreateResponse as AccessTokenCreateResponse from .contract_note_parse_response import ContractNoteParseResponse as ContractNoteParseResponse from .inbox_connect_email_response import InboxConnectEmailResponse as InboxConnectEmailResponse from .kfintech_generate_cas_params import KfintechGenerateCasParams as KfintechGenerateCasParams from .verify_token_verify_response import VerifyTokenVerifyResponse as VerifyTokenVerifyResponse +from .inbound_email_create_response import InboundEmailCreateResponse as InboundEmailCreateResponse +from .inbound_email_delete_response import InboundEmailDeleteResponse as InboundEmailDeleteResponse from .inbox_list_cas_files_response import InboxListCasFilesResponse as InboxListCasFilesResponse from .kfintech_generate_cas_response import KfintechGenerateCasResponse as KfintechGenerateCasResponse +from .inbound_email_retrieve_response import InboundEmailRetrieveResponse as InboundEmailRetrieveResponse from .inbox_disconnect_email_response import InboxDisconnectEmailResponse as InboxDisconnectEmailResponse from .inbox_check_connection_status_response import ( InboxCheckConnectionStatusResponse as InboxCheckConnectionStatusResponse, diff --git a/src/cas_parser/types/inbound_email_create_params.py b/src/cas_parser/types/inbound_email_create_params.py new file mode 100644 index 0000000..356d7e1 --- /dev/null +++ b/src/cas_parser/types/inbound_email_create_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InboundEmailCreateParams"] + + +class InboundEmailCreateParams(TypedDict, total=False): + callback_url: Required[str] + """ + Webhook URL where we POST email notifications. Must be HTTPS in production (HTTP + allowed for localhost during development). + """ + + alias: str + """Optional custom email prefix for user-friendly addresses. + + - Must be 3-32 characters + - Alphanumeric + hyphens only + - Must start and end with letter/number + - Example: `john-portfolio@import.casparser.in` + - If omitted, generates random ID like `ie_abc123xyz@import.casparser.in` + """ + + allowed_sources: List[Literal["cdsl", "nsdl", "cams", "kfintech"]] + """Filter emails by CAS provider. If omitted, accepts all providers. + + - `cdsl` → eCAS@cdslstatement.com + - `nsdl` → NSDL-CAS@nsdl.co.in + - `cams` → donotreply@camsonline.com + - `kfintech` → samfS@kfintech.com + """ + + metadata: Dict[str, str] + """ + Optional key-value pairs (max 10) to include in webhook payload. Useful for + passing context like plan_type, campaign_id, etc. + """ + + reference: str + """ + Your internal identifier (e.g., user_id, account_id). Returned in webhook + payload for correlation. + """ diff --git a/src/cas_parser/types/inbound_email_create_response.py b/src/cas_parser/types/inbound_email_create_response.py new file mode 100644 index 0000000..29f89dc --- /dev/null +++ b/src/cas_parser/types/inbound_email_create_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["InboundEmailCreateResponse"] + + +class InboundEmailCreateResponse(BaseModel): + """An inbound email address for receiving forwarded CAS emails""" + + allowed_sources: Optional[List[Literal["cdsl", "nsdl", "cams", "kfintech"]]] = None + """Accepted CAS providers (empty = all)""" + + callback_url: Optional[str] = None + """Webhook URL for email notifications""" + + created_at: Optional[datetime] = None + """When the mailbox was created""" + + email: Optional[str] = None + """The inbound email address to forward CAS statements to""" + + inbound_email_id: Optional[str] = None + """Unique inbound email identifier""" + + metadata: Optional[Dict[str, str]] = None + """Custom key-value metadata""" + + reference: Optional[str] = None + """Your internal reference identifier""" + + status: Optional[Literal["active", "paused"]] = None + """Current mailbox status""" + + updated_at: Optional[datetime] = None + """When the mailbox was last updated""" diff --git a/src/cas_parser/types/inbound_email_delete_response.py b/src/cas_parser/types/inbound_email_delete_response.py new file mode 100644 index 0000000..fdb55b2 --- /dev/null +++ b/src/cas_parser/types/inbound_email_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["InboundEmailDeleteResponse"] + + +class InboundEmailDeleteResponse(BaseModel): + msg: Optional[str] = None + + status: Optional[str] = None diff --git a/src/cas_parser/types/inbound_email_list_params.py b/src/cas_parser/types/inbound_email_list_params.py new file mode 100644 index 0000000..c70d034 --- /dev/null +++ b/src/cas_parser/types/inbound_email_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["InboundEmailListParams"] + + +class InboundEmailListParams(TypedDict, total=False): + limit: int + """Maximum number of inbound emails to return""" + + offset: int + """Pagination offset""" + + status: Literal["active", "paused", "all"] + """Filter by status""" diff --git a/src/cas_parser/types/inbound_email_list_response.py b/src/cas_parser/types/inbound_email_list_response.py new file mode 100644 index 0000000..b1eea1b --- /dev/null +++ b/src/cas_parser/types/inbound_email_list_response.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["InboundEmailListResponse", "InboundEmail"] + + +class InboundEmail(BaseModel): + """An inbound email address for receiving forwarded CAS emails""" + + allowed_sources: Optional[List[Literal["cdsl", "nsdl", "cams", "kfintech"]]] = None + """Accepted CAS providers (empty = all)""" + + callback_url: Optional[str] = None + """Webhook URL for email notifications""" + + created_at: Optional[datetime] = None + """When the mailbox was created""" + + email: Optional[str] = None + """The inbound email address to forward CAS statements to""" + + inbound_email_id: Optional[str] = None + """Unique inbound email identifier""" + + metadata: Optional[Dict[str, str]] = None + """Custom key-value metadata""" + + reference: Optional[str] = None + """Your internal reference identifier""" + + status: Optional[Literal["active", "paused"]] = None + """Current mailbox status""" + + updated_at: Optional[datetime] = None + """When the mailbox was last updated""" + + +class InboundEmailListResponse(BaseModel): + inbound_emails: Optional[List[InboundEmail]] = None + + limit: Optional[int] = None + + offset: Optional[int] = None + + status: Optional[str] = None + + total: Optional[int] = None + """Total number of inbound emails (for pagination)""" diff --git a/src/cas_parser/types/inbound_email_retrieve_response.py b/src/cas_parser/types/inbound_email_retrieve_response.py new file mode 100644 index 0000000..601fc87 --- /dev/null +++ b/src/cas_parser/types/inbound_email_retrieve_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["InboundEmailRetrieveResponse"] + + +class InboundEmailRetrieveResponse(BaseModel): + """An inbound email address for receiving forwarded CAS emails""" + + allowed_sources: Optional[List[Literal["cdsl", "nsdl", "cams", "kfintech"]]] = None + """Accepted CAS providers (empty = all)""" + + callback_url: Optional[str] = None + """Webhook URL for email notifications""" + + created_at: Optional[datetime] = None + """When the mailbox was created""" + + email: Optional[str] = None + """The inbound email address to forward CAS statements to""" + + inbound_email_id: Optional[str] = None + """Unique inbound email identifier""" + + metadata: Optional[Dict[str, str]] = None + """Custom key-value metadata""" + + reference: Optional[str] = None + """Your internal reference identifier""" + + status: Optional[Literal["active", "paused"]] = None + """Current mailbox status""" + + updated_at: Optional[datetime] = None + """When the mailbox was last updated""" diff --git a/src/cas_parser/types/inbox_list_cas_files_response.py b/src/cas_parser/types/inbox_list_cas_files_response.py index ef27d92..25b960f 100644 --- a/src/cas_parser/types/inbox_list_cas_files_response.py +++ b/src/cas_parser/types/inbox_list_cas_files_response.py @@ -30,6 +30,12 @@ class File(BaseModel): original_filename: Optional[str] = None """Original attachment filename from the email""" + sender_email: Optional[str] = None + """ + Email address of the CAS authority (CDSL, NSDL, CAMS, or KFintech) who + originally sent this statement + """ + size: Optional[int] = None """File size in bytes""" diff --git a/tests/api_resources/test_inbound_email.py b/tests/api_resources/test_inbound_email.py new file mode 100644 index 0000000..6970229 --- /dev/null +++ b/tests/api_resources/test_inbound_email.py @@ -0,0 +1,371 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from cas_parser import CasParser, AsyncCasParser +from tests.utils import assert_matches_type +from cas_parser.types import ( + InboundEmailListResponse, + InboundEmailCreateResponse, + InboundEmailDeleteResponse, + InboundEmailRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInboundEmail: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create(self, client: CasParser) -> None: + inbound_email = client.inbound_email.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + ) + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: CasParser) -> None: + inbound_email = client.inbound_email.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + alias="john-portfolio", + allowed_sources=["cdsl", "nsdl"], + metadata={ + "plan": "premium", + "source": "onboarding", + }, + reference="user_12345", + ) + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_create(self, client: CasParser) -> None: + response = client.inbound_email.with_raw_response.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = response.parse() + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_create(self, client: CasParser) -> None: + with client.inbound_email.with_streaming_response.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = response.parse() + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_retrieve(self, client: CasParser) -> None: + inbound_email = client.inbound_email.retrieve( + "ie_a1b2c3d4e5f6", + ) + assert_matches_type(InboundEmailRetrieveResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: CasParser) -> None: + response = client.inbound_email.with_raw_response.retrieve( + "ie_a1b2c3d4e5f6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = response.parse() + assert_matches_type(InboundEmailRetrieveResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: CasParser) -> None: + with client.inbound_email.with_streaming_response.retrieve( + "ie_a1b2c3d4e5f6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = response.parse() + assert_matches_type(InboundEmailRetrieveResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: CasParser) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `inbound_email_id` but received ''"): + client.inbound_email.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: CasParser) -> None: + inbound_email = client.inbound_email.list() + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: CasParser) -> None: + inbound_email = client.inbound_email.list( + limit=1, + offset=0, + status="active", + ) + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: CasParser) -> None: + response = client.inbound_email.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = response.parse() + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: CasParser) -> None: + with client.inbound_email.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = response.parse() + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_delete(self, client: CasParser) -> None: + inbound_email = client.inbound_email.delete( + "inbound_email_id", + ) + assert_matches_type(InboundEmailDeleteResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_delete(self, client: CasParser) -> None: + response = client.inbound_email.with_raw_response.delete( + "inbound_email_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = response.parse() + assert_matches_type(InboundEmailDeleteResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: CasParser) -> None: + with client.inbound_email.with_streaming_response.delete( + "inbound_email_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = response.parse() + assert_matches_type(InboundEmailDeleteResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_delete(self, client: CasParser) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `inbound_email_id` but received ''"): + client.inbound_email.with_raw_response.delete( + "", + ) + + +class TestAsyncInboundEmail: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncCasParser) -> None: + inbound_email = await async_client.inbound_email.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + ) + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncCasParser) -> None: + inbound_email = await async_client.inbound_email.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + alias="john-portfolio", + allowed_sources=["cdsl", "nsdl"], + metadata={ + "plan": "premium", + "source": "onboarding", + }, + reference="user_12345", + ) + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncCasParser) -> None: + response = await async_client.inbound_email.with_raw_response.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = await response.parse() + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncCasParser) -> None: + async with async_client.inbound_email.with_streaming_response.create( + callback_url="https://api.yourapp.com/webhooks/cas-email", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = await response.parse() + assert_matches_type(InboundEmailCreateResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncCasParser) -> None: + inbound_email = await async_client.inbound_email.retrieve( + "ie_a1b2c3d4e5f6", + ) + assert_matches_type(InboundEmailRetrieveResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncCasParser) -> None: + response = await async_client.inbound_email.with_raw_response.retrieve( + "ie_a1b2c3d4e5f6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = await response.parse() + assert_matches_type(InboundEmailRetrieveResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncCasParser) -> None: + async with async_client.inbound_email.with_streaming_response.retrieve( + "ie_a1b2c3d4e5f6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = await response.parse() + assert_matches_type(InboundEmailRetrieveResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncCasParser) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `inbound_email_id` but received ''"): + await async_client.inbound_email.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncCasParser) -> None: + inbound_email = await async_client.inbound_email.list() + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncCasParser) -> None: + inbound_email = await async_client.inbound_email.list( + limit=1, + offset=0, + status="active", + ) + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncCasParser) -> None: + response = await async_client.inbound_email.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = await response.parse() + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncCasParser) -> None: + async with async_client.inbound_email.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = await response.parse() + assert_matches_type(InboundEmailListResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncCasParser) -> None: + inbound_email = await async_client.inbound_email.delete( + "inbound_email_id", + ) + assert_matches_type(InboundEmailDeleteResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncCasParser) -> None: + response = await async_client.inbound_email.with_raw_response.delete( + "inbound_email_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inbound_email = await response.parse() + assert_matches_type(InboundEmailDeleteResponse, inbound_email, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncCasParser) -> None: + async with async_client.inbound_email.with_streaming_response.delete( + "inbound_email_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inbound_email = await response.parse() + assert_matches_type(InboundEmailDeleteResponse, inbound_email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncCasParser) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `inbound_email_id` but received ''"): + await async_client.inbound_email.with_raw_response.delete( + "", + ) diff --git a/tests/test_client.py b/tests/test_client.py index ef1fd91..5a768aa 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -863,7 +863,7 @@ def test_parse_retry_after_header( @mock.patch("cas_parser._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: CasParser) -> None: - respx_mock.post("/credits").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/v1/credits").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): client.credits.with_streaming_response.check().__enter__() @@ -873,7 +873,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien @mock.patch("cas_parser._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: CasParser) -> None: - respx_mock.post("/credits").mock(return_value=httpx.Response(500)) + respx_mock.post("/v1/credits").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): client.credits.with_streaming_response.check().__enter__() @@ -903,7 +903,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/credits").mock(side_effect=retry_handler) + respx_mock.post("/v1/credits").mock(side_effect=retry_handler) response = client.credits.with_raw_response.check() @@ -927,7 +927,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/credits").mock(side_effect=retry_handler) + respx_mock.post("/v1/credits").mock(side_effect=retry_handler) response = client.credits.with_raw_response.check(extra_headers={"x-stainless-retry-count": Omit()}) @@ -950,7 +950,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/credits").mock(side_effect=retry_handler) + respx_mock.post("/v1/credits").mock(side_effect=retry_handler) response = client.credits.with_raw_response.check(extra_headers={"x-stainless-retry-count": "42"}) @@ -1775,7 +1775,7 @@ async def test_parse_retry_after_header( async def test_retrying_timeout_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncCasParser ) -> None: - respx_mock.post("/credits").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/v1/credits").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): await async_client.credits.with_streaming_response.check().__aenter__() @@ -1787,7 +1787,7 @@ async def test_retrying_timeout_errors_doesnt_leak( async def test_retrying_status_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncCasParser ) -> None: - respx_mock.post("/credits").mock(return_value=httpx.Response(500)) + respx_mock.post("/v1/credits").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): await async_client.credits.with_streaming_response.check().__aenter__() @@ -1817,7 +1817,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/credits").mock(side_effect=retry_handler) + respx_mock.post("/v1/credits").mock(side_effect=retry_handler) response = await client.credits.with_raw_response.check() @@ -1841,7 +1841,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/credits").mock(side_effect=retry_handler) + respx_mock.post("/v1/credits").mock(side_effect=retry_handler) response = await client.credits.with_raw_response.check(extra_headers={"x-stainless-retry-count": Omit()}) @@ -1864,7 +1864,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/credits").mock(side_effect=retry_handler) + respx_mock.post("/v1/credits").mock(side_effect=retry_handler) response = await client.credits.with_raw_response.check(extra_headers={"x-stainless-retry-count": "42"})