docs(api-guide): document flat vs nested URL path rules (section 4)

Describes which object types are always-flat (never nested in URL) for
ALL operations, vs event sub-objects which use nested paths for mutations
but flat paths for all reads (GET, list, search, delete).

Always-flat objects:
- Core: account, activity_log, address, contact, hosted_file, organization,
  page, person, site, user
- Other: archive, event, journal, post

Event sub-objects (event_badge, event_session, etc.) use nested
create_nested_obj_v3 / update_nested_obj_v3 for POST/PATCH, but flat
paths for everything else.

Includes Playwright mock URL patterns for each operation type.
This commit is contained in:
Scott Idem
2026-02-26 18:47:50 -05:00
parent 7c6f264266
commit 8cb8195ecd

View File

@@ -70,7 +70,77 @@ Modify data in the system.
---
## 4. V3 Uniform Lookup System
## 4. Flat vs Nested URL Paths
This is the single most common source of mock errors in Playwright tests. The V3 API has two URL structures and the right one to use depends on the object type and the operation.
### Always-Flat Objects
These object types use flat paths for **every** operation — GET, list, search, create, update, delete. They are never children in a nested URL.
**Core modules:**
`account`, `activity_log`, `address`, `contact`, `hosted_file`, `organization`, `page`, `person`, `site`, `user`
**Other modules:**
`archive`, `event`, `journal`, `post`
All operations for these objects use the flat helpers:
| Operation | Function | URL pattern |
|---|---|---|
| GET single | `api.get_ae_obj_v3` | `/v3/crud/{obj_type}/{id}` |
| GET list | `api.get_ae_obj_li_v3` | `/v3/crud/{obj_type}/?for_obj_id=...` |
| Search | `api.search_ae_obj_v3` | `/v3/crud/{obj_type}/search` |
| Create | `api.create_ae_obj_v3` | `/v3/crud/{obj_type}/` |
| Update | `api.patch_ae_obj_v3` | `/v3/crud/{obj_type}/{id}` |
| Delete | `api.delete_ae_obj_v3` | `/v3/crud/{obj_type}/{id}` |
### Nested Objects (Event Sub-objects)
Objects that belong to a parent (e.g. `event_badge`, `event_session`, `event_badge_template`, `event_file`, `event_presenter`, `event_presentation`, `event_device`, `event_location`) use **nested paths for mutations** but **flat paths for reads**.
| Operation | Function | URL pattern |
|---|---|---|
| GET single | `api.get_ae_obj_v3` | `/v3/crud/{obj_type}/{id}`**flat** |
| GET list | `api.get_ae_obj_li_v3` | `/v3/crud/{obj_type}/?for_obj_id=...`**flat** |
| Search | `api.search_ae_obj_v3` | `/v3/crud/{obj_type}/search`**flat** |
| Create | `api.create_nested_obj_v3` | `/v3/crud/{parent_type}/{parent_id}/{child_type}/`**nested** |
| Update | `api.update_nested_obj_v3` | `/v3/crud/{parent_type}/{parent_id}/{child_type}/{child_id}`**nested** |
| Delete | `api.delete_ae_obj_v3` | `/v3/crud/{obj_type}/{id}`**flat** |
**Example — `event_badge` under event `abc123`:**
```
GET /v3/crud/event_badge/badge-xyz ← flat (get_ae_obj_v3)
GET /v3/crud/event_badge/?for_obj_id=abc123 ← flat (get_ae_obj_li_v3)
POST /v3/crud/event_badge/search ← flat (search_ae_obj_v3)
POST /v3/crud/event/abc123/event_badge/ ← nested (create_nested_obj_v3)
PATCH /v3/crud/event/abc123/event_badge/badge-xyz ← nested (update_nested_obj_v3)
DELETE /v3/crud/event_badge/badge-xyz ← flat (delete_ae_obj_v3)
```
### Playwright Mock Rule of Thumb
Since all reads are flat, the most common mock patterns are:
```typescript
// ✅ Search (always flat)
url.includes('/v3/crud/event_badge/search') && method === 'POST'
// ✅ List (always flat, filter by query param)
url.includes('/v3/crud/event_badge/') && url.includes('for_obj_id') && method === 'GET'
// ✅ GET single (always flat)
url.includes('/v3/crud/event_badge/badge-xyz') && method === 'GET'
// ✅ Create (nested for event sub-objects)
url.match(/\/v3\/crud\/event\/[^/]+\/event_badge\/$/) && method === 'POST'
// ✅ Update (nested for event sub-objects)
url.match(/\/v3\/crud\/event\/[^/]+\/event_badge\/badge-xyz/) && method === 'PATCH'
```
---
## 5. V3 Uniform Lookup System
<!-- was section 4 -->
The V3 Lookup system provides a hierarchical, deduplicated interface for standardized tables (Countries, Timezones, etc.). It supports global defaults, account overrides, and site-specific whitelisting.
@@ -104,7 +174,7 @@ To limit lookups for a specific site, add a `lookup_policy` to the `site.cfg_jso
---
## 5. Event File Data Retrieval (Hosted Files)
## 6. Event File Data Retrieval (Hosted Files)
Every Event File (`event_file`) **must** have a linked Hosted File (`hosted_file`). The Hosted File itself is a metadata record for binary content (files), which is accessed via separate Action endpoints (e.g., `/v3/action/hosted_file/download`). This API endpoint provides metadata about the associated hosted file. To retrieve this additional metadata:
@@ -123,7 +193,7 @@ Every Event File (`event_file`) **must** have a linked Hosted File (`hosted_file
---
## 5. Troubleshooting 403 Forbidden
## 7. Troubleshooting 403 Forbidden
If you receive a 403 on a valid ID:
1. Verify `x-aether-api-key` is correct.