Run batches and import JSON or CSV through the API
Each operation is dispatched as a request of its own, so a batch is held to the same validation and permissions as the calls it contains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,8 @@ tags:
|
||||
description: Review, publication and preview links
|
||||
- name: Health
|
||||
description: What is wrong with the site, and what is left to translate
|
||||
- name: Batch
|
||||
description: Several operations in one, and importing records from JSON or CSV
|
||||
- name: Users
|
||||
description: Local user administration
|
||||
- name: Media
|
||||
@@ -3638,6 +3640,107 @@ paths:
|
||||
items: { $ref: "#/components/schemas/PendingTranslation" }
|
||||
"422": { $ref: "#/components/responses/ValidationError" }
|
||||
|
||||
/v1/batch:
|
||||
post:
|
||||
tags: [Batch]
|
||||
summary: Run several operations
|
||||
description: |
|
||||
Up to 50 admin API calls in one request. Each runs as a request of its
|
||||
own with the caller's credentials — validated, authorised and answered
|
||||
exactly as if sent separately — so a batch can do nothing its
|
||||
operations would be refused. Their answers come back in order.
|
||||
|
||||
With `atomic`, it stops at the first failure and undoes everything
|
||||
before it. What already left the database — a queued job, a
|
||||
notification — is not undone.
|
||||
|
||||
`path` is an admin path such as `/v1/pages/3`; batches do not nest and
|
||||
do not reach the import or the token exchange.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [operations]
|
||||
properties:
|
||||
atomic: { type: boolean, default: false }
|
||||
operations:
|
||||
type: array
|
||||
maxItems: 50
|
||||
items:
|
||||
type: object
|
||||
required: [method, path]
|
||||
properties:
|
||||
method: { type: string, enum: [GET, POST, PUT, PATCH, DELETE] }
|
||||
path: { type: string, example: /v1/technologies }
|
||||
body: { type: object, nullable: true, additionalProperties: true }
|
||||
responses:
|
||||
"200":
|
||||
description: Each operation's answer
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/BatchOutcome" }
|
||||
"403": { $ref: "#/components/responses/Forbidden" }
|
||||
"422": { $ref: "#/components/responses/ValidationError" }
|
||||
|
||||
/v1/import/{resource}:
|
||||
post:
|
||||
tags: [Batch]
|
||||
summary: Import records
|
||||
description: |
|
||||
Rows of one kind of record, as JSON (`rows`) or a CSV `file`, turned
|
||||
into the create and update calls the API already has — up to 50 per
|
||||
import, each validated and authorised like any other call.
|
||||
|
||||
A row with an `id`, or whose `match` field (`slug`, `name` or
|
||||
`full_name`) names an existing record, updates it; any other row
|
||||
creates one. In a CSV, a column `title.es` fills the Spanish title, a
|
||||
list is written `1|2|3`, `true`/`false`/`null` are what they say, and
|
||||
an empty cell is left out rather than erasing what is there.
|
||||
|
||||
All-or-nothing unless `atomic` is false. `dry_run` checks every row and
|
||||
keeps none.
|
||||
parameters:
|
||||
- name: resource
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [pages, categories, projects, members, services, clients, testimonials, talks, technologies, calls]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [rows]
|
||||
properties:
|
||||
rows:
|
||||
type: array
|
||||
maxItems: 50
|
||||
items: { type: object, additionalProperties: true, description: The same body the resource's create endpoint takes. }
|
||||
match: { type: string, enum: [slug, name, full_name], nullable: true }
|
||||
atomic: { type: boolean, default: true }
|
||||
dry_run: { type: boolean, default: false }
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
required: [file]
|
||||
properties:
|
||||
file: { type: string, format: binary, description: "CSV with a header row, UTF-8." }
|
||||
match: { type: string, enum: [slug, name, full_name] }
|
||||
atomic: { type: boolean, default: true }
|
||||
dry_run: { type: boolean, default: false }
|
||||
responses:
|
||||
"200":
|
||||
description: Each row's answer
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/BatchOutcome" }
|
||||
"403": { $ref: "#/components/responses/Forbidden" }
|
||||
"422": { $ref: "#/components/responses/ValidationError" }
|
||||
|
||||
/v1/media:
|
||||
get:
|
||||
tags: [Media]
|
||||
@@ -5418,6 +5521,27 @@ components:
|
||||
missing: { type: array, items: { type: string }, description: Languages with nothing written. }
|
||||
stale: { type: object, additionalProperties: { type: string, format: date-time }, description: Language → since when it has been behind. }
|
||||
|
||||
BatchOutcome:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
index: { type: integer }
|
||||
method: { type: string }
|
||||
path: { type: string }
|
||||
status: { type: integer, description: The HTTP status the operation answered with. }
|
||||
body: { nullable: true, description: What the operation answered. }
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
committed: { type: boolean, description: False when an atomic run or a dry run undid everything. }
|
||||
succeeded: { type: integer }
|
||||
failed: { type: integer }
|
||||
dry_run: { type: boolean, nullable: true }
|
||||
|
||||
MediaFor:
|
||||
type: string
|
||||
enum: [pages, technologies, projects, members, clients, testimonials]
|
||||
|
||||
Reference in New Issue
Block a user