-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of HTTP connector
- Loading branch information
1 parent
662266a
commit 10ea58f
Showing
9 changed files
with
318 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
"""HTTP-based tap class for Singer SDK.""" | ||
|
||
from __future__ import annotations | ||
|
||
import typing as t | ||
|
||
import requests | ||
|
||
from singer_sdk.authenticators import NoopAuth | ||
from singer_sdk.connectors.base import BaseConnector | ||
|
||
if t.TYPE_CHECKING: | ||
import sys | ||
|
||
from requests.adapters import BaseAdapter | ||
|
||
if sys.version_info >= (3, 10): | ||
from typing import TypeAlias # noqa: ICN003 | ||
else: | ||
from typing_extensions import TypeAlias | ||
|
||
_Auth: TypeAlias = t.Callable[[requests.PreparedRequest], requests.PreparedRequest] | ||
|
||
|
||
class HTTPConnector(BaseConnector[requests.Session]): | ||
"""Base class for all HTTP-based connectors.""" | ||
|
||
def __init__(self, config: t.Mapping[str, t.Any] | None) -> None: | ||
"""Initialize the HTTP connector. | ||
Args: | ||
config: Connector configuration parameters. | ||
""" | ||
super().__init__(config) | ||
self._session = self.get_session() | ||
self.refresh_auth() | ||
|
||
def get_connection(self, *, authenticate: bool = True) -> requests.Session: | ||
"""Return a new HTTP session object. | ||
Adds adapters and optionally authenticates the session. | ||
Args: | ||
authenticate: Whether to authenticate the request. | ||
Returns: | ||
A new HTTP session object. | ||
""" | ||
for prefix, adapter in self.adapters.items(): | ||
self._session.mount(prefix, adapter) | ||
|
||
self._session.auth = self._auth if authenticate else None | ||
|
||
return self._session | ||
|
||
def get_session(self) -> requests.Session: | ||
"""Return a new HTTP session object. | ||
Returns: | ||
A new HTTP session object. | ||
""" | ||
return requests.Session() | ||
|
||
def get_authenticator(self) -> _Auth: | ||
"""Authenticate the HTTP session. | ||
Returns: | ||
An auth callable. | ||
""" | ||
return NoopAuth() | ||
|
||
def refresh_auth(self) -> None: | ||
"""Refresh the HTTP session authentication.""" | ||
self._auth = self.get_authenticator() | ||
|
||
@property | ||
def adapters(self) -> dict[str, BaseAdapter]: | ||
"""Return a mapping of URL prefixes to adapter objects. | ||
Returns: | ||
A mapping of URL prefixes to adapter objects. | ||
""" | ||
return {} | ||
|
||
@property | ||
def default_request_kwargs(self) -> dict[str, t.Any]: | ||
"""Return default kwargs for HTTP requests. | ||
Returns: | ||
A mapping of default kwargs for HTTP requests. | ||
""" | ||
return {} | ||
|
||
def request( | ||
self, | ||
*args: t.Any, | ||
authenticate: bool = True, | ||
**kwargs: t.Any, | ||
) -> requests.Response: | ||
"""Make an HTTP request. | ||
Args: | ||
*args: Positional arguments to pass to the request method. | ||
authenticate: Whether to authenticate the request. | ||
**kwargs: Keyword arguments to pass to the request method. | ||
Returns: | ||
The HTTP response object. | ||
""" | ||
with self._connect(authenticate=authenticate) as session: | ||
kwargs = {**self.default_request_kwargs, **kwargs} | ||
return session.request(*args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
"""Base class for all connectors.""" | ||
|
||
from __future__ import annotations | ||
|
||
import abc | ||
import typing as t | ||
from contextlib import contextmanager | ||
|
||
from singer_sdk.helpers._compat import Protocol | ||
|
||
_T = t.TypeVar("_T", covariant=True) | ||
|
||
|
||
class ContextManagerProtocol(Protocol[_T]): | ||
"""Protocol for context manager enter/exit.""" | ||
|
||
def __enter__(self) -> _T: # noqa: D105 | ||
... | ||
|
||
def __exit__(self, *args: t.Any) -> None: # noqa: D105 | ||
... | ||
|
||
|
||
_C = t.TypeVar("_C", bound=ContextManagerProtocol) | ||
|
||
|
||
class BaseConnector(abc.ABC, t.Generic[_C]): | ||
"""Base class for all connectors.""" | ||
|
||
def __init__(self, config: t.Mapping[str, t.Any] | None) -> None: | ||
"""Initialize the connector. | ||
Args: | ||
config: Plugin configuration parameters. | ||
""" | ||
self._config = config or {} | ||
|
||
@property | ||
def config(self) -> t.Mapping: | ||
"""Return the connector configuration. | ||
Returns: | ||
A mapping of configuration parameters. | ||
""" | ||
return self._config | ||
|
||
@contextmanager | ||
def _connect(self, *args: t.Any, **kwargs: t.Any) -> t.Generator[_C, None, None]: | ||
"""Connect to the destination. | ||
Args: | ||
args: Positional arguments to pass to the connection method. | ||
kwargs: Keyword arguments to pass to the connection method. | ||
Yields: | ||
A connection object. | ||
""" | ||
with self.get_connection(*args, **kwargs) as connection: | ||
yield connection | ||
|
||
@abc.abstractmethod | ||
def get_connection(self, *args: t.Any, **kwargs: t.Any) -> _C: | ||
"""Connect to the destination. | ||
Args: | ||
args: Positional arguments to pass to the connection method. | ||
kwargs: Keyword arguments to pass to the connection method. | ||
""" | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Oops, something went wrong.