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()
Metadata
Release files for ahasura 1.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ahasura-1.4.2.tar.gz | 5.9 kB | Without provenance | Details for ahasura-1.4.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ahasura-1.4.2-py3-none-any.whl | Python 3 | none | any | Details for ahasura-1.4.2-py3-none-any.whl |
Total release size: 11.6 kB
Release files / ahasura-1.4.2.tar.gz
| Download URL | ahasura-1.4.2.tar.gz |
|---|---|
| Size | 5.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c19e907c3685b8c46d00311e0dcbe4dbd887c17338a280f963b79f4fbd6ab827
|
|
BLAKE2b-256 checksum How to use checksums |
4f3630a052933e2bab74f41c0a67b31f855ae6134045e5e92f20690c389e2c85
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.1.13 CPython/3.8.13 Linux/5.4.109+
|
Release files / ahasura-1.4.2-py3-none-any.whl
| Download URL | ahasura-1.4.2-py3-none-any.whl |
|---|---|
| Size | 5.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d4c8822be1828bbbded1a53decad03f818bacad721402e38e4ededcc3a2c52ed
|
|
BLAKE2b-256 checksum How to use checksums |
15b5f4e3c28f52ecfff4b634053c91fc056f98c8ddfefc4b56e34a3111078044
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.1.13 CPython/3.8.13 Linux/5.4.109+
|
Security for ahasura 1.4.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.