Skip to content

feat(sites,jobs): batch job-status lookup + Job.wait_all() helper #14

Description

@mrrobot47

Summary

The SDK today only wraps GET /job-status/{id} (single job). The WP Cloud API also exposes a POST form that accepts multiple job IDs in a single call, returning their statuses as a batch. Any SDK consumer polling N jobs today does N HTTP round-trips per poll; batch support is a substantial performance win for anything that fans out work (site creation + DNS + SSL verification, fleet tasks, migration workflows).

Also introduce a Job.wait_all(jobs, ...) class-level helper built on the batch endpoint so callers don't have to stitch the pattern together each time.

Endpoint

Source: https://wp.cloud/docs/api/openapi.json → tag Jobs.

# Verb Path Body Response
1 POST /job-status/{id} form: additional job IDs as ids[] (spec-exact shape to verify during implementation) list of job status objects

Note: the {id} path param may be the first job id (per-endpoint convention); verify the exact batch-submit shape against a live API call during implementation. The spec description is light — "Get full status details of one or more jobs by ID."

Scope

1. New method sites.get_job_statuses(job_ids: list[str]) -> list[dict]

  • Location: atomic_sdk/api/sites.py (alongside existing get_job_status and get_job_completion).
  • Accepts a list of job IDs, returns a list of dicts — one per job — each with the same shape get_job_status returns today.
  • Client-side validation: ValueError if the list is empty. Cap batch size at 50 (client-side — API may have a higher limit; 50 is a reasonable safeguard to avoid surprise 4xx from mega-batches).

2. Class helper Job.wait_all(jobs, *, timeout=300, poll_interval=5) -> dict[str, str]

  • Location: atomic_sdk/models.py on the Job model.
  • Input: a list of Job instances (already carry _client).
  • Output: dict of {job_id: terminal_status} — one entry per input job.
  • Behaviour: polls via the batch endpoint every poll_interval seconds until every job is terminal (success / failure) or overall timeout elapses. Raises TimeoutError if the deadline passes with any job still queued.
  • On any single job's batch response shape being unexpected, reuse fix(api): raise AtomicAPIError on non-dict job responses instead of Pydantic ValidationError #11's AtomicAPIError-with-payload pattern rather than leaking KeyError/ValidationError.

3. Deprecation notice

  • Mark get_job_completion as deprecated in its docstring (it's already described as deprecated in the public docs). Emit a DeprecationWarning on call.
  • Do not remove — still a valid single-job path.

Client-side validation

  • get_job_statuses([])ValueError("at least one job_id required").
  • get_job_statuses(ids) with len(ids) > 50ValueError with clear message.

Acceptance criteria

  • client.sites.get_job_statuses([job1.job_id, job2.job_id]) returns a list of two status dicts in a single HTTP call (verify via a mock or live staging).
  • Job.wait_all([job1, job2, job3]) resolves once all three reach terminal status and returns a {job_id: status} dict.
  • Job.wait_all with timeout=1 on a job known to run longer raises TimeoutError.
  • get_job_completion emits DeprecationWarning on call; still works.
  • Empty-list / over-cap inputs raise ValueError client-side.

Out of scope

  • Typed response model for job status — defer to the repo-wide model pass.
  • Removing get_job_completion entirely — it's still a valid endpoint for callers who don't want the batch flow.
  • Batch job_completion similarly — skip; it's the deprecated one.

References

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions