Skip to content

eng: Add client helpers to the generated template for alpha and beta endpoints BNCH-111588 #228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: prod/2.x
Choose a base branch
from
25 changes: 25 additions & 0 deletions end_to_end_tests/golden-record/my_test_api_client/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ssl
import urllib.parse
from typing import Any, Optional, Union

import httpx
Expand Down Expand Up @@ -266,3 +267,27 @@ async def __aenter__(self) -> "AuthenticatedClient":
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
await self.get_async_httpx_client().__aexit__(*args, **kwargs)


def replace_client_path(client: Client, base_path: str) -> Client:
"""Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
parsed = urllib.parse.urlparse(client._base_url)
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
updated_url = parsed._replace(path=base_path)
client._base_url = updated_url.geturl()
return client


def v3_stable_client(client: Client) -> Client:
"""Override a client's base URL with a v2 stable path."""
return replace_client_path(client, "api/v3-draft")


def v3_alpha_client(client: Client) -> Client:
"""Override a client's base URL with a v2-alpha path."""
return replace_client_path(client, "api/v3-alpha")


def v3_beta_client(client: Client) -> Client:
"""Override a client's base URL with a v2-beta path."""
return replace_client_path(client, "api/v3-beta")
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ssl
import urllib.parse
from typing import Any, Optional, Union

import httpx
Expand Down Expand Up @@ -266,3 +267,27 @@ async def __aenter__(self) -> "AuthenticatedClient":
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
await self.get_async_httpx_client().__aexit__(*args, **kwargs)


def replace_client_path(client: Client, base_path: str) -> Client:
"""Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
parsed = urllib.parse.urlparse(client._base_url)
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
updated_url = parsed._replace(path=base_path)
client._base_url = updated_url.geturl()
return client


def v3_stable_client(client: Client) -> Client:
"""Override a client's base URL with a v2 stable path."""
return replace_client_path(client, "api/v3-draft")


def v3_alpha_client(client: Client) -> Client:
"""Override a client's base URL with a v2-alpha path."""
return replace_client_path(client, "api/v3-alpha")


def v3_beta_client(client: Client) -> Client:
"""Override a client's base URL with a v2-beta path."""
return replace_client_path(client, "api/v3-beta")
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ssl
import urllib.parse
from typing import Any, Optional, Union

import httpx
Expand Down Expand Up @@ -266,3 +267,27 @@ async def __aenter__(self) -> "AuthenticatedClient":
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
await self.get_async_httpx_client().__aexit__(*args, **kwargs)


def replace_client_path(client: Client, base_path: str) -> Client:
"""Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
parsed = urllib.parse.urlparse(client._base_url)
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
updated_url = parsed._replace(path=base_path)
client._base_url = updated_url.geturl()
return client


def v3_stable_client(client: Client) -> Client:
"""Override a client's base URL with a v2 stable path."""
return replace_client_path(client, "api/v3-draft")


def v3_alpha_client(client: Client) -> Client:
"""Override a client's base URL with a v2-alpha path."""
return replace_client_path(client, "api/v3-alpha")


def v3_beta_client(client: Client) -> Client:
"""Override a client's base URL with a v2-beta path."""
return replace_client_path(client, "api/v3-beta")
25 changes: 25 additions & 0 deletions integration-tests/integration_tests/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ssl
import urllib.parse
from typing import Any, Optional, Union

import httpx
Expand Down Expand Up @@ -266,3 +267,27 @@ async def __aenter__(self) -> "AuthenticatedClient":
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
await self.get_async_httpx_client().__aexit__(*args, **kwargs)


def replace_client_path(client: Client, base_path: str) -> Client:
"""Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
parsed = urllib.parse.urlparse(client._base_url)
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
updated_url = parsed._replace(path=base_path)
client._base_url = updated_url.geturl()
return client


def v3_stable_client(client: Client) -> Client:
"""Override a client's base URL with a v2 stable path."""
return replace_client_path(client, "api/v3-draft")


def v3_alpha_client(client: Client) -> Client:
"""Override a client's base URL with a v2-alpha path."""
return replace_client_path(client, "api/v3-alpha")


def v3_beta_client(client: Client) -> Client:
"""Override a client's base URL with a v2-beta path."""
return replace_client_path(client, "api/v3-beta")
24 changes: 24 additions & 0 deletions openapi_python_client/templates/client.py.jinja
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ssl
import urllib.parse
from typing import Any, Union, Optional

from attrs import define, field, evolve
Expand Down Expand Up @@ -189,3 +190,26 @@ class AuthenticatedClient:

{{ builders("AuthenticatedClient") }}
{{ httpx_stuff("AuthenticatedClient", "self._headers[self.auth_header_name] = f\"{self.prefix} {self.token}\" if self.prefix else self.token") }}

def replace_client_path(client: Client, base_path: str) -> Client:
"""Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
parsed = urllib.parse.urlparse(client._base_url)
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
updated_url = parsed._replace(path=base_path)
client._base_url = updated_url.geturl()
return client


def v3_stable_client(client: Client) -> Client:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we want to expose these in a lowish level library rather than in benchling-sdk? IIRC there's some "invariant" code that doesn't get generated that can be referenced from generated code. Could that be another location for this?

This is far too specific to our use of APIs to include in this library. Any functionality we write should be general to APIs. You could argue that replace_client_path could live here but... something tells me there ought to be a way to specify this in the caller (in benchling-sdk) instead?

"""Override a client's base URL with a v2 stable path."""
return replace_client_path(client, "api/v3-draft")


def v3_alpha_client(client: Client) -> Client:
"""Override a client's base URL with a v2-alpha path."""
return replace_client_path(client, "api/v3-alpha")


def v3_beta_client(client: Client) -> Client:
"""Override a client's base URL with a v2-beta path."""
return replace_client_path(client, "api/v3-beta")