feat: updated integration page with missing API routes

This commit is contained in:
Florian Sylvain committed 2026-10-26 02:18:35 +01:00
1 parent 25d9c30b8a
commit 7c0ee21906
25 files changed
+2402 -350

No files matched your search

+10 -9
View File
@@ -11,13 +11,13 @@ Feature: Integration screen (API reference)
And the PageHeader shows breadcrumb "Integration", title "API reference" and description
"Everything a developer needs to build on RenewCMS."
And the endpoint catalogue is a typed static data file at web/src/data/apiCatalogue.ts, and the view only renders it
And the catalogue holds 20 key-callable endpoints in 4 expandable groups, in this order:
And the catalogue holds 42 key-callable endpoints in 9 expandable groups (the Content types, Entries, Built-in types and Sites and authors groups and the extra Media routes are in features/integration_api_key_routes.feature), in this order:
| group | routes | endpoints |
| Articles | 7 | GET /v1/article/, GET /v1/article/{id}, POST /v1/article/, PATCH /v1/article/{id}, PATCH /v1/article/{id}/publish, PATCH /v1/article/{id}/unpublish, DELETE /v1/article/{id} |
| Media | 6 | GET /v1/media/, POST /v1/media/, PATCH /v1/media/{id}, DELETE /v1/media/{id}, POST /v1/media/merge, GET /v1/media/{id}/usage |
| Articles | 8 | GET /v1/article/, GET /v1/article/{id}, POST /v1/article/, PATCH /v1/article/{id}, PATCH /v1/article/{id}/publish, PATCH /v1/article/{id}/unpublish, DELETE /v1/article/{id}, POST /v1/article/{id}/seo/check |
| Media | 11 | GET /v1/media/, GET /v1/media/{id}, POST /v1/media/, PATCH /v1/media/{id}, DELETE /v1/media/{id}, POST /v1/media/merge, GET /v1/media/{id}/usage, GET and PATCH /v1/media/purge-settings, GET /v1/media/purge-preview, POST /v1/media/purge |
| Users and invites | 6 | GET /v1/user/, PATCH /v1/user/me, POST /v1/user/invite, DELETE /v1/user/invite/{id}, POST /v1/user/invite/{id}/renew, GET /v1/auth/me |
| Settings | 1 | GET /v1/settings |
And the "Session only" group has 13 non-expandable rows in 3 sub-groups:
And the "Session only" group has 37 non-expandable rows in 6 sub-groups (the full list is in features/integration_api_key_routes.feature); the first three are:
| sub-group | rows |
| System · owner only | GET /v1/system/update, POST /v1/system/update/check, POST /v1/system/update/download, POST /v1/system/update/restart |
| Sign in and account | POST /v1/auth/login, POST /v1/auth/logout, POST /v1/auth/register, POST /v1/auth/verify, DELETE /v1/auth/register, POST /v1/auth/password-reset |
@@ -36,7 +36,7 @@ Feature: Integration screen (API reference)
Scenario: Default, every card collapsed
When the user navigates to /integration
Then the intro reads "RenewCMS is headless: it stores your content and your own code displays it. There are two ways in. Public read-only access lets a website read online articles. API keys let a script or service manage articles, media and users without the back-office."
Then the intro reads "RenewCMS is headless: it stores your content and your own code displays it. There are two ways in. Public read-only access lets a website read online articles. API keys let a script or service manage articles, media and users, and manage content types and entries, without the back-office."
And a "Base URL" label sits above a CodeSnippet with the single tab "Base URL" showing window.location.origin and a "Copy" button (icon copy)
And every EndpointCard is collapsed as a 44 px row: method badge, mono path, one-line description, chips, chevron
And the sticky section index lists the sections on desktop and a "Jump to" select lists them on mobile
@@ -117,7 +117,7 @@ Feature: Integration screen (API reference)
And its CodeSnippet has tabs curl and fetch for GET /v1/auth/me, using the current origin
And it says "Keys start with `rcms_` and are shown in full once, when you create them."
And a "Manage API keys" link navigates to /integration/api-keys
And Note 1 reads "A key can't create, list or revoke API keys, verify or cancel a registration, or call anything under `/v1/system`. Those routes answer 403 "This action needs a real session." An invalid, unknown, expired or revoked key answers 401, even on public routes, and so does a key whose user was deleted or unverified."
And Note 1 reads "A key can't create, list or revoke API keys, verify or cancel a registration, or call anything under `/v1/system`. Those routes answer 403 "This action needs a real session." An invalid, unknown, expired or revoked key answers 401, even on public routes, and so does a key whose user was deleted or unverified. Owner and admin routes answer 403 to a key whose creator is neither."
And Note 2 reads "Each key can make 120 requests per minute. Beyond that the API answers 429 with a `Retry-After` header."
Scenario: Session only group
@@ -134,9 +134,10 @@ Feature: Integration screen (API reference)
| status | message |
| 400 | The request cannot be processed due to a mismatch in the format of the body. / The expiry must be a date in the future. |
| 401 | You must be logged in to access this resource. |
| 403 | This action needs a real session. / Only the owner can do this. / You must verify your account to access this resource. |
| 404 | Article not found / Image not found / Invite not found / API key not found |
| 409 | There's already a pending invite for this address. / This invite has already been accepted. |
| 403 | This action needs a real session. / Only owners and admins can do this. / Only the owner can do this. / You must verify your account to access this resource. |
| 404 | Article not found / Image not found / Invite not found / API key not found / content_type_not_found / entry_not_found / builtin_type_not_found |
| 409 | There's already a pending invite for this address. / This invite has already been accepted. / Some of the images to merge changed. Refresh and try again. / This address already has an account. / content_type_key_taken / content_type_has_entries / content_type_referenced / schema_incompatible / invalid_entry: <reason> / slug_taken |
| 422 | invalid_entry: <reason> |
| 429 | Too many requests. Please try again later. |
| 500 | Internal server error |
And a CodeSnippet with the single tab "Error response · text/plain" shows "HTTP/1.1 403 Forbidden", "Content-Type: text/plain", a blank line, and "This action needs a real session."
+1 -1
View File
@@ -54,7 +54,7 @@ Component specs, one per design state:
6. **Session only**: the corrected banner; lock icon and "Session only" chip only on rows that
answer 403; the caption on the login family; no expand control; the "Public auth flow" rows with
the "Public" chip.
7. **Errors**: table rows 400, 401, 403, 404, 409, 429, 500 with the real strings and the 403
7. **Errors**: table rows 400, 401, 403, 404, 409, 422, 429, 500 with the real strings and the 403
sample block.
8. **Navigation**: clicking an index item sets aria-current and scrolls to its anchor; the mobile
"Jump to" lists the same sections.
+169
View File
@@ -0,0 +1,169 @@
Feature: Integration page documents every route an API key can call
As a developer building on RenewCMS
I want the API reference to cover content types, entries, built-in types, sites, authors and media purge
So that I can use every route my API key can call without reading the source
Background:
Given the SPA route /integration of the verified back-office (static page, no /v1 call, web/ only)
And the page follows docs/design/screens/integration.md, sections "Custom content types round" and "Final round"
And the boards are IntegrationPage.dc.html, IntegrationSearch.dc.html and states 8c, 8d, 8f and 9a to 9j of "RenewCMS Screens Back-office.dc.html"
And endpoint rows, groups, index and session-only sub-groups come from the catalogue the board builds with A(...), G, IDX and sessGroups
And every sample and count follows the real API, not the board's illustrative values
And the components are Nuxt UI v4 with design tokens only
And every state is drawn in light and dark at 1440x900 and 390x844
And no state is date dependent
Rule: Section index and Jump to
Scenario: Index order
Given the viewport is 1440x900
Then the sticky index lists, in this order:
| Authentication | Articles | Search and sharing | Content types | Entries | Built-in types | Media |
| Sites and authors | Images and variants | Users and invites | Settings | Session only | Errors | Notes |
And on 390x844 the "Jump to" list shows the same items in the same order
Scenario: Scroll-spy on Content types (state 9j)
Given the Content types group is the section nearest the top of the page
Then its index item has aria-current "true" and the 2 px --ui-primary bar
And on mobile the "Jump to" select reads "Content types" and its open list marks that item
Rule: Endpoint groups
Scenario Outline: Group contents
Then the group "<group>" shows "<count>" with these routes and access chips
Examples:
| group | count | routes |
| Articles | 8 routes | unchanged (the 7 article routes and POST /v1/article/{id}/seo/check) |
| Search and sharing | 2 routes | unchanged |
| Content types | 5 routes | GET /v1/content-type/ and GET /v1/content-type/{key} Verified user; POST /v1/content-type/, PUT and DELETE /v1/content-type/{key} Owner or admin |
| Entries | 5 routes | GET and POST /v1/content-type/{key}/entry; GET, PUT and DELETE /v1/content-type/{key}/entry/{id}, all Verified user |
| Built-in types | 2 routes | GET /v1/builtin-type/ and GET /v1/builtin-type/{key}, Owner or admin |
| Media | 11 routes | the 7 existing (including GET /v1/media/{id}); GET and PATCH /v1/media/purge-settings, GET /v1/media/purge-preview, POST /v1/media/purge, Owner or admin |
| Sites and authors | 2 routes | GET /v1/site/ and GET /v1/author/, Verified user (the former Sites and Authors groups, merged) |
| Users and invites | 6 routes | unchanged |
| Settings | 1 route | unchanged |
And every route of these groups carries the chip "API key OK"
And the new groups appear in the order of the index
Scenario: Group descriptions
Then "Content types" reads "Define your own kind of content, like events or recipes. An owner or admin creates a type made of 1 to 50 ordered fields; the type then has its own entry routes."
And "Entries" reads "Read and write the content of a custom type. Any verified user can write entries."
And "Built-in types" says they are read-only through the API and that the keys are article, author, site, media and user
Scenario: Field types reference above the Content types routes
Then an anchor "ct-fields" sits above the first Content types route and is not an index item
And a "Field types" table with columns Type, Value in an entry and Rules lists text, richtext, number, boolean, date, select, media and relation
And a sample field object shows key capacity, label Capacity, type number, required true, unique false, default null, min 1, max 5000, options null, relation null
Scenario: PUT badge
Then the PUT method badge uses the PATCH colours
Scenario Outline: Expanded cards show their request fields and errors (states 9a and 9c)
When I expand "<route>"
Then the request table or query table shows "<fields>"
And the Errors list shows "<errors>"
Examples:
| route | fields | errors |
| POST /v1/content-type/ | key, name, icon, fields | 400, 401, 403 "Only owners and admins can do this.", 409 content_type_key_taken |
| PUT /v1/content-type/{key} | name, icon, fields | 400, 401, 403, 404 content_type_not_found, 409 schema_incompatible |
| DELETE /v1/content-type/{key} | No request body. | 401, 403, 404 content_type_not_found, 409 content_type_has_entries, 409 content_type_referenced |
| GET /v1/content-type/{key}/entry | page, size (5, 25, 50 or 100), sort (id, created_at or updated_at, then :asc or :desc) | 400 "Invalid page, size or sort.", 401, 404 content_type_not_found |
| POST /v1/content-type/{key}/entry | data | 400, 401, 404 content_type_not_found, 409 invalid_entry: title must be unique, 422 invalid_entry: capacity must be at most 5000 |
| GET /v1/content-type/{key}/entry/{id} | No request body. | 400 "The server expects the ID to be in the format of an unsigned 32-bit integer (uint32).", 401, 404 entry_not_found, 404 content_type_not_found |
| PUT /v1/content-type/{key}/entry/{id} | data | 400, 401, 404 entry_not_found, 404 content_type_not_found, 409 and 422 invalid_entry: <reason> |
| GET /v1/builtin-type/{key} | No request body. | 401, 403, 404 builtin_type_not_found |
And code-style captions show the literal body
Scenario: Response samples follow the real API
Then POST cards show only the JSON body, with no status line
And the entries list Response is an object with items, total, page and size, not a bare array
And an entry sample has id, content_type, data, created_by, updated_by, created_at, updated_at, media and relations
And the entry media sample is keyed by the field key, for example cover
And a built-in type sample has only key, name and fields, with no icon
And 409 on an entry only happens when a unique field clashes; any other field error is 422
Scenario: GET /v1/media/ is paginated
When I expand "GET /v1/media/"
Then a "Query parameters" table lists page and size (5, 25, 50 or 100, default 25) next to q, hash, filter, sort and variants
And the Response is an object with items, total, page, size and counts
And the Errors list has a 400 row "The media query is invalid."
Scenario: Media purge routes
Then GET and PATCH /v1/media/purge-settings use the keys auto_purge_enabled, auto_purge_interval_days (1 to 365), last_purge_at and last_purge_merged
And POST /v1/media/purge takes an optional fingerprint taken from purge-preview
And its Response sample has exactly groups_total, groups_purged, merged, articles_updated, sites_updated, authors_updated and failed_groups
And its description says failed_groups counts groups that could not be purged and the others were still purged
And no sample uses articlesUpdated or count for the purge response
Scenario: Sites and authors lists
Then GET /v1/site/ lists sites
And GET /v1/author/ has Query parameters page and size and returns an object with items, total, page and size
Rule: Session only
Scenario: Thirty-seven routes in six sub-groups (state 9e)
Then the heading reads "Session only" and the count reads "37 routes, not available to API keys", derived from the data
And the sub-groups are, in order: "System · owner only", "Sign in and account", "API key management", "Sites and authors writes", "Image variants", "Activity"
And every /v1/system route is in "System · owner only", with PATCH /v1/settings
And "Sign in and account" holds DELETE /v1/auth/register
And each row is dashed, muted and cannot expand
And each row that answers 403 to a key has the lock icon and the "Session only" chip; Log in, Log out, Register and Reset password keep their caption "Made for the browser. A key is ignored here, with no 403." and no chip
And the banner is unchanged
Rule: Errors table (state 9d)
Scenario: Rows and copy
Then the table has rows 400, 401, 403, 404, 409, 422, 429 and 500
And the 403 row lists "This action needs a real session.", "Only owners and admins can do this.", "Only the owner can do this." and "You must verify your account to access this resource."
And the 403 row keeps its muted note about site SEO defaults
And the 404 row adds content_type_not_found, entry_not_found and builtin_type_not_found
And the 409 row lists content_type_key_taken, content_type_has_entries, content_type_referenced, schema_incompatible, a unique-value clash and the existing media and user examples
And the 422 row lists "invalid_entry: <reason>"
And a note says code-style text is the literal body
And the old sentence saying there is no 409 or 422 row is gone
Rule: Authentication copy
Scenario: Key rights
Then the API key card says requests act as the user who created the key, with that user's role
And the page says a key can manage articles, media and users, and manage content types and entries, without the back-office
And Note 1 gains the sentence "Owner and admin routes answer 403 to a key whose creator is neither."
And the other authentication notes are unchanged
Rule: Search covers the new routes
Scenario: Index content
Then the search index holds 85 endpoints: 42 documented, 37 session-only and 6 public auth
And it includes the new endpoints, their body fields and query parameters, the events sample keys under data (title, starts_on, capacity, online, category, cover) as "Body field", and the new error codes and messages
Scenario Outline: Queries and counts
When I type "<query>" in the palette
Then the count and live region read "<announced>"
And the notes are "<notes>"
Examples:
| query | announced | notes |
| media | 17 results, showing 7 | Section Media reads "11 routes" |
| 404 | 24 results, showing 7 | rows show an "Error" second line |
| login | 2 results | Session only section reads "37 routes, not available to API keys" |
| entry | 8 results, showing 7 | also matches "entries" |
| capacity | 2 results | second line "Body field capacity" on POST and PUT entry |
| schema_incompatible | 1 result | second line "Error" on PUT /v1/content-type/{key} |
| content type | 12 results, showing 8 | Sections group lists Content types and Entries |
Scenario: Cap line is singular for one
Given exactly one endpoint is beyond the cap
Then the line reads "1 more endpoint. Keep typing to narrow the list."
And with several it reads "N more endpoints. Keep typing to narrow the list."
Scenario: Selecting a new endpoint
When I select PUT /v1/content-type/{key}
Then the palette closes, the card is scrolled to, expanded and focused, and flashes for 1.5 s
And the URL hash is #ep-ct-update
And the index and "Jump to" read "Content types"
Rule: Existing states stay
Scenario: Unchanged behaviour
Then states 1 to 7, 8a to 8h, the Images and variants section and the "Manage API keys" link behave as before
And figures in the existing specs (media routes, result counts, session-only count, the 409 and 422 sentence) are corrected to this feature
+47
View File
@@ -0,0 +1,47 @@
# QA procedure: Integration page, every API-key route documented
No Go test: SPA only, no /v1 call, no API change. Samples and counts follow the real API; where the board differs (purge response, built-in type samples) the API wins.
## Automated SPA (Vitest + Vue Test Utils, `task test:web`)
Files: extend `web/src/views/__tests__/IntegrationView.spec.ts`, `web/src/views/__tests__/IntegrationSearch.spec.ts` and the pure matcher spec if present. Same setup as the existing specs (router at `/integration`, stubbed scroll helpers, fake timers for the flash). No test freezes the system date. Also correct the superseded figures in the existing specs.
Cases (`it.each` for outlines):
1. Index order, 14 items, desktop index and mobile select.
2. Group table: titles, counts (Articles 8, Media 11, Sites and authors 2, "1 route" singular for Settings), method, path and access chip per route, "API key OK" chip.
3. `ct-fields` anchor above the first Content types route, not an index item; Field types table rows; sample field object.
4. PUT badge colours equal the PATCH colours.
5. Expanded card outline: request or query fields and error rows for the eight routes of the outline, with code-style captions.
6. Response samples: entries list shape, entry sample keys, `media` keyed by field key, built-in type has no `icon`, purge response has exactly the seven keys, purge-settings keys, POST cards without a status line.
7. GET /v1/media/: Query parameters (page, size), object response, 400 "The media query is invalid.". Real API: no X-Total-Count header and no "Invalid page or size." on media.
8. GET /v1/author/ page and size; GET /v1/site/.
9. Session only: derived "37 routes", six sub-groups in order, every `/v1/system` route in System (PATCH /v1/settings joins it), DELETE /v1/auth/register under Sign in and account, row styling, unchanged banner.
10. Errors table: eight rows (400, 401, 403, 404, 409, 422, 429, 500), both 403 texts plus the note, new 404 codes, 409 and 422 content, literal-body note, old "no 409 or 422 row" sentence absent.
11. Authentication copy.
12. Search: index size 85 (42 documented, 37 session-only, 6 public auth); each query of the outline with its announced count and second line; "entry" matches "entries"; cap line singular and plural.
13. Select PUT /v1/content-type/{key}: card expanded and focused, flash class then removed after 1500 ms, hash `#ep-ct-update`, index reads "Content types".
14. Scroll-spy marks Content types (state 9j).
15. Regression: the existing cases still pass.
## Visual (`task visual:check -- integration`, desktop and mobile, light and dark)
References: `docs/design/screens/integration.md`, `docs/design/source/IntegrationPage.dc.html`, `IntegrationSearch.dc.html`, states of `RenewCMS Screens Back-office.dc.html`. Add setups to `web/visual/states.ts` with no /v1 call unmocked, using the board's sample data (events type, entry sample, fingerprint c41f9a7e).
| state | setup |
|---|---|
| 8c, 8d, 8f | updated counts |
| 9a | scroll to ct-fields, open ct-create on curl |
| 9b | open en-list on Response |
| 9c | open en-update on fetch |
| 9d | scroll to errors |
| 9e | scroll to session |
| 9f to 9i | search "entry", "capacity", "schema_incompatible", "content type" |
| 9j | scroll-spy on Content types; mobile Jump to open |
No clock-sensitive state exists. A diff exception needs the user's explicit approval.
## Manual checks (screenshots cannot cover)
- The search palette and the Session only list on mobile (design tool did not confirm them), and dark mode.
- Keyboard and focus order across the new groups, hover, the 1.5 s flash and its reduced-motion ring.
- Scroll offset under the sticky bar for `ct-fields` and the last Session only sub-group.
- Long paths such as `/v1/content-type/{key}/entry/{id}` ellipsise in collapsed rows and in the palette on mobile.
+8 -8
View File
@@ -106,20 +106,20 @@ Feature: Integration page search, a jump-to command palette on /integration
Scenario: 8c Typing "media"
When the user types "media"
Then group "Endpoints" lists the first 6 of 11 media routes
Then group "Endpoints" lists the first 6 of the 16 matching routes (GET /v1/builtin-type/ first, because its description names Media, then the Media routes)
And group "Sections" lists "Media" with description "11 routes"
And the line "5 more endpoints. Keep typing to narrow the list." follows the rows
And the count reads "12 results"
And the live region reads "12 results, showing 7"
And the line "10 more endpoints. Keep typing to narrow the list." follows the rows
And the count reads "17 results"
And the live region reads "17 results, showing 7"
And "media" is highlighted with --rc-sand on --rc-on-sand in paths and descriptions
Scenario: 8d More endpoints than the cap
When the user types "404"
Then group "Endpoints" lists 6 rows, each with a second line "Error" and the match, for example "404 Article not found"
And group "Sections" lists "Errors" with description "Status codes and messages"
And the line "8 more endpoints. Keep typing to narrow the list." follows the endpoint rows
And the count reads "15 results"
And the live region reads "15 results, showing 7"
And the line "17 more endpoints. Keep typing to narrow the list." follows the endpoint rows
And the count reads "24 results"
And the live region reads "24 results, showing 7"
Scenario: 8e Match on a body field
When the user types "keep_id"
@@ -132,7 +132,7 @@ Feature: Integration page search, a jump-to command palette on /integration
When the user types "login"
Then group "Endpoints" lists POST /v1/auth/login, "Log in", with a dashed "Session only" chip and a lock icon
And its path weight is 400 and its text is --ui-text-muted
And group "Sections" lists "Session only" with a lock icon and "25 routes, not available to API keys"
And group "Sections" lists "Session only" with a lock icon and "37 routes, not available to API keys"
And the count reads "2 results"
Scenario: 8g No results
+5 -5
View File
@@ -17,10 +17,10 @@ Cases (one `it` per scenario or Examples row; `it.each` for outlines):
2. Shortcuts: `/`, Ctrl+K and Cmd+K open; `/` ignored in input, textarea and contenteditable and not typed into the palette input; Ctrl+K opens from inside an input and calls preventDefault; listeners removed on unmount; Ctrl/Cmd+S in the editor is not affected.
3. Open focus: input focused; close returns focus to the opener; Cancel on mobile.
4. Empty query: the 5 Suggested rows in order, first selected, "5 suggestions" in footer and live region; no recent searches stored.
5. "media": 6 endpoint rows of 11, Sections row Media "11 routes", cap line "5 more endpoints. Keep typing to narrow the list.", "12 results", live "12 results, showing 7", highlight marks.
6. "404": 6 rows with "Error" second line, Errors section, cap line "8 more endpoints. Keep typing to narrow the list.", "15 results", live "15 results, showing 7".
5. "media": 6 endpoint rows of 16, Sections row Media "11 routes", cap line "10 more endpoints. Keep typing to narrow the list.", "17 results", live "17 results, showing 7", highlight marks.
6. "404": 6 rows with "Error" second line, Errors section, cap line "17 more endpoints. Keep typing to narrow the list.", "24 results", live "24 results, showing 7".
7. "keep_id": one row, "Body field keep_id", "1 result". "page": GET /v1/site/{key}/sitemap shows "Query parameter page"; GET /v1/article/ matches in its description and has no second line.
8. "login": dashed chip with lock, weight 400, Session only section row "25 routes, not available to API keys" with lock icon, "2 results".
8. "login": dashed chip with lock, weight 400, Session only section row "37 routes, not available to API keys" with lock icon, "2 results".
9. "xyz": empty copy, "0 results", live "No results".
10. Matcher unit table: multi-term AND, case-insensitive, substring, "mdia" no match, extra spaces trimmed, path/description matches first then catalogue order, no search over code samples or response examples.
11. Keyboard: ArrowDown/ArrowUp wrap, Home, End, typing resets to row 1, Enter selects, Esc closes with no scroll and no hash change.
@@ -41,8 +41,8 @@ References: `docs/design/screens/integration.md` "Search (jump-to palette)", `do
|---|---|---|
| 8a Trigger focused | focus the trigger | |
| 8b Open, empty | open the palette | 5 Suggested rows |
| 8c "media" | type "media" | 6 of 11 endpoints, Media section, 12 results |
| 8d "404", third row selected | type "404", ArrowDown x2 | cap line, 15 results |
| 8c "media" | type "media" | 6 of 16 endpoints, Media section, 17 results |
| 8d "404", third row selected | type "404", ArrowDown x2 | cap line, 24 results |
| 8e "keep_id" | type "keep_id" | 1 result |
| 8f "login" | type "login" | session-only styling |
| 8g no results | type "xyz" | |