diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 091cfb12..f7014c35 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.10.0" + ".": "0.11.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5548309c..f80222bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.11.0 (2024-01-11) + +Full Changelog: [v0.10.0...v0.11.0](https://github.com/Finch-API/finch-api-python/compare/v0.10.0...v0.11.0) + +### Features + +* **client:** hook up sandbox auth ([#256](https://github.com/Finch-API/finch-api-python/issues/256)) ([13f29d3](https://github.com/Finch-API/finch-api-python/commit/13f29d37dd22e9f83010434e63e5efe428d3dda0)) +* remove redundant endpoint, add sandbox client options (not yet used) ([#255](https://github.com/Finch-API/finch-api-python/issues/255)) ([3fbedde](https://github.com/Finch-API/finch-api-python/commit/3fbedde2c7eddc74193c6b60cc89021392b41c25)) + + +### Chores + +* **client:** improve debug logging for failed requests ([#257](https://github.com/Finch-API/finch-api-python/issues/257)) ([04a3069](https://github.com/Finch-API/finch-api-python/commit/04a306915f19ebdbad1ec25f90bc58bcfa9aff57)) +* **internal:** rename unreleased connection status type ([#253](https://github.com/Finch-API/finch-api-python/issues/253)) ([e7f4e7d](https://github.com/Finch-API/finch-api-python/commit/e7f4e7da44347de96e49e0a802ea335cce56ec20)) + ## 0.10.0 (2024-01-09) Full Changelog: [v0.9.5...v0.10.0](https://github.com/Finch-API/finch-api-python/compare/v0.9.5...v0.10.0) diff --git a/api.md b/api.md index 0529d9d2..62d3fb7c 100644 --- a/api.md +++ b/api.md @@ -1,12 +1,7 @@ # Shared Types ```python -from finch.types import ( - IntrospectResponseConnectionStatus, - OperationSupport, - OperationSupportMatrix, - Paging, -) +from finch.types import ConnectionStatusType, OperationSupport, OperationSupportMatrix, Paging ``` # Finch @@ -229,12 +224,6 @@ Methods: - client.jobs.manual.retrieve(job_id) -> ManualAsyncJob -# Auth - -Methods: - -- client.auth.create_token(\*\*params) -> CreateAccessTokenResponse - # Sandbox ## Connections diff --git a/pyproject.toml b/pyproject.toml index a374dc26..8e77530a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finch-api" -version = "0.10.0" +version = "0.11.0" description = "The official Python library for the Finch API" readme = "README.md" license = "Apache-2.0" diff --git a/src/finch/_base_client.py b/src/finch/_base_client.py index 97c6bef9..c2c2db5f 100644 --- a/src/finch/_base_client.py +++ b/src/finch/_base_client.py @@ -646,26 +646,33 @@ def _should_retry(self, response: httpx.Response) -> bool: # If the server explicitly says whether or not to retry, obey. if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") return True if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") return False # Retry on request timeouts. if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) return True # Retry on lock timeouts. if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) return True # Retry on rate limits. if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) return True # Retry internal errors. if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) return True + log.debug("Not retrying") return False def _idempotency_key(self) -> str: @@ -883,6 +890,8 @@ def _request( **kwargs, ) except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + if retries > 0: return self._retry_request( options, @@ -893,8 +902,11 @@ def _request( response_headers=None, ) + log.debug("Raising timeout error") raise APITimeoutError(request=request) from err except Exception as err: + log.debug("Encountered Exception", exc_info=True) + if retries > 0: return self._retry_request( options, @@ -905,6 +917,7 @@ def _request( response_headers=None, ) + log.debug("Raising connection error") raise APIConnectionError(request=request) from err log.debug( @@ -914,6 +927,8 @@ def _request( try: response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + if retries > 0 and self._should_retry(err.response): err.response.close() return self._retry_request( @@ -930,6 +945,7 @@ def _request( if not err.response.is_closed: err.response.read() + log.debug("Re-raising status error") raise self._make_status_error_from_response(err.response) from None return self._process_response( @@ -951,6 +967,11 @@ def _retry_request( stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: remaining = remaining_retries - 1 + if remaining == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining) + timeout = self._calculate_retry_timeout(remaining, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) @@ -1349,6 +1370,8 @@ async def _request( **kwargs, ) except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + if retries > 0: return await self._retry_request( options, @@ -1359,8 +1382,11 @@ async def _request( response_headers=None, ) + log.debug("Raising timeout error") raise APITimeoutError(request=request) from err except Exception as err: + log.debug("Encountered Exception", exc_info=True) + if retries > 0: return await self._retry_request( options, @@ -1371,6 +1397,7 @@ async def _request( response_headers=None, ) + log.debug("Raising connection error") raise APIConnectionError(request=request) from err log.debug( @@ -1380,6 +1407,8 @@ async def _request( try: response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + if retries > 0 and self._should_retry(err.response): await err.response.aclose() return await self._retry_request( @@ -1396,6 +1425,7 @@ async def _request( if not err.response.is_closed: await err.response.aread() + log.debug("Re-raising status error") raise self._make_status_error_from_response(err.response) from None return self._process_response( @@ -1417,6 +1447,11 @@ async def _retry_request( stream_cls: type[_AsyncStreamT] | None, ) -> ResponseT | _AsyncStreamT: remaining = remaining_retries - 1 + if remaining == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining) + timeout = self._calculate_retry_timeout(remaining, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) diff --git a/src/finch/_client.py b/src/finch/_client.py index 6e220ad4..a2235b30 100644 --- a/src/finch/_client.py +++ b/src/finch/_client.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +import base64 from typing import Any, Union, Mapping from typing_extensions import Self, override @@ -58,7 +59,6 @@ class Finch(SyncAPIClient): webhooks: resources.Webhooks request_forwarding: resources.RequestForwarding jobs: resources.Jobs - auth: resources.Auth sandbox: resources.Sandbox with_raw_response: FinchWithRawResponse @@ -66,6 +66,8 @@ class Finch(SyncAPIClient): access_token: str | None client_id: str | None client_secret: str | None + sandbox_client_id: str | None + sandbox_client_secret: str | None webhook_secret: str | None def __init__( @@ -74,6 +76,8 @@ def __init__( access_token: str | None = None, client_id: str | None = None, client_secret: str | None = None, + sandbox_client_id: str | None = None, + sandbox_client_secret: str | None = None, webhook_secret: str | None = None, base_url: str | httpx.URL | None = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, @@ -103,6 +107,8 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `client_id` from `FINCH_CLIENT_ID` - `client_secret` from `FINCH_CLIENT_SECRET` + - `sandbox_client_id` from `FINCH_SANDBOX_CLIENT_ID` + - `sandbox_client_secret` from `FINCH_SANDBOX_CLIENT_SECRET` - `webhook_secret` from `FINCH_WEBHOOK_SECRET` """ self.access_token = access_token @@ -115,6 +121,14 @@ def __init__( client_secret = os.environ.get("FINCH_CLIENT_SECRET") self.client_secret = client_secret + if sandbox_client_id is None: + sandbox_client_id = os.environ.get("FINCH_SANDBOX_CLIENT_ID") + self.sandbox_client_id = sandbox_client_id + + if sandbox_client_secret is None: + sandbox_client_secret = os.environ.get("FINCH_SANDBOX_CLIENT_SECRET") + self.sandbox_client_secret = sandbox_client_secret + if webhook_secret is None: webhook_secret = os.environ.get("FINCH_WEBHOOK_SECRET") self.webhook_secret = webhook_secret @@ -145,7 +159,6 @@ def __init__( self.webhooks = resources.Webhooks(self) self.request_forwarding = resources.RequestForwarding(self) self.jobs = resources.Jobs(self) - self.auth = resources.Auth(self) self.sandbox = resources.Sandbox(self) self.with_raw_response = FinchWithRawResponse(self) @@ -157,11 +170,29 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: + if self._bearer_auth: + return self._bearer_auth + if self._basic_auth: + return self._basic_auth + return {} + + @property + def _bearer_auth(self) -> dict[str, str]: access_token = self.access_token if access_token is None: return {} return {"Authorization": f"Bearer {access_token}"} + @property + def _basic_auth(self) -> dict[str, str]: + if self.sandbox_client_id is None: + return {} + if self.sandbox_client_secret is None: + return {} + credentials = f"{self.sandbox_client_id}:{self.sandbox_client_secret}".encode("ascii") + header = f"Basic {base64.b64encode(credentials).decode('ascii')}" + return {"Authorization": header} + @property @override def default_headers(self) -> dict[str, str | Omit]: @@ -179,8 +210,13 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if isinstance(custom_headers.get("Authorization"), Omit): return + if self.sandbox_client_id and self.sandbox_client_secret and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + raise TypeError( - '"Could not resolve authentication method. Expected the access_token to be set. Or for the `Authorization` headers to be explicitly omitted"' + '"Could not resolve authentication method. Expected either access_token, sandbox_client_id or sandbox_client_secret to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) def copy( @@ -189,6 +225,8 @@ def copy( access_token: str | None = None, client_id: str | None = None, client_secret: str | None = None, + sandbox_client_id: str | None = None, + sandbox_client_secret: str | None = None, webhook_secret: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, @@ -244,6 +282,8 @@ def copy( access_token=access_token or self.access_token, client_id=client_id or self.client_id, client_secret=client_secret or self.client_secret, + sandbox_client_id=sandbox_client_id or self.sandbox_client_id, + sandbox_client_secret=sandbox_client_secret or self.sandbox_client_secret, webhook_secret=webhook_secret or self.webhook_secret, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, @@ -369,7 +409,6 @@ class AsyncFinch(AsyncAPIClient): webhooks: resources.AsyncWebhooks request_forwarding: resources.AsyncRequestForwarding jobs: resources.AsyncJobs - auth: resources.AsyncAuth sandbox: resources.AsyncSandbox with_raw_response: AsyncFinchWithRawResponse @@ -377,6 +416,8 @@ class AsyncFinch(AsyncAPIClient): access_token: str | None client_id: str | None client_secret: str | None + sandbox_client_id: str | None + sandbox_client_secret: str | None webhook_secret: str | None def __init__( @@ -385,6 +426,8 @@ def __init__( access_token: str | None = None, client_id: str | None = None, client_secret: str | None = None, + sandbox_client_id: str | None = None, + sandbox_client_secret: str | None = None, webhook_secret: str | None = None, base_url: str | httpx.URL | None = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, @@ -414,6 +457,8 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `client_id` from `FINCH_CLIENT_ID` - `client_secret` from `FINCH_CLIENT_SECRET` + - `sandbox_client_id` from `FINCH_SANDBOX_CLIENT_ID` + - `sandbox_client_secret` from `FINCH_SANDBOX_CLIENT_SECRET` - `webhook_secret` from `FINCH_WEBHOOK_SECRET` """ self.access_token = access_token @@ -426,6 +471,14 @@ def __init__( client_secret = os.environ.get("FINCH_CLIENT_SECRET") self.client_secret = client_secret + if sandbox_client_id is None: + sandbox_client_id = os.environ.get("FINCH_SANDBOX_CLIENT_ID") + self.sandbox_client_id = sandbox_client_id + + if sandbox_client_secret is None: + sandbox_client_secret = os.environ.get("FINCH_SANDBOX_CLIENT_SECRET") + self.sandbox_client_secret = sandbox_client_secret + if webhook_secret is None: webhook_secret = os.environ.get("FINCH_WEBHOOK_SECRET") self.webhook_secret = webhook_secret @@ -456,7 +509,6 @@ def __init__( self.webhooks = resources.AsyncWebhooks(self) self.request_forwarding = resources.AsyncRequestForwarding(self) self.jobs = resources.AsyncJobs(self) - self.auth = resources.AsyncAuth(self) self.sandbox = resources.AsyncSandbox(self) self.with_raw_response = AsyncFinchWithRawResponse(self) @@ -468,11 +520,29 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: + if self._bearer_auth: + return self._bearer_auth + if self._basic_auth: + return self._basic_auth + return {} + + @property + def _bearer_auth(self) -> dict[str, str]: access_token = self.access_token if access_token is None: return {} return {"Authorization": f"Bearer {access_token}"} + @property + def _basic_auth(self) -> dict[str, str]: + if self.sandbox_client_id is None: + return {} + if self.sandbox_client_secret is None: + return {} + credentials = f"{self.sandbox_client_id}:{self.sandbox_client_secret}".encode("ascii") + header = f"Basic {base64.b64encode(credentials).decode('ascii')}" + return {"Authorization": header} + @property @override def default_headers(self) -> dict[str, str | Omit]: @@ -490,8 +560,13 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if isinstance(custom_headers.get("Authorization"), Omit): return + if self.sandbox_client_id and self.sandbox_client_secret and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + raise TypeError( - '"Could not resolve authentication method. Expected the access_token to be set. Or for the `Authorization` headers to be explicitly omitted"' + '"Could not resolve authentication method. Expected either access_token, sandbox_client_id or sandbox_client_secret to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) def copy( @@ -500,6 +575,8 @@ def copy( access_token: str | None = None, client_id: str | None = None, client_secret: str | None = None, + sandbox_client_id: str | None = None, + sandbox_client_secret: str | None = None, webhook_secret: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, @@ -555,6 +632,8 @@ def copy( access_token=access_token or self.access_token, client_id=client_id or self.client_id, client_secret=client_secret or self.client_secret, + sandbox_client_id=sandbox_client_id or self.sandbox_client_id, + sandbox_client_secret=sandbox_client_secret or self.sandbox_client_secret, webhook_secret=webhook_secret or self.webhook_secret, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, @@ -680,7 +759,6 @@ def __init__(self, client: Finch) -> None: self.account = resources.AccountWithRawResponse(client.account) self.request_forwarding = resources.RequestForwardingWithRawResponse(client.request_forwarding) self.jobs = resources.JobsWithRawResponse(client.jobs) - self.auth = resources.AuthWithRawResponse(client.auth) self.sandbox = resources.SandboxWithRawResponse(client.sandbox) @@ -692,7 +770,6 @@ def __init__(self, client: AsyncFinch) -> None: self.account = resources.AsyncAccountWithRawResponse(client.account) self.request_forwarding = resources.AsyncRequestForwardingWithRawResponse(client.request_forwarding) self.jobs = resources.AsyncJobsWithRawResponse(client.jobs) - self.auth = resources.AsyncAuthWithRawResponse(client.auth) self.sandbox = resources.AsyncSandboxWithRawResponse(client.sandbox) diff --git a/src/finch/_version.py b/src/finch/_version.py index e6546369..81d4eb27 100644 --- a/src/finch/_version.py +++ b/src/finch/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "finch" -__version__ = "0.10.0" # x-release-please-version +__version__ = "0.11.0" # x-release-please-version diff --git a/src/finch/resources/__init__.py b/src/finch/resources/__init__.py index f5912eb0..b3c215be 100644 --- a/src/finch/resources/__init__.py +++ b/src/finch/resources/__init__.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. -from .auth import Auth, AsyncAuth, AuthWithRawResponse, AsyncAuthWithRawResponse from .hris import HRIS, AsyncHRIS, HRISWithRawResponse, AsyncHRISWithRawResponse from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse from .account import Account, AsyncAccount, AccountWithRawResponse, AsyncAccountWithRawResponse @@ -47,10 +46,6 @@ "AsyncJobs", "JobsWithRawResponse", "AsyncJobsWithRawResponse", - "Auth", - "AsyncAuth", - "AuthWithRawResponse", - "AsyncAuthWithRawResponse", "Sandbox", "AsyncSandbox", "SandboxWithRawResponse", diff --git a/src/finch/resources/auth.py b/src/finch/resources/auth.py deleted file mode 100644 index 1a7bd991..00000000 --- a/src/finch/resources/auth.py +++ /dev/null @@ -1,129 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import httpx - -from ..types import CreateAccessTokenResponse, auth_create_token_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) - -__all__ = ["Auth", "AsyncAuth"] - - -class Auth(SyncAPIResource): - @cached_property - def with_raw_response(self) -> AuthWithRawResponse: - return AuthWithRawResponse(self) - - def create_token( - self, - *, - client_id: str, - client_secret: str, - code: str, - redirect_uri: 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, - ) -> CreateAccessTokenResponse: - """ - Exchange the authorization code for an access token - - 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 - """ - return self._post( - "/auth/token", - body=maybe_transform( - { - "client_id": client_id, - "client_secret": client_secret, - "code": code, - "redirect_uri": redirect_uri, - }, - auth_create_token_params.AuthCreateTokenParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=CreateAccessTokenResponse, - ) - - -class AsyncAuth(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncAuthWithRawResponse: - return AsyncAuthWithRawResponse(self) - - async def create_token( - self, - *, - client_id: str, - client_secret: str, - code: str, - redirect_uri: 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, - ) -> CreateAccessTokenResponse: - """ - Exchange the authorization code for an access token - - 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 - """ - return await self._post( - "/auth/token", - body=maybe_transform( - { - "client_id": client_id, - "client_secret": client_secret, - "code": code, - "redirect_uri": redirect_uri, - }, - auth_create_token_params.AuthCreateTokenParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=CreateAccessTokenResponse, - ) - - -class AuthWithRawResponse: - def __init__(self, auth: Auth) -> None: - self.create_token = to_raw_response_wrapper( - auth.create_token, - ) - - -class AsyncAuthWithRawResponse: - def __init__(self, auth: AsyncAuth) -> None: - self.create_token = async_to_raw_response_wrapper( - auth.create_token, - ) diff --git a/src/finch/resources/sandbox/connections/accounts.py b/src/finch/resources/sandbox/connections/accounts.py index c685e92a..98008ee3 100644 --- a/src/finch/resources/sandbox/connections/accounts.py +++ b/src/finch/resources/sandbox/connections/accounts.py @@ -15,7 +15,7 @@ from ...._base_client import ( make_request_options, ) -from ....types.shared import IntrospectResponseConnectionStatus +from ....types.shared import ConnectionStatusType from ....types.sandbox.connections import ( AccountCreateResponse, AccountUpdateResponse, @@ -80,7 +80,7 @@ def create( def update( self, *, - connection_status: IntrospectResponseConnectionStatus | NotGiven = NOT_GIVEN, + connection_status: ConnectionStatusType | 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, @@ -166,7 +166,7 @@ async def create( async def update( self, *, - connection_status: IntrospectResponseConnectionStatus | NotGiven = NOT_GIVEN, + connection_status: ConnectionStatusType | 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, diff --git a/src/finch/types/__init__.py b/src/finch/types/__init__.py index aaa3cad1..3519a122 100644 --- a/src/finch/types/__init__.py +++ b/src/finch/types/__init__.py @@ -7,8 +7,8 @@ from .shared import ( Paging as Paging, OperationSupport as OperationSupport, + ConnectionStatusType as ConnectionStatusType, OperationSupportMatrix as OperationSupportMatrix, - IntrospectResponseConnectionStatus as IntrospectResponseConnectionStatus, ) from .location import Location as Location from .provider import Provider as Provider @@ -17,7 +17,6 @@ from .introspection import Introspection as Introspection from .location_param import LocationParam as LocationParam from .disconnect_response import DisconnectResponse as DisconnectResponse -from .auth_create_token_params import AuthCreateTokenParams as AuthCreateTokenParams from .access_token_create_params import AccessTokenCreateParams as AccessTokenCreateParams from .create_access_token_response import CreateAccessTokenResponse as CreateAccessTokenResponse from .request_forwarding_forward_params import RequestForwardingForwardParams as RequestForwardingForwardParams diff --git a/src/finch/types/auth_create_token_params.py b/src/finch/types/auth_create_token_params.py deleted file mode 100644 index 9cce752f..00000000 --- a/src/finch/types/auth_create_token_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["AuthCreateTokenParams"] - - -class AuthCreateTokenParams(TypedDict, total=False): - client_id: Required[str] - - client_secret: Required[str] - - code: Required[str] - - redirect_uri: Required[str] diff --git a/src/finch/types/introspection.py b/src/finch/types/introspection.py index bdf99fa7..e7a41f2e 100644 --- a/src/finch/types/introspection.py +++ b/src/finch/types/introspection.py @@ -3,7 +3,7 @@ from typing import List, Optional from typing_extensions import Literal -from .shared import IntrospectResponseConnectionStatus +from .shared import ConnectionStatusType from .._models import BaseModel __all__ = ["Introspection", "AuthenticationMethods", "AuthenticationMethodsConnectionStatus"] @@ -12,7 +12,7 @@ class AuthenticationMethodsConnectionStatus(BaseModel): message: Optional[str] = None - status: Optional[IntrospectResponseConnectionStatus] = None + status: Optional[ConnectionStatusType] = None class AuthenticationMethods(BaseModel): diff --git a/src/finch/types/sandbox/connections/account_update_params.py b/src/finch/types/sandbox/connections/account_update_params.py index c7af5455..b2b8874d 100644 --- a/src/finch/types/sandbox/connections/account_update_params.py +++ b/src/finch/types/sandbox/connections/account_update_params.py @@ -4,10 +4,10 @@ from typing_extensions import TypedDict -from ...shared import IntrospectResponseConnectionStatus +from ...shared import ConnectionStatusType __all__ = ["AccountUpdateParams"] class AccountUpdateParams(TypedDict, total=False): - connection_status: IntrospectResponseConnectionStatus + connection_status: ConnectionStatusType diff --git a/src/finch/types/shared/__init__.py b/src/finch/types/shared/__init__.py index 8aada49c..47337493 100644 --- a/src/finch/types/shared/__init__.py +++ b/src/finch/types/shared/__init__.py @@ -2,7 +2,5 @@ from .paging import Paging as Paging from .operation_support import OperationSupport as OperationSupport +from .connection_status_type import ConnectionStatusType as ConnectionStatusType from .operation_support_matrix import OperationSupportMatrix as OperationSupportMatrix -from .introspect_response_connection_status import ( - IntrospectResponseConnectionStatus as IntrospectResponseConnectionStatus, -) diff --git a/src/finch/types/shared/introspect_response_connection_status.py b/src/finch/types/shared/connection_status_type.py similarity index 67% rename from src/finch/types/shared/introspect_response_connection_status.py rename to src/finch/types/shared/connection_status_type.py index bd418208..f493e50b 100644 --- a/src/finch/types/shared/introspect_response_connection_status.py +++ b/src/finch/types/shared/connection_status_type.py @@ -2,8 +2,8 @@ from typing_extensions import Literal -__all__ = ["IntrospectResponseConnectionStatus"] +__all__ = ["ConnectionStatusType"] -IntrospectResponseConnectionStatus = Literal[ +ConnectionStatusType = Literal[ "pending", "processing", "connected", "error_no_account_setup", "error_permissions", "reauth" ] diff --git a/src/finch/types/shared_params/__init__.py b/src/finch/types/shared_params/__init__.py index aa9fdc38..d47d37df 100644 --- a/src/finch/types/shared_params/__init__.py +++ b/src/finch/types/shared_params/__init__.py @@ -1,5 +1,3 @@ # File generated from our OpenAPI spec by Stainless. -from .introspect_response_connection_status import ( - IntrospectResponseConnectionStatus as IntrospectResponseConnectionStatus, -) +from .connection_status_type import ConnectionStatusType as ConnectionStatusType diff --git a/src/finch/types/shared_params/introspect_response_connection_status.py b/src/finch/types/shared_params/connection_status_type.py similarity index 70% rename from src/finch/types/shared_params/introspect_response_connection_status.py rename to src/finch/types/shared_params/connection_status_type.py index 02ca322c..26562dda 100644 --- a/src/finch/types/shared_params/introspect_response_connection_status.py +++ b/src/finch/types/shared_params/connection_status_type.py @@ -4,8 +4,8 @@ from typing_extensions import Literal -__all__ = ["IntrospectResponseConnectionStatus"] +__all__ = ["ConnectionStatusType"] -IntrospectResponseConnectionStatus = Literal[ +ConnectionStatusType = Literal[ "pending", "processing", "connected", "error_no_account_setup", "error_permissions", "reauth" ] diff --git a/tests/api_resources/sandbox/connections/test_accounts.py b/tests/api_resources/sandbox/connections/test_accounts.py index e61edc81..ef1b3f3c 100644 --- a/tests/api_resources/sandbox/connections/test_accounts.py +++ b/tests/api_resources/sandbox/connections/test_accounts.py @@ -23,6 +23,7 @@ class TestAccounts: loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize def test_method_create(self, client: Finch) -> None: account = client.sandbox.connections.accounts.create( @@ -31,6 +32,7 @@ def test_method_create(self, client: Finch) -> None: ) assert_matches_type(AccountCreateResponse, account, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize def test_method_create_with_all_params(self, client: Finch) -> None: account = client.sandbox.connections.accounts.create( @@ -41,6 +43,7 @@ def test_method_create_with_all_params(self, client: Finch) -> None: ) assert_matches_type(AccountCreateResponse, account, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize def test_raw_response_create(self, client: Finch) -> None: response = client.sandbox.connections.accounts.with_raw_response.create( @@ -76,6 +79,7 @@ class TestAsyncAccounts: loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize async def test_method_create(self, client: AsyncFinch) -> None: account = await client.sandbox.connections.accounts.create( @@ -84,6 +88,7 @@ async def test_method_create(self, client: AsyncFinch) -> None: ) assert_matches_type(AccountCreateResponse, account, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize async def test_method_create_with_all_params(self, client: AsyncFinch) -> None: account = await client.sandbox.connections.accounts.create( @@ -94,6 +99,7 @@ async def test_method_create_with_all_params(self, client: AsyncFinch) -> None: ) assert_matches_type(AccountCreateResponse, account, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize async def test_raw_response_create(self, client: AsyncFinch) -> None: response = await client.sandbox.connections.accounts.with_raw_response.create( diff --git a/tests/api_resources/sandbox/test_connections.py b/tests/api_resources/sandbox/test_connections.py index 160c0ad5..f505c56b 100644 --- a/tests/api_resources/sandbox/test_connections.py +++ b/tests/api_resources/sandbox/test_connections.py @@ -20,6 +20,7 @@ class TestConnections: loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize def test_method_create(self, client: Finch) -> None: connection = client.sandbox.connections.create( @@ -27,6 +28,7 @@ def test_method_create(self, client: Finch) -> None: ) assert_matches_type(ConnectionCreateResponse, connection, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize def test_method_create_with_all_params(self, client: Finch) -> None: connection = client.sandbox.connections.create( @@ -37,6 +39,7 @@ def test_method_create_with_all_params(self, client: Finch) -> None: ) assert_matches_type(ConnectionCreateResponse, connection, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize def test_raw_response_create(self, client: Finch) -> None: response = client.sandbox.connections.with_raw_response.create( @@ -52,6 +55,7 @@ class TestAsyncConnections: loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize async def test_method_create(self, client: AsyncFinch) -> None: connection = await client.sandbox.connections.create( @@ -59,6 +63,7 @@ async def test_method_create(self, client: AsyncFinch) -> None: ) assert_matches_type(ConnectionCreateResponse, connection, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize async def test_method_create_with_all_params(self, client: AsyncFinch) -> None: connection = await client.sandbox.connections.create( @@ -69,6 +74,7 @@ async def test_method_create_with_all_params(self, client: AsyncFinch) -> None: ) assert_matches_type(ConnectionCreateResponse, connection, path=["response"]) + @pytest.mark.skip(reason="Auth isn't setup correctly in this test") @parametrize async def test_raw_response_create(self, client: AsyncFinch) -> None: response = await client.sandbox.connections.with_raw_response.create( diff --git a/tests/api_resources/test_auth.py b/tests/api_resources/test_auth.py deleted file mode 100644 index f826ee79..00000000 --- a/tests/api_resources/test_auth.py +++ /dev/null @@ -1,71 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os - -import pytest - -from finch import Finch, AsyncFinch -from finch.types import CreateAccessTokenResponse -from tests.utils import assert_matches_type -from finch._client import Finch, AsyncFinch - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -access_token = "My Access Token" - - -class TestAuth: - strict_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=True) - loose_client = Finch(base_url=base_url, access_token=access_token, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - - @parametrize - def test_method_create_token(self, client: Finch) -> None: - auth = client.auth.create_token( - client_id="", - client_secret="", - code="", - redirect_uri="https://example.com", - ) - assert_matches_type(CreateAccessTokenResponse, auth, path=["response"]) - - @parametrize - def test_raw_response_create_token(self, client: Finch) -> None: - response = client.auth.with_raw_response.create_token( - client_id="", - client_secret="", - code="", - redirect_uri="https://example.com", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth = response.parse() - assert_matches_type(CreateAccessTokenResponse, auth, path=["response"]) - - -class TestAsyncAuth: - strict_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=True) - loose_client = AsyncFinch(base_url=base_url, access_token=access_token, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - - @parametrize - async def test_method_create_token(self, client: AsyncFinch) -> None: - auth = await client.auth.create_token( - client_id="", - client_secret="", - code="", - redirect_uri="https://example.com", - ) - assert_matches_type(CreateAccessTokenResponse, auth, path=["response"]) - - @parametrize - async def test_raw_response_create_token(self, client: AsyncFinch) -> None: - response = await client.auth.with_raw_response.create_token( - client_id="", - client_secret="", - code="", - redirect_uri="https://example.com", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth = response.parse() - assert_matches_type(CreateAccessTokenResponse, auth, path=["response"]) diff --git a/tests/test_client.py b/tests/test_client.py index f2b955cf..e7480def 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -315,7 +315,7 @@ def test_validate_headers(self) -> None: client2 = Finch(base_url=base_url, access_token=None, _strict_response_validation=True) with pytest.raises( TypeError, - match="Could not resolve authentication method. Expected the access_token to be set. Or for the `Authorization` headers to be explicitly omitted", + match="Could not resolve authentication method. Expected either access_token, sandbox_client_id or sandbox_client_secret to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted", ): client2._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1050,7 +1050,7 @@ def test_validate_headers(self) -> None: client2 = AsyncFinch(base_url=base_url, access_token=None, _strict_response_validation=True) with pytest.raises( TypeError, - match="Could not resolve authentication method. Expected the access_token to be set. Or for the `Authorization` headers to be explicitly omitted", + match="Could not resolve authentication method. Expected either access_token, sandbox_client_id or sandbox_client_secret to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted", ): client2._build_request(FinalRequestOptions(method="get", url="/foo"))