Metadata-Version: 2.4
Name: hubuum-client
Version: 0.0.1
Summary: Modern typed Python client for Hubuum server v0.0.3
Project-URL: Homepage, https://github.com/hubuum/hubuum-client-python
Project-URL: Documentation, https://github.com/hubuum/hubuum-client-python/tree/main/docs
Project-URL: Issues, https://github.com/hubuum/hubuum-client-python/issues
Project-URL: Repository, https://github.com/hubuum/hubuum-client-python
Author: Hubuum contributors
License-Expression: MIT
License-File: LICENSE
Keywords: api,async,client,cmdb,hubuum
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.8
Provides-Extra: dev
Requires-Dist: bandit<2,>=1.9.4; extra == 'dev'
Requires-Dist: mkdocs-material<10,>=9.5; extra == 'dev'
Requires-Dist: mkdocstrings[python]<2,>=1.0.6; extra == 'dev'
Requires-Dist: mypy<3,>=2.3.0; extra == 'dev'
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'dev'
Requires-Dist: pytest-cov<8,>=6; extra == 'dev'
Requires-Dist: pytest<10,>=8.3; extra == 'dev'
Requires-Dist: respx<1,>=0.22; extra == 'dev'
Requires-Dist: ruff<1,>=0.9; extra == 'dev'
Requires-Dist: zizmor<2,>=1.27; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material<10,>=9.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]<2,>=1.0.6; extra == 'docs'
Provides-Extra: test
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'test'
Requires-Dist: pytest-cov<8,>=6; extra == 'test'
Requires-Dist: pytest<10,>=8.3; extra == 'test'
Requires-Dist: respx<1,>=0.22; extra == 'test'
Description-Content-Type: text/markdown

# Hubuum client library (Python)

[![CI](https://github.com/hubuum/hubuum-client-python/actions/workflows/ci.yml/badge.svg)](https://github.com/hubuum/hubuum-client-python/actions/workflows/ci.yml)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![Typed](https://img.shields.io/badge/typing-typed-blue.svg)](https://peps.python.org/pep-0561/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

`hubuum-client` is a modern, fully typed Python client for the
[Hubuum](https://github.com/hubuum/hubuum) asset-management API. It provides
matching synchronous and asynchronous clients, Pydantic v2 models, typed
resource IDs, immutable queries, cursor pagination, structured errors, and a
contract-checked interface for all 196 operations in the server's OpenAPI
surface.

Version 0.0.1 targets Hubuum server **v0.0.3**. Compatibility is tested against
the tag-and-digest server image recorded in the
[compatibility matrix](docs/compatibility.md).

## Installation

```bash
python -m pip install hubuum-client
```

Python 3.11 or newer is required. The runtime dependencies are only
[HTTPX](https://www.python-httpx.org/) and
[Pydantic](https://docs.pydantic.dev/).

## Quick start

```python
from hubuum_client import ClassCreate, Client, Credentials, Query

with Client("https://hubuum.example.com") as client:
    client.login(Credentials("alice", "correct-horse-battery-staple"))

    classes = client.classes.list(
        Query().where("name", "server").limit(25).include_total()
    )

    created = client.classes.create(
        ClassCreate(
            name="server",
            collection_id=1,
            description="Server inventory",
        )
    )
    print(created.id, len(classes))
```

The asynchronous API has the same shape:

```python
import asyncio

from hubuum_client import AsyncClient, Credentials, Query


async def main() -> None:
    async with AsyncClient("https://hubuum.example.com") as client:
        await client.login(Credentials("alice", "secret"))
        page = await client.classes.by_name("Servers").objects.page(
            Query().where("name", "web-01").include_total()
        )
        print(page.total_count, page.items)


asyncio.run(main())
```

Context-managed clients remain open for the entire block and reuse their HTTP
connection pools. Applications that manage startup and shutdown explicitly can
keep the same client for their full lifetime:

```python
client = Client("https://hubuum.example.com")
try:
    client.login(Credentials("alice", "secret"))
    run_application(client)
finally:
    client.close()
```

The async equivalent uses `await client.close()`. Reuse a client instead of
constructing one per request so eligible TCP/TLS connections can be reused; see
[Client setup](docs/client.md) for synchronous and asynchronous lifetime
examples.

Credentials and bearer tokens have redacted representations. TLS certificate
validation is enabled by default; disabling it is an explicit client option and
should be limited to disposable development systems.

## Resource services

The typed surface currently covers the most common Hubuum workflows:

- collections, hierarchy traversal, and parent moves;
- classes and class-scoped objects, including exact-name addressing and nested
  `data` filtering and atomic JSON Patch;
- users, groups, memberships, and user anonymization;
- class relations and object relations;
- cursor pagination and task polling;
- health, readiness, and public server configuration.

Every v0.0.3 OpenAPI operation is registered by its stable `operationId`:

```python
from hubuum_client import OpenAPIOptions

result = client.openapi.call(
    "getApiV1Search",
    options=OpenAPIOptions(params={"q": "server", "limit_per_kind": 10}),
)
```

The checked-in manifest covers all 196 methods, paths, path variables, body
media types, public/authenticated policies, JSON responses, rendered text
exports, and the search event stream. `request()` remains available for
server extensions outside the pinned specification. Both interfaces are
constrained to the configured origin.

## Querying and pagination

Queries are immutable and reusable:

```python
from hubuum_client import FilterOperator, Query

base = Query().where("name", "server", FilterOperator.ICONTAINS)
first_page = client.classes.page(base.limit(25).include_total())
all_matches = client.classes.all(base, max_items=5_000)

active_web_servers = client.classes.by_name("Servers").objects.all(
    Query()
    .data("status")
    .equals("active")
    .data("tags")
    .contains_all("web", "api")
)
```

`Page` exposes `items`, `next_cursor`, `total_count`, and the effective
`page_limit`. Automatic pagination detects repeated cursors and enforces page
and item limits. The fluent `data()` selector supports nested paths, typed
comparisons, arrays, nulls, object keys, and IP/network operators; see
[Queries and pagination](docs/querying.md).

Group membership uses the same typed, cursor-aware interface:

```python
member_page = client.groups.members_page(group_id, Query().limit(25).include_total())
all_members = client.groups.all_members(group_id, max_items=5_000)
```

## Documentation

- [Client setup](docs/client.md)
- [Queries and pagination](docs/querying.md)
- [API reference](docs/api.md)
- [Compatibility policy](docs/compatibility.md)
- [Docker-backed end-to-end tests](docs/e2e.md)
- [Changelog](CHANGELOG.md)

## Development

```bash
uv sync --extra dev
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run bandit -q -r src scripts
uv run zizmor .
uv run pytest --cov
uv run mkdocs build --strict
```

The full pinned-server workflow is:

```bash
./scripts/run-e2e-tests.sh
```

See [AGENTS.md](AGENTS.md) for repository conventions and the exact e2e stack.
Contributions are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md) for the
development and pull-request workflow. Maintainers can find the release process
in [the release guide](docs/releasing.md).

Please report security issues according to [SECURITY.md](SECURITY.md), rather
than in a public issue.

## License

Distributed under the MIT License. See [LICENSE](LICENSE).
