rozsireni o keywords a uprava swaggeru

This commit is contained in:
JiriUhlir
2026-07-20 09:02:42 +02:00
parent 0ed3e11a4d
commit 71eeb51847
7 changed files with 608 additions and 62 deletions
+42 -5
View File
@@ -49,6 +49,7 @@ access denied, bad arguments) are surfaced as `upstream_error` with the Sklik
| GET | `/sklik/campaigns` | `campaigns.list`, all pages collected. |
| GET | `/sklik/groups` | `groups.list`, filterable by `campaign_ids`. |
| GET | `/sklik/ads` | `ads.list`, filterable by `campaign_ids` / `group_ids`. |
| GET | `/sklik/keywords` | `keywords.list`, filterable by `campaign_ids` / `group_ids`. |
| POST | `/sklik/campaigns` `/sklik/groups` `/sklik/ads` | **Create — always paused.** |
| PUT | `/sklik/campaigns` `/sklik/groups` `/sklik/ads` | **Update** by id (partial). |
| DELETE | `/sklik/campaigns` `/sklik/groups` `/sklik/ads` | **Remove** (`?ids=1,2,3`) — reversible. |
@@ -94,18 +95,48 @@ Query parameters:
| Remove | `DELETE /sklik/{entity}?ids=1,2` | `{entity}.remove` | ids in the query |
| Restore | `POST /sklik/{entity}/restore?ids=1,2` | `{entity}.restore` | ids in the query |
`{entity}``campaigns`, `groups`, `ads`. The structs are exactly the ones Sklik
`{entity}``campaigns`, `groups`, `ads`, `keywords`. The structs are exactly the ones Sklik
documents ([campaigns.create](https://api.sklik.cz/drak/campaigns.create.html),
[campaigns.update](https://api.sklik.cz/drak/campaigns.update.html), and the
`groups.*` / `ads.*` equivalents). Sklik batches are **all-or-nothing**: if one
item fails, nothing is applied.
### Create — everything is paused, not configurable
### The bodies are typed in Swagger
Create and update bodies are modelled (`app/sklik_models.py`), so `/docs` shows
every field with its type, whether it is required, the allowed enum values
(campaign `type`, `status`, `adType`, `adSelection`, `paymentMethod`,
`premiseMode`, `videoFormat`) and a description with the unit. "Try it out" is
therefore fillable without reading the Sklik docs first.
Two properties of the models matter:
- **They do not restrict you.** Every model accepts undeclared fields and
forwards them to Sklik untouched, so the less common parts of the API
(retargeting, product sets, anything Sklik adds later) keep working without a
code change here. The schema documents the common path; it is not a whitelist.
- **Update is genuinely partial.** Only the fields you actually send are
forwarded, so omitting a field leaves it alone rather than resetting it. An
explicit `null` is still sent (that is how you clear `totalBudget`).
Schema violations (missing required field, bad enum value) come back as **422**
with the offending field in `detail[].loc` — before any Sklik call. The business
guard rails (budget ceilings, blocked activation) return **400** / **403**.
### Create — campaigns, groups and ads are paused, not configurable
Sklik's `status` field **defaults to `active`**, so an omitted status would
create a *live, spending* campaign. The proxy therefore forces
`status: "suspend"` on every created entity and ignores any other value you
send (the override is logged).
`status: "suspend"` on every created campaign, group and ad, and ignores any
other value you send (the override is logged).
> **Keywords are the exception** and are *not* forced. A keyword cannot spend
> anything on its own — the campaign, group and ad above it are all created
> paused, and those gate the spending. Forcing keywords paused as well would
> only create a trap: you activate the campaign in the Sklik UI, nothing
> happens, and the cause is a fourth paused level nobody expected. Sklik's own
> default (`active`) applies; send `status` explicitly if you want otherwise.
> `GET /sklik/write-limits` lists which entities are force-paused.
### Update — ordinary CRUD, status is not forced
@@ -125,7 +156,13 @@ way.
> one, so the ad gets a **new id**. Re-read the group's ads after such an
> update. Changing only `status` keeps the id.
`type` cannot be changed on a campaign.
Immutable fields, so `PUT` does not offer them at all:
| Entity | Cannot be changed |
| --- | --- |
| campaign | `type` |
| keyword | `name`, `matchType` — remove it and create a new one |
| ad | the creative (see the note above) |
### Remove and restore — reversible