Async and sync Hasura client
Project description
ahasura
Async and sync Hasura client.
Install
ahasura is available on PyPi:
pip install ahasura
# Or
poetry add ahasura
Quick example
from ahasura import ADMIN, Hasura
hasura = Hasura("http://localhost:8080", admin_secret="fake secret")
data = hasura(
"""
query($id: uuid!) {
item_by_pk(id: $id) {
name
}
}
""",
auth=ADMIN,
id="00000000-0000-0000-0000-000000000001",
)
item = data["item_by_pk"]
assert item["name"] == "Some name"
Create client
hasura = Hasura(...)- Args:
endpoint: str-HASURA_GRAPHQL_ENDPOINT, without trailing/or/v1/graphqladmin_secret: Optional[str]-HASURA_GRAPHQL_ADMIN_SECRET, required forauth=ADMINonlytimeout: Optional[float] = 10- Seconds of network inactivity allowed.Nonedisables the timeout
hasuraclient just keeps the configuration above, so you can reuse global client(s)- Shortcuts:
hasura(...)is a shortcut for sync GraphQL client:hasura.gql(...)- You can define a shortcut for Async GraphQL client:
ahasura = hasura.agql
Execute GraphQL query
- With shortcuts:
- Sync:
data = hasura(...) - Async:
data = await ahasura(...)
- Sync:
- Without shortcuts:
- Sync:
data = hasura.gql(...) - Async:
data = await hasura.agql(...)
- Sync:
- Args:
query: str- GraphQL query, e.g.query { item { id }}auth: str- EitherADMINor value ofAuthorizationheader, e.g.Bearer {JWT}headers: Optional[Dict[str, str]]- Custom headers, if any**variables- Variables used inquery, if any
- Returns: GraphQL response data, e.g.
{"item": [{"id": "..."}]} - Raises:
HasuraError- If JSON response from Hasura containserrorskey
Execute SQL query
- Sync:
rows = hasura.sql(...) - Async:
rows = await hasura.asql(...) - Args:
query: str- SQL query, e.g.SELECT "id" FROM "item"headers: Optional[Dict[str, str]]- Custom headers, if any
- Returns:
- Rows selected by
SELECTquery, e.g.[{"id": "..."}] - Or
[{"ok": True}]for non-SELECTquery
- Rows selected by
- Raises:
HasuraError- If JSON response from Hasura containserrorkey
Auth
- SQL queries are admin-only
- GraphQL queries can use both admin and non-admin
auth auth=ADMINis not default, because:sudois not default- It's easy to forget to propagate
Authorizationheader of the user to Hasura - Declarative Hasura permissions are better than checking permissions in Python
- When we set Hasura permissions, we should test them for each role supported
How to test
test_item.py
from typing import Any, Dict
from ahasura import Hasura, HasuraError
import pytest
def test_reader_reads_item_ok(
hasura: Hasura,
reader_auth: str,
ok_item: Dict[str, Any],
) -> None:
data = hasura(
"""
query($id: uuid!) {
item_by_pk(id: $id) {
name
}
}
""",
auth=reader_auth,
id=ok_item["id"],
)
item = data["item_by_pk"]
assert item["name"] == "Some name"
def test_error(hasura: Hasura, reader_auth: str) -> None:
with pytest.raises(HasuraError) as error:
hasura("bad query", auth=reader_auth)
assert error.value.response == {"errors": [...]}
conftest.py
from typing import Any, Dict, Generator, List
from ahasura import ADMIN, Hasura
import jwt
import pytest
_TABLE_NAMES = ["item"]
@pytest.fixture(scope="session")
def hasura() -> Hasura:
return Hasura("http://localhost:8080", admin_secret="fake secret")
@pytest.fixture(scope="session")
def reader_auth() -> str:
decoded_token = ...
encoded_token = jwt.encode(decoded_token, "")
return f"Bearer {encoded_token}"
@pytest.fixture(scope="session")
def test_row_ids() -> List[str]:
"""
When a test function creates a row in any table,
it should append this `row["id"]` to `test_row_ids`
UUIDs are unique across all tables with enough probability
"""
return []
@pytest.fixture(scope="function")
def ok_item(hasura: Hasura, test_row_ids: List[str]) -> Dict[str, Any]:
data = hasura(
"""
mutation($item: item_insert_input!) {
insert_item_one(object: $item) {
id
name
}
}
""",
auth=ADMIN,
name="Some name",
)
item: Dict[str, Any] = data["insert_item_one"]
test_row_ids.append(item["id"])
return item
@pytest.fixture(scope="function", autouse=True)
def cleanup(hasura: Hasura, test_row_ids: List[str]) -> Generator[None, None, None]:
"""
When the test function ends,
this autouse fixture deletes all test rows from all tables
"""
yield
if test_row_ids:
for table_name in _TABLE_NAMES:
hasura(
"""
mutation($ids: [uuid!]!) {
delete_{table_name}(where: {id: {_in: $ids}}) {
affected_rows
}
}
""".replace(
"{table_name}", table_name
),
auth=ADMIN,
ids=test_row_ids,
)
test_row_ids.clear()
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
ahasura-1.4.2.tar.gz
(5.9 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ahasura-1.4.2.tar.gz.
File metadata
- Download URL: ahasura-1.4.2.tar.gz
- Upload date:
- Size: 5.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.13 CPython/3.8.13 Linux/5.4.109+
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c19e907c3685b8c46d00311e0dcbe4dbd887c17338a280f963b79f4fbd6ab827
|
|
| MD5 |
5305fe5bbe677dcdd8039ae129ca8509
|
|
| BLAKE2b-256 |
4f3630a052933e2bab74f41c0a67b31f855ae6134045e5e92f20690c389e2c85
|
File details
Details for the file ahasura-1.4.2-py3-none-any.whl.
File metadata
- Download URL: ahasura-1.4.2-py3-none-any.whl
- Upload date:
- Size: 5.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.13 CPython/3.8.13 Linux/5.4.109+
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4c8822be1828bbbded1a53decad03f818bacad721402e38e4ededcc3a2c52ed
|
|
| MD5 |
58149d1be0585d453e4e64c35a3df0ea
|
|
| BLAKE2b-256 |
15b5f4e3c28f52ecfff4b634053c91fc056f98c8ddfefc4b56e34a3111078044
|