Skip to content

Commit ec23a81

Browse files
committed
add feat verify_api_token
1 parent 9575a89 commit ec23a81

File tree

7 files changed

+87
-29
lines changed

7 files changed

+87
-29
lines changed

eventsourcingdb/client.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,28 @@ async def ping(self) -> None:
8383
raise ServerError(f"Received unexpected response: {response_body}")
8484

8585
async def verify_api_token(self) -> None:
86-
# POST Request. Ist wie ping, aber mit POST. Authorization header token
87-
# mitschicken. Token gültig dann event, Token ungültig 401
88-
raise NotImplementedError("verify_api_token is not implemented yet.")
86+
request_body = json.dumps({})
87+
88+
response: Response = await self.http_client.post(
89+
path='/api/v1/verify-api-token',
90+
request_body=request_body,
91+
)
92+
async with response:
93+
if response.status_code != HTTPStatus.OK:
94+
raise ServerError(
95+
f'Failed to verify API token: {response}'
96+
)
97+
98+
response_data = await response.body.read()
99+
response_data = bytes.decode(response_data, encoding='utf-8')
100+
response_json = json.loads(response_data)
101+
102+
if not isinstance(response_json, dict) or 'type' not in response_json:
103+
raise ServerError('Failed to parse response: {response}')
104+
105+
expected_event_type = 'io.eventsourcingdb.api.api-token-verified'
106+
if response_json.get('type') != expected_event_type:
107+
raise ServerError(f'Failed to verify API token: {response}')
89108

90109
async def write_events(
91110
self,
@@ -111,7 +130,7 @@ async def write_events(
111130
if response.status_code != HTTPStatus.OK:
112131
raise ServerError(
113132
f'Unexpected response status: '
114-
)
133+
)
115134

116135
response_data = await response.body.read()
117136
response_data = bytes.decode(response_data, encoding='utf-8')
@@ -144,7 +163,7 @@ async def read_events(
144163
if response.status_code != HTTPStatus.OK:
145164
raise ServerError(
146165
f'Unexpected response status: {response}'
147-
)
166+
)
148167
async for raw_message in response.body:
149168
message = parse_raw_message(raw_message)
150169

@@ -209,7 +228,7 @@ async def run_eventql_query(self, query: str) -> AsyncGenerator[Any, None]:
209228

210229
if message.get('type') == 'row':
211230
payload = message['payload']
212-
231+
213232
yield payload
214233
continue
215234

@@ -228,7 +247,7 @@ async def observe_events(
228247
'options': options.to_json()
229248
})
230249

231-
response : Response = await self.http_client.post(
250+
response: Response = await self.http_client.post(
232251
path='/api/v1/observe-events',
233252
request_body=request_body,
234253
)
@@ -296,7 +315,7 @@ async def read_subjects(
296315
'baseSubject': base_subject
297316
})
298317

299-
response : Response = await self.http_client.post(
318+
response: Response = await self.http_client.post(
300319
path='/api/v1/read-subjects',
301320
request_body=request_body,
302321
)

eventsourcingdb/http_client/http_client.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async def close(self):
4343

4444
async def post(self, path: str, request_body: str) -> Response:
4545
if self.__session is None:
46-
raise CustomError()
46+
await self.initialize()
4747

4848
url_path = url.join_segments(self.__base_url, path)
4949
headers = get_post_headers(self.__api_token)
@@ -58,14 +58,13 @@ async def post(self, path: str, request_body: str) -> Response:
5858

5959
return response
6060

61-
6261
async def get(
6362
self,
6463
path: str,
6564
with_authorization: bool = True,
6665
) -> Response:
6766
if self.__session is None:
68-
raise CustomError()
67+
raise CustomError("HTTP client session not initialized. Call initialize() before making requests.")
6968

7069
async def __request_executor() -> Response:
7170
url_path = url.join_segments(self.__base_url, path)

tests/shared/database.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def __init__(
1818
self,
1919
create_key,
2020
with_authorization_client: Client,
21-
with_invalid_url_client: Client ,
21+
with_invalid_url_client: Client,
2222
):
2323
assert create_key == Database.__create_key, \
2424
'Database objects must be created using Database.create.'
@@ -114,6 +114,14 @@ def get_client(self, client_type: str = CLIENT_TYPE_WITH_AUTH) -> Client:
114114

115115
raise ValueError(f"Unknown client type: {client_type}")
116116

117+
def get_base_url(self) -> str:
118+
"""Get the base URL of the EventSourceDB container."""
119+
return self.__container.get_base_url()
120+
121+
def get_api_token(self) -> str:
122+
"""Get the API token for the EventSourceDB container."""
123+
return self.__container.get_api_token()
124+
117125
async def stop(self) -> None:
118126
# Use walrus operator for concise access and check
119127
if (container := getattr(self.__class__, '_Database__container', None)):

tests/test_observe_events.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,12 @@ async def test_cancelling_observe_events_async(
406406
async def process_events():
407407
nonlocal events_processed
408408
async for _ in client.observe_events(
409-
'/',
410-
ObserveEventsOptions(
411-
recursive=True,
412-
from_latest_event=None
413-
)
414-
):
409+
'/',
410+
ObserveEventsOptions(
411+
recursive=True,
412+
from_latest_event=None
413+
)
414+
):
415415
events_processed += 1
416416
await asyncio.sleep(0.25)
417417

tests/test_register_event_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ async def test_throws_error_if_schema_is_invalid(
8686
):
8787
client = database.get_client()
8888

89-
with pytest.raises(ServerError, match='value must be "object"'):
89+
with pytest.raises(ServerError, match='value must be "object"'):
9090
await client.register_event_schema(
9191
"com.gornisht.ekht",
9292
{

tests/test_run_eventql_query.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ async def test_reads_no_rows_if_query_does_not_return_any_rows(
2929
database: Database
3030
):
3131
client = database.get_client()
32-
32+
3333
did_read_rows = False
3434
async for _ in client.run_eventql_query('FROM e IN events PROJECT INTO e'):
3535
did_read_rows = True
36-
36+
3737
assert did_read_rows is False
3838

3939
@staticmethod
@@ -42,7 +42,7 @@ async def test_reads_all_rows_the_query_returns(
4242
database: Database
4343
):
4444
client = database.get_client()
45-
45+
4646
first_event = EventCandidate(
4747
source='https://www.eventsourcingdb.io',
4848
subject='/test',
@@ -51,7 +51,7 @@ async def test_reads_all_rows_the_query_returns(
5151
'value': 23,
5252
},
5353
)
54-
54+
5555
second_event = EventCandidate(
5656
source='https://www.eventsourcingdb.io',
5757
subject='/test',
@@ -60,21 +60,20 @@ async def test_reads_all_rows_the_query_returns(
6060
'value': 42,
6161
},
6262
)
63-
63+
6464
await client.write_events([first_event, second_event])
65-
65+
6666
rows_read = []
6767
async for row in client.run_eventql_query('FROM e IN events PROJECT INTO e'):
6868
rows_read.append(row)
69-
69+
7070
assert len(rows_read) == 2
71-
71+
7272
first_row = rows_read[0]
7373
# Use dictionary access instead of attribute access
7474
assert first_row['id'] == '0'
7575
assert first_row['data']['value'] == 23
76-
76+
7777
second_row = rows_read[1]
7878
assert second_row['id'] == '1'
7979
assert second_row['data']['value'] == 42
80-

tests/test_verify_api_token.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
3+
from eventsourcingdb.client import Client
4+
from eventsourcingdb.errors.custom_error import CustomError
5+
from eventsourcingdb.errors.server_error import ServerError
6+
7+
from .shared.database import Database
8+
9+
10+
class TestVerifyApiToken:
11+
@staticmethod
12+
@pytest.mark.asyncio
13+
async def test_does_not_throw_if_token_is_valid(
14+
database: Database
15+
):
16+
client = database.get_client()
17+
await client.verify_api_token()
18+
19+
20+
@staticmethod
21+
@pytest.mark.asyncio
22+
async def test_throws_error_if_token_is_invalid(
23+
database: Database
24+
):
25+
base_url = database.get_base_url()
26+
27+
valid_token = database.get_api_token()
28+
invalid_token = f"{valid_token}-invalid"
29+
30+
client = Client(base_url=base_url, api_token=invalid_token)
31+
32+
with pytest.raises(ServerError, match="Failed to verify API token"):
33+
await client.verify_api_token()

0 commit comments

Comments
 (0)