Querying and pagination¶
Hubuum filters use field__operator=value. The Python client represents them as
immutable Query values:
from hubuum_client import FilterOperator, Query
query = (
Query()
.where("name", "server", FilterOperator.ICONTAINS)
.where("validate_schema", True)
.sort("name.asc")
.limit(25)
.include_total()
)
page = client.classes.page(query)
Dates and datetimes are encoded as ISO 8601 values. Booleans are encoded as lowercase JSON-style strings.
Operators¶
FilterOperator includes equality, case-insensitive equality, containment,
prefix, suffix, SQL-like, regular-expression, comparison, range, membership,
null, JSON structure, and IP/network operators, along with each operator's
negated form. The server validates whether an operator is legal for a
particular field.
Object data¶
Use data() to select a key path inside an object's JSON data document. A
terminal filter returns a new immutable Query, so filters compose naturally:
query = (
Query()
.data("status")
.equals("active")
.data("metrics", "cpu_count")
.gte(4)
.data("tags")
.contains_all("web", "api")
)
objects = client.classes.by_id(class_id).objects.all(query)
The path is passed as one key per argument. For example,
data("network", "address") selects data["network"]["address"] and encodes
the server value network,address=.... Commas and equals signs cannot be used
in path keys because Hubuum v0.0.16 does not define escaping for those
delimiters.
Common scalar and textual filters use direct method names:
from datetime import date
Query().data("hostname").icontains("web")
Query().data("enabled").equals(True)
Query().data("maintenance", "starts_at").between(
date(2026, 7, 1),
date(2026, 7, 31),
)
Available scalar methods are equals, iequals, contains, icontains,
starts_with, istarts_with, ends_with, iends_with, like, regex,
gt, gte, lt, lte, and between. Booleans use lowercase wire values,
and dates and datetimes use ISO 8601. Set negate=True on any terminal to use
the corresponding server not_ operator.
JSON arrays, objects, and nulls have semantic helpers:
Query().data("status").one_of("active", "standby")
Query().data("tags").contains_all("web", "api")
Query().data("tags").array_length(2)
Query().data("config").has_key("hostname")
Query().data("retired_at").is_null()
Query().data("retired_at").is_null(negate=True)
one_of matches either a scalar in the supplied set or an array containing at
least one supplied value. contains_all requires every supplied array value.
is_null matches both a missing path and JSON null, following server semantics.
Network-aware filters accept strings or values from Python's ipaddress
module:
from ipaddress import ip_network
Query().data("network", "address").within_network(ip_network("10.0.0.0/24"))
Query().data("network", "address").contains_network("10.0.0.0/25")
Query().data("network", "address").contains_ip("10.0.0.10")
Query().data("network", "address").overlaps_network("10.0.0.64/26")
Query().data("network", "address").inet_equals("10.0.0.10/32")
These helpers generate normal json_data__operator parameters and work
unchanged with both synchronous and asynchronous object services.
Result shapes¶
Resource services expose four common terminals:
list(query)returns the current page's items as a list;page(query)returnsPage[T]with items and cursor metadata;all(query)follows cursors and collects bounded results;one(query)requires exactly one item and raisesResultCardinalityErrorotherwise.
page = client.classes.by_id(class_id).objects.page(Query().limit(50).include_total())
print(page.total_count)
print(page.page_limit)
if page.has_next:
next_page = client.classes.by_id(class_id).objects.page(Query().cursor(page.next_cursor))
total_count is None when include_total=False or the server omits the
header. page_limit is the effective server-applied limit.
Automatic pagination safeguards¶
The client rejects repeated cursors, more than max_pages, and more than
max_items. Bounds must be positive and are validated before the client makes a
request. The async client provides the same API, and its pages() method is an
async iterator.
The default bounds are intentionally conservative. Increase them explicitly for a known large result set, or consume pages individually when streaming work is more appropriate.
Group members are returned as typed PrincipalMember values. Membership
pagination follows the same bounds and cursor rules through members_page(),
member_pages(), and all_members():
page = client.groups.members_page(group_id, Query().limit(50).include_total())
members = client.groups.all_members(group_id, max_pages=20, max_items=5_000)
Each membership has its own revision, group_id, and principal_id. List
responses also include the optional nested principal details, whose revision
is independent of the enclosing membership revision.
Exact-name routes¶
Hubuum v0.0.16 supports explicit natural-key aliases for classes and objects. Use the complete name-addressed service when class and object names are already known:
selected_class = client.classes.by_name("12345")
hubuum_class = selected_class.get()
hubuum_object = selected_class.objects.get("67890")
all_objects = selected_class.objects.all()
id_selected_class = client.classes.by_id(class_id)
same_class = id_selected_class.get()
id_scoped_objects = id_selected_class.objects.all()
Both names are encoded as opaque path segments after explicit by-name
markers, so spaces, slashes, and numeric-looking values remain unambiguous.
The nested selectors make the resource hierarchy and ID-versus-name choice
explicit.
Task discovery¶
Use TaskQuery for client.tasks.page(), list(), pages(), and all().
Task filters use plain parameter names; generic resource Query.where()
produces operator-suffixed parameters that the task endpoint does not accept.
from datetime import UTC, datetime
from hubuum_client import TaskKind, TaskQuery, TaskStatus
query = (
TaskQuery(
kind=(TaskKind.EXPORT, TaskKind.BACKUP),
status=(TaskStatus.SUCCEEDED, TaskStatus.FAILED),
terminal=True,
created_after=datetime(2026, 9, 22, tzinfo=UTC),
)
.limit(25)
.sort("finished_at.desc,id.desc")
.include_total()
)
tasks = client.tasks.all(query)
Kinds and statuses use comma-separated OR lists; different filters combine
with AND. Immutable pagination builders preserve all filters across cursors.
The server validates combinations: relation identity requires both
relation_type and relation_id, and schema/computation revisions require
class_id. Supply timezone-qualified timestamps; lower bounds are inclusive
and upper bounds exclusive. Explicit statuses must agree with terminal.
Resource filters include class_id, object_id, collection_id, relation
identity, remote_target_id, and export_template_id. Kind-specific filters
cover schema work, computation revision, remote side-effect state, export
warnings/truncation, import options/failures, backup history, and output_state.
Lifecycle controls also include cancel_requested, terminal_reason,
submitted_by (administrator-only), and trace_id. See TaskQuery in the
API reference for every typed field.
Task.details supports all six kinds: import_, export, backup,
schema_validation, reindex, and remote_call. Import/export/backup details
include typed retained options. Schema details include the class, revision,
work status, and retained results URL; rebuild details include the computation
revision, and remote calls include their explicit target.
Historical unknown values remain None, distinct from False. Output state
is available, expired, not_produced, or unknown. Discovery checks current
resource authorization before matching/counting and may suppress details and
output links while retaining basic task status. Resource filters match explicit
captured targets, not present-day membership or resources inferred from an
import payload or export query. See the server task reference.