Skip to content

Commit ac996eb

Browse files
feat(api): manual updates (#9)
1 parent 5054460 commit ac996eb

File tree

4 files changed

+242
-46
lines changed

4 files changed

+242
-46
lines changed

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,34 @@ pip install --pre openint
2424
The full API of this library can be found in [api.md](api.md).
2525

2626
```python
27+
import os
2728
from openint import Openint
2829

29-
client = Openint()
30+
client = Openint(
31+
api_key=os.environ.get("OPENINT_API_KEY"), # This is the default and can be omitted
32+
)
3033

3134
response = client.get_connection()
3235
print(response.items)
3336
```
3437

38+
While you can provide an `api_key` keyword argument,
39+
we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
40+
to add `OPENINT_API_KEY="My API Key"` to your `.env` file
41+
so that your API Key is not stored in source control.
42+
3543
## Async usage
3644

3745
Simply import `AsyncOpenint` instead of `Openint` and use `await` with each API call:
3846

3947
```python
48+
import os
4049
import asyncio
4150
from openint import AsyncOpenint
4251

43-
client = AsyncOpenint()
52+
client = AsyncOpenint(
53+
api_key=os.environ.get("OPENINT_API_KEY"), # This is the default and can be omitted
54+
)
4455

4556

4657
async def main() -> None:

src/openint/_client.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,29 @@ def __init__(
128128
def qs(self) -> Querystring:
129129
return Querystring(array_format="comma")
130130

131+
@property
132+
@override
133+
def auth_headers(self) -> dict[str, str]:
134+
if self._organization_auth:
135+
return self._organization_auth
136+
if self._customer_auth:
137+
return self._customer_auth
138+
return {}
139+
140+
@property
141+
def _organization_auth(self) -> dict[str, str]:
142+
api_key = self.api_key
143+
if api_key is None:
144+
return {}
145+
return {"authorization": api_key}
146+
147+
@property
148+
def _customer_auth(self) -> dict[str, str]:
149+
customer_token = self.customer_token
150+
if customer_token is None:
151+
return {}
152+
return {"Authorization": f"Bearer {customer_token}"}
153+
131154
@property
132155
@override
133156
def default_headers(self) -> dict[str, str | Omit]:
@@ -137,6 +160,22 @@ def default_headers(self) -> dict[str, str | Omit]:
137160
**self._custom_headers,
138161
}
139162

163+
@override
164+
def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
165+
if self.api_key and headers.get("authorization"):
166+
return
167+
if isinstance(custom_headers.get("authorization"), Omit):
168+
return
169+
170+
if self.customer_token and headers.get("Authorization"):
171+
return
172+
if isinstance(custom_headers.get("Authorization"), Omit):
173+
return
174+
175+
raise TypeError(
176+
'"Could not resolve authentication method. Expected either api_key or customer_token to be set. Or for one of the `authorization` or `Authorization` headers to be explicitly omitted"'
177+
)
178+
140179
def copy(
141180
self,
142181
*,
@@ -800,6 +839,29 @@ def __init__(
800839
def qs(self) -> Querystring:
801840
return Querystring(array_format="comma")
802841

842+
@property
843+
@override
844+
def auth_headers(self) -> dict[str, str]:
845+
if self._organization_auth:
846+
return self._organization_auth
847+
if self._customer_auth:
848+
return self._customer_auth
849+
return {}
850+
851+
@property
852+
def _organization_auth(self) -> dict[str, str]:
853+
api_key = self.api_key
854+
if api_key is None:
855+
return {}
856+
return {"authorization": api_key}
857+
858+
@property
859+
def _customer_auth(self) -> dict[str, str]:
860+
customer_token = self.customer_token
861+
if customer_token is None:
862+
return {}
863+
return {"Authorization": f"Bearer {customer_token}"}
864+
803865
@property
804866
@override
805867
def default_headers(self) -> dict[str, str | Omit]:
@@ -809,6 +871,22 @@ def default_headers(self) -> dict[str, str | Omit]:
809871
**self._custom_headers,
810872
}
811873

874+
@override
875+
def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
876+
if self.api_key and headers.get("authorization"):
877+
return
878+
if isinstance(custom_headers.get("authorization"), Omit):
879+
return
880+
881+
if self.customer_token and headers.get("Authorization"):
882+
return
883+
if isinstance(custom_headers.get("Authorization"), Omit):
884+
return
885+
886+
raise TypeError(
887+
'"Could not resolve authentication method. Expected either api_key or customer_token to be set. Or for one of the `authorization` or `Authorization` headers to be explicitly omitted"'
888+
)
889+
812890
def copy(
813891
self,
814892
*,

tests/conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
2828

2929
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
3030

31+
api_key = "My API Key"
32+
3133

3234
@pytest.fixture(scope="session")
3335
def client(request: FixtureRequest) -> Iterator[Openint]:
3436
strict = getattr(request, "param", True)
3537
if not isinstance(strict, bool):
3638
raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")
3739

38-
with Openint(base_url=base_url, _strict_response_validation=strict) as client:
40+
with Openint(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
3941
yield client
4042

4143

@@ -45,5 +47,5 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenint]:
4547
if not isinstance(strict, bool):
4648
raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")
4749

48-
async with AsyncOpenint(base_url=base_url, _strict_response_validation=strict) as client:
50+
async with AsyncOpenint(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
4951
yield client

0 commit comments

Comments
 (0)