Eternaltwin

Home | /api | v1

/api/v1/job

Background work. Defined in crates/rest/src/job.rs.

A job is a complete unit of work; a task is a sub-unit, and many tasks compose into one job. Every job has a root task, and every task belongs to a job.

Administrators only. The four routes all check is_administrator on the auth context before anything else. A guest, an OAuth token or an ordinary member is refused.

Mind the status codes: only POST /jobs reports a refusal as 403. The three reads collapse every service failure — including the permission one — into 500{"error": "internal error"}. That is a rough edge of the REST layer, not a statement about the permission, which is checked exactly the same way on all four.

GET /jobs

Lists jobs, most recent first.

No query parameter is read: the handler always asks for offset = 0, limit = 100, no status filter and no creator filter.

Example

GET /api/v1/job/jobs
Cookie: sid=b8be19ef-2d61-44de-b7d2-9c34ccb8a763
{
  "offset": 0,
  "limit": 100,
  "count": 1,
  "items": [
    {
      "id": "1b5f36a1-8e6c-4b59-bd62-2f6a0b8f4e11",
      "created_at": "2021-01-15T14:17:14.015Z",
      "created_by": {
        "type": "User",
        "id": "9f310484-963b-446b-af69-797feec6813f",
        "display_name": {"current": {"value": "Demurgos"}}
      },
      "task": {
        "id": "c0a0d0b8-6e0e-4a4e-9b8a-51d1e9b9f0c2",
        "revision": 3,
        "polled_at": "2021-01-15T14:17:20.000Z",
        "status": "Complete",
        "starvation": 0,
        "kind": "SendEmail",
        "kind_version": 1
      }
    }
  ]
}
FieldTypeMeaning
created_byshort user or nullWho started the job. null for one the server started itself.
taskobjectThe root task, without its state.
task.revisionintegerIncremented on every update of the task.
task.polled_attimestamp or nullLast time a worker picked it up.
task.statusstring"Available", "Blocked" or "Complete".
task.starvationintegerHow long the task has been waiting for a worker.
task.kindstringTask kind, ^[A-Z][A-Za-z0-9]{0,31}$.
task.kind_versionintegerVersion of the state layout for that kind.

Errors

StatusBodyWhen
500{"error": "internal error"}The caller is not an administrator, or the store failed.

POST /jobs

Starts a job.

Body

FieldTypeMeaning
kindstringTask kind of the root task.
stateany JSONInitial state handed to that task.

kind_version is not part of the body; the handler always sends 1.

POST /api/v1/job/jobs
Content-Type: application/json
Cookie: sid=b8be19ef-2d61-44de-b7d2-9c34ccb8a763

{"kind": "SendEmail", "state": {}}

No task kind is registered yet. After the administrator check, the service rejects every kind as unknown, and the REST layer reports that as 500. The route is in place for the job runtime, but there is nothing it can start today.

Errors

StatusBodyWhen
403{"error": "current actor does not have the permission to create a job"}Not an administrator.
500{"error": "internal error"}Unknown task kind, which is currently every kind.

GET /jobs/:job_id

Returns one job, in the same shape as a row of the listing above. :job_id is a UUID.

GET /api/v1/job/jobs/1b5f36a1-8e6c-4b59-bd62-2f6a0b8f4e11
Cookie: sid=b8be19ef-2d61-44de-b7d2-9c34ccb8a763

Errors

StatusBodyWhen
500{"error": "internal error"}Not an administrator, no such job, or the store failed.

GET /tasks/:task_id

Returns one task with its state, which is what the listing omits.

GET /api/v1/job/tasks/c0a0d0b8-6e0e-4a4e-9b8a-51d1e9b9f0c2
Cookie: sid=b8be19ef-2d61-44de-b7d2-9c34ccb8a763
{
  "id": "c0a0d0b8-6e0e-4a4e-9b8a-51d1e9b9f0c2",
  "revision": 3,
  "polled_at": "2021-01-15T14:17:20.000Z",
  "status": "Complete",
  "starvation": 0,
  "kind": "SendEmail",
  "kind_version": 1,
  "state": {},
  "output": null
}

state and output are opaque JSON: their shape belongs to the task kind, not to this API. output is null until the task completes.

Errors

StatusBodyWhen
500{"error": "internal error"}Not an administrator, no such task, or the store failed.