Skip to main content

Async Python client for the BoondManager API

Project description

boondmanager

Async Python client for the BoondManager API.

Why it's built this way

BoondManager validates write payloads against strict JSON schemas (additionalProperties: false, all keys required) and silently drops any non-conforming item while returning HTTP 200. A malformed timesheet entry doesn't fail; it just never persists.

To make that class of bug impossible to reintroduce, the client is driven by BoondManager's own published spec:

  • boondmanager/_spec/spec.json.gz — vendored bundle: the full endpoint registry (598 endpoints, 844 methods) plus every published request-body JSON schema, extracted from their public RAML build.
  • Every request body is validated against the matching schema before sending; mismatches raise BoondManagerValidationError with the exact violations instead of silently losing data.
  • update_times_report additionally raises BoondManagerSilentDropError when the server returns 200 but persisted fewer entries than were sent (schema-valid but semantically rejected, e.g. a nonexistent row id).

Three API layers

from boondmanager import BoondManagerClient

async with BoondManagerClient(client_token=..., client_key=..., user_token=...) as client:
    # 1. High-level domain objects — no BoondManager quirks to know about
    ts = await client.fetch_timesheet("1652")
    ts.row(project="23").set("2026-06-30", 0.5)   # delivery auto-picked, rows managed
    await ts.save()                                # validated, silent-drop guarded

    # 2. Curated helpers returning pydantic models
    report = await client.get_times_report("1652")
    reports = await client.get_resource_times_reports(resource_id)

    # 3. Full generated surface — one namespace per resource, every endpoint
    doc = await client.api.times_reports.search(params={"period": "2026-06"})
    doc = await client.api.projects.get("23")
    await client.api.times_reports.validate("1652")

client.api (boondmanager/api.py, generated) covers the entire public API as namespaces: client.api.times_reports.update(id, body), client.api.resources.absences_reports(id), ... Its methods return Document objects — dict-compatible JSON:API wrappers with navigation:

doc = await client.api.times_reports.get("1652")
report = doc.one                       # Entity: report["term"], report.rel("projects")
for project in doc.included("project"):
    deliveries = doc.related(project, "deliveries")

api.ENDPOINT_INDEX maps ("PUT", "/times-reports/{id}") to ("times_reports", "update") for programmatic discovery, and client.request(method, path, json=..., params=..., validate=True) is the underlying escape hatch.

To find a method, grep docs/API.md — one line per endpoint method with its required query params. Each generated method's docstring documents all query parameters (name, type, required, enum, default) and the request-body schema name.

Timesheet gotchas (learned the hard way)

Timesheet (layer 1) absorbs all of these; they only matter when using the lower layers directly.

  • regularTimes entries: row <= -1 creates a new row (entries sharing the same negative value land on the same row); row >= 1 must reference an existing row id. Unknown positive ids are silently discarded.
  • Missing relationships are {"data": null}, never a bare null or an omitted key.
  • exceptionalTimes have no duration/row, and project/delivery are mandatory there.
  • BoondManager mirrors exceptionalTimes entries into regularTimes for activity totals. Always filter by activity_type in {"exceptionalTime", "exceptionalCalendar"} before iterating regularTimesrows() and editable_rows() on TimesReport already do this.
  • Keys that appear in GET responses (calendar, entry ids for new items) are not accepted in PUT bodies.

Regenerating the spec and API surface

When BoondManager publishes API changes:

uv run scripts/fetch_spec.py     # re-download RAML + schemas -> _spec/spec.json.gz
uv run scripts/generate_api.py   # regenerate boondmanager/api.py + docs/API.md
uv run pytest                    # sanity: registry, validation, api surface

A handful of schemas are referenced by the RAML but not published (listed in scripts/fetch_spec.py); those endpoints work but skip pre-send validation.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

boondmanager_client-0.0.1.tar.gz (246.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

boondmanager_client-0.0.1-py3-none-any.whl (211.4 kB view details)

Uploaded Python 3

File details

Details for the file boondmanager_client-0.0.1.tar.gz.

File metadata

  • Download URL: boondmanager_client-0.0.1.tar.gz
  • Upload date:
  • Size: 246.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for boondmanager_client-0.0.1.tar.gz
Algorithm Hash digest
SHA256 b4faf274ea3ea99f5b98c6aedd11729fe73c23b43f1f24154404dbe038b54326
MD5 84e5bb787eb5da23a621199db38ead91
BLAKE2b-256 ae369e3aad31d13d51c276bdad2339942a00b941c26783843cfeada4770d26f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for boondmanager_client-0.0.1.tar.gz:

Publisher: publish.yml on Lenstra/boondmanager-client-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file boondmanager_client-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for boondmanager_client-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 53372d427cb0906aa69278715b825669788dc404b74779455956766f568957a9
MD5 873019ea7a7d4b5d49c2584980169824
BLAKE2b-256 30ea3c8a9ad65c9a3e6faeab073e6d07b436a9be4b6eef94afb2451a45acfff6

See more details on using hashes here.

Provenance

The following attestation bundles were made for boondmanager_client-0.0.1-py3-none-any.whl:

Publisher: publish.yml on Lenstra/boondmanager-client-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page