Skip to content
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

Introducing Database Class #18

Merged
merged 1 commit into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions arangoasync/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
JwtConnection,
JwtSuperuserConnection,
)
from arangoasync.database import Database
from arangoasync.database import StandardDatabase
from arangoasync.http import DefaultHTTPClient, HTTPClient
from arangoasync.resolver import HostResolver, get_resolver
from arangoasync.version import __version__
Expand Down Expand Up @@ -124,7 +124,7 @@ async def db(
token: Optional[JwtToken] = None,
verify: bool = False,
compression: Optional[CompressionManager] = None,
) -> Database:
) -> StandardDatabase:
"""Connects to a database and returns and API wrapper.

Args:
Expand All @@ -147,7 +147,7 @@ async def db(
client-level compression settings.

Returns:
Database: Database API wrapper.
StandardDatabase: Database API wrapper.

Raises:
ValueError: If the authentication is invalid.
Expand Down Expand Up @@ -198,4 +198,4 @@ async def db(
if verify:
await connection.ping()

return Database(connection)
return StandardDatabase(connection)
52 changes: 47 additions & 5 deletions arangoasync/database.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,59 @@
__all__ = [
"Database",
"StandardDatabase",
]

from arangoasync.connection import BaseConnection
import json
from typing import Any

from arangoasync.connection import Connection
from arangoasync.exceptions import ServerStatusError
from arangoasync.executor import ApiExecutor, DefaultApiExecutor
from arangoasync.request import Method, Request
from arangoasync.response import Response


class Database:
"""Database API."""

def __init__(self, connection: BaseConnection) -> None:
self._conn = connection
def __init__(self, executor: ApiExecutor) -> None:
self._executor = executor

@property
def conn(self) -> BaseConnection:
def connection(self) -> Connection:
"""Return the HTTP connection."""
return self._conn
return self._executor.connection

@property
def name(self) -> str:
"""Return the name of the current database."""
return self.connection.db_name

# TODO - user real return type
async def status(self) -> Any:
"""Query the server status.

Returns:
Json: Server status.

Raises:
ServerSatusError: If retrieval fails.
"""
request = Request(method=Method.GET, endpoint="/_admin/status")

# TODO
# - introduce specific return type for response_handler
# - introduce specific serializer and deserializer
def response_handler(resp: Response) -> Any:
if not resp.is_success:
raise ServerStatusError(resp, request)
return json.loads(resp.raw_body)

return await self._executor.execute(request, response_handler)


class StandardDatabase(Database):
"""Standard database API wrapper."""

def __init__(self, connection: Connection) -> None:
super().__init__(DefaultApiExecutor(connection))
4 changes: 4 additions & 0 deletions arangoasync/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,7 @@ class JWTRefreshError(ArangoClientError):

class ServerConnectionError(ArangoServerError):
"""Failed to connect to ArangoDB server."""


class ServerStatusError(ArangoServerError):
"""Failed to retrieve server status."""
43 changes: 43 additions & 0 deletions arangoasync/executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Callable, TypeVar

from arangoasync.connection import Connection
from arangoasync.request import Request
from arangoasync.response import Response

T = TypeVar("T")


class DefaultApiExecutor:
"""Default API executor.

Responsible for executing requests and handling responses.

Args:
connection: HTTP connection.
"""

def __init__(self, connection: Connection) -> None:
self._conn = connection

@property
def connection(self) -> Connection:
return self._conn

@property
def context(self) -> str:
return "default"

async def execute(
self, request: Request, response_handler: Callable[[Response], T]
) -> T:
"""Execute the request and handle the response.

Args:
request: HTTP request.
response_handler: HTTP response handler.
"""
response = await self._conn.send_request(request)
return response_handler(response)


ApiExecutor = DefaultApiExecutor
4 changes: 2 additions & 2 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def mock_method(*args, **kwargs):


@pytest.mark.asyncio
async def test_client_bad_auth_method(url, sys_db_name, root, password):
async def test_client_bad_auth_method(url, sys_db_name):
async with ArangoClient(hosts=url) as client:
with pytest.raises(ValueError):
await client.db(sys_db_name, auth_method="invalid")
Expand Down Expand Up @@ -89,7 +89,7 @@ async def test_client_jwt_auth(url, sys_db_name, root, password):
# successful authentication with auth only
async with ArangoClient(hosts=url) as client:
db = await client.db(sys_db_name, auth_method="jwt", auth=auth, verify=True)
token = db.conn.token
token = db.connection.token

# successful authentication with token only
async with ArangoClient(hosts=url) as client:
Expand Down
15 changes: 15 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest

from arangoasync.auth import Auth
from arangoasync.client import ArangoClient


@pytest.mark.asyncio
async def test_client_basic_auth(url, sys_db_name, root, password):
auth = Auth(username=root, password=password)

# TODO create a test database and user
async with ArangoClient(hosts=url) as client:
db = await client.db(sys_db_name, auth_method="basic", auth=auth, verify=True)
status = await db.status()
assert status["server"] == "arango"
Loading