From 6d49c6bd82f6591b394595c1bbf029e330331ac5 Mon Sep 17 00:00:00 2001 From: Danylo_Kriachkov Date: Wed, 10 Jun 2026 14:25:18 +0300 Subject: [PATCH] feat: add user resource and documentation for retrieving authenticated user info --- README.md | 18 +++++++ aidial_client/_client.py | 2 + aidial_client/resources/__init__.py | 3 ++ aidial_client/resources/user.py | 20 ++++++++ tests/resources/test_user.py | 79 +++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 aidial_client/resources/user.py create mode 100644 tests/resources/test_user.py diff --git a/README.md b/README.md index 98ffb61..767be76 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ - [Get Application by Id](#get-application-by-id) - [Models](#models) - [Get Model by Name](#get-model-by-name) + - [User](#user) + - [Get Authenticated User Info](#get-authenticated-user-info) - [Toolsets](#toolsets) - [Get Toolset by Id](#get-toolset-by-id) - [Resource Permissions](#resource-permissions) @@ -799,6 +801,22 @@ ModelInfo( ) ``` +### User + +#### Get Authenticated User Info + +To retrieve information about the currently authenticated user: + +```python +# Sync +user_info = client.user.info() + +# Async +user_info = await async_client.user.info() +``` + +The response shape may evolve depending on DIAL deployment settings, so this method returns a plain `dict[str, Any]`. + ### Toolsets #### Get Toolset by Id diff --git a/aidial_client/_client.py b/aidial_client/_client.py index 5c4907d..67b8ddf 100644 --- a/aidial_client/_client.py +++ b/aidial_client/_client.py @@ -122,6 +122,7 @@ def _init_resources(self) -> None: self.client_channel = resources.ClientChannel( http_client=self._http_client ) + self.user = resources.User(http_client=self._http_client) def _create_http_client(self) -> SyncHTTPClient: return SyncHTTPClient( @@ -213,6 +214,7 @@ def _init_resources(self) -> None: self.client_channel = resources.AsyncClientChannel( http_client=self._http_client ) + self.user = resources.AsyncUser(http_client=self._http_client) def _create_http_client(self) -> AsyncHTTPClient: return AsyncHTTPClient( diff --git a/aidial_client/resources/__init__.py b/aidial_client/resources/__init__.py index ed55b9a..170b700 100644 --- a/aidial_client/resources/__init__.py +++ b/aidial_client/resources/__init__.py @@ -10,6 +10,7 @@ ResourcePermissions, ) from aidial_client.resources.toolset import AsyncToolset, Toolset +from aidial_client.resources.user import AsyncUser, User from .application import Application, AsyncApplication from .bucket import AsyncBucket, Bucket @@ -40,4 +41,6 @@ "AsyncResourcePermissions", "ClientChannel", "AsyncClientChannel", + "User", + "AsyncUser", ] diff --git a/aidial_client/resources/user.py b/aidial_client/resources/user.py new file mode 100644 index 0000000..11e7b8d --- /dev/null +++ b/aidial_client/resources/user.py @@ -0,0 +1,20 @@ +from typing import Any, Dict + +from aidial_client._internal_types._http_request import FinalRequestOptions +from aidial_client.resources.base import AsyncResource, Resource + + +class User(Resource): + def info(self) -> Dict[str, Any]: + return self.http_client.request( + cast_to=dict, + options=FinalRequestOptions(method="GET", url="v1/user/info"), + ) + + +class AsyncUser(AsyncResource): + async def info(self) -> Dict[str, Any]: + return await self.http_client.request( + cast_to=dict, + options=FinalRequestOptions(method="GET", url="v1/user/info"), + ) diff --git a/tests/resources/test_user.py b/tests/resources/test_user.py new file mode 100644 index 0000000..848f320 --- /dev/null +++ b/tests/resources/test_user.py @@ -0,0 +1,79 @@ +import httpx +import pytest + +from aidial_client import AsyncDial, Dial +from aidial_client._exception import DialException +from tests.client_mock import get_async_client_mock, get_client_mock + +BASE_URL = "http://dial.core" + +USER_INFO_MOCK = { + "sub": "user-123", + "email": "user@example.com", + "name": "Test User", +} + + +def test_get_user_info(): + client = get_client_mock(status_code=200, json_mock=USER_INFO_MOCK) + result = client.user.info() + assert result == USER_INFO_MOCK + + +@pytest.mark.asyncio +async def test_async_get_user_info(): + client = get_async_client_mock(status_code=200, json_mock=USER_INFO_MOCK) + result = await client.user.info() + assert result == USER_INFO_MOCK + + +def test_get_user_info_request_method_and_url(): + captured: list[httpx.Request] = [] + client = Dial(api_key="dummy", base_url=BASE_URL) + + def send_mock(request: httpx.Request, **kwargs): + captured.append(request) + return httpx.Response(200, request=request, json=USER_INFO_MOCK) + + client._http_client._internal_http_client.send = send_mock + client.user.info() + + assert len(captured) == 1 + assert captured[0].method == "GET" + assert captured[0].url.path == "/v1/user/info" + + +@pytest.mark.asyncio +async def test_async_get_user_info_request_method_and_url(): + captured: list[httpx.Request] = [] + client = AsyncDial(api_key="dummy", base_url=BASE_URL) + + async def send_mock(request: httpx.Request, **kwargs): + captured.append(request) + return httpx.Response(200, request=request, json=USER_INFO_MOCK) + + client._http_client._internal_http_client.send = send_mock + await client.user.info() + + assert len(captured) == 1 + assert captured[0].method == "GET" + assert captured[0].url.path == "/v1/user/info" + + +def test_get_user_info_http_error(): + client = get_client_mock( + status_code=401, + json_mock={"error": {"message": "Unauthorized", "type": "auth_error"}}, + ) + with pytest.raises(DialException): + client.user.info() + + +@pytest.mark.asyncio +async def test_async_get_user_info_http_error(): + client = get_async_client_mock( + status_code=401, + json_mock={"error": {"message": "Unauthorized", "type": "auth_error"}}, + ) + with pytest.raises(DialException): + await client.user.info()