feat: add V3 action endpoint for event exhibit tracking export
- New router: app/routers/api_v3_actions_event_exhibit.py
- GET /v3/action/event_exhibit/{exhibit_id}/tracking_export
- Full V3 auth (x-aether-api-key + account context)
- Multi-tenant ownership check via check_account_access
- Permission gate: leads_api_access flag OR manager-level access
- Returns CSV or XLSX file attachment (return_file=false for JSON)
- Flattens responses_json custom Q&A columns; strips HTML from exhibitor_notes
- Exports all records regardless of hidden/enabled state
- Registered in registry.py under prefix /v3/action/event_exhibit
- New E2E test: tests/e2e/test_e2e_v3_action_event_exhibit_tracking_export.py
- 7/7 tests passing against dev-api.oneskyit.com
- Docs: GUIDE__AE_API_V3_for_Frontend.md — new Section 7 covering endpoint
usage, columns, leads_api_access dual-purpose (3rd-party API + UI export gate)
- Docs: tests/README.md — added test to table and when-to-run matrix
This commit is contained in:
@@ -154,8 +154,77 @@ Frontend guidance:
|
||||
- These endpoints run synchronously and can take time for large inputs; for heavy or batch workloads use a queued job pattern instead.
|
||||
- These endpoints may take time for large inputs. Prefer using `?background=true` to schedule work and receive a `202 Accepted` response for async processing. For heavy or batch workloads use a queued job pattern instead.
|
||||
|
||||
---
|
||||
|
||||
## 5. Troubleshooting 403 Forbidden
|
||||
## 7. Event Exhibit Tracking Export (Leads Export)
|
||||
|
||||
Allows an exhibitor to download all lead-capture records for their exhibit as a CSV or XLSX file.
|
||||
|
||||
- **Method:** `GET`
|
||||
- **Path:** `/v3/action/event_exhibit/{exhibit_id}/tracking_export`
|
||||
- **Auth:** Standard V3 headers (`x-aether-api-key` + `x-account-id` or `?jwt=`)
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `file_type` | `CSV` \| `XLSX` | `CSV` | Output format. |
|
||||
| `return_file` | bool | `true` | `true` → file download response. `false` → JSON body with row data. |
|
||||
|
||||
### Response
|
||||
|
||||
- `Content-Type: text/csv` (CSV) or `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` (XLSX)
|
||||
- `Content-Disposition: attachment; filename="leads_export_<timestamp>.csv"`
|
||||
- If there are no tracking records, a valid file with headers only is returned (not a 404).
|
||||
|
||||
### Columns Returned
|
||||
|
||||
Fixed columns (always present), followed by any custom question columns flattened from `responses_json`:
|
||||
|
||||
`event_exhibit_tracking_id`, `created_on`, `updated_on`, `event_exhibit_name`, `event_badge_full_name`, `event_badge_email`, `event_badge_professional_title`, `event_badge_affiliations`, `event_badge_location`, `event_badge_country`, `external_person_id`, `exhibitor_notes`, `priority`, `enable`, `hide`, `[custom question codes…]`
|
||||
|
||||
> **Note:** `exhibitor_notes` has HTML tags stripped automatically for clean CSV output.
|
||||
|
||||
### Permission Requirement — `leads_api_access`
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This endpoint enforces a **per-exhibit permission flag**. The `event_exhibit` record **must** have `leads_api_access = true` set in the database, OR the caller must have manager-level account access (JWT with `manager: true`).
|
||||
>
|
||||
> If `leads_api_access` is `false` or `null` on the exhibit, the API returns:
|
||||
> ```json
|
||||
> { "detail": "Access denied: leads API access is not enabled for this exhibit." }
|
||||
> ```
|
||||
> **Fix:** Enable the flag on the exhibit record via `PATCH /v3/crud/event_exhibit/{id}` with `{ "leads_api_access": true }`, or set it directly in the database/admin panel.
|
||||
|
||||
#### Dual purpose of `leads_api_access`
|
||||
|
||||
This flag serves two related but distinct roles:
|
||||
|
||||
1. **3rd-party API access (original intent):** Controls whether external systems (exhibitor apps, badge-scanning devices, etc.) are permitted to push or pull lead data for this exhibit via the API.
|
||||
2. **UI export gate (new):** The frontend should read `leads_api_access` from the exhibit record and use it to show or hide the export/download button. Only render the button when the flag is `true` — this prevents users from triggering a request that will always 403.
|
||||
|
||||
The recommended pattern is to fetch the exhibit record first and gate the UI on this field before the user ever sees the export option. The API enforces the same check server-side as a safety net.
|
||||
|
||||
### Example Request
|
||||
|
||||
```ts
|
||||
const resp = await fetch(
|
||||
`https://dev-api.oneskyit.com/v3/action/event_exhibit/${exhibitId}/tracking_export?file_type=CSV&return_file=true`,
|
||||
{
|
||||
headers: {
|
||||
'x-aether-api-key': API_KEY,
|
||||
'x-account-id': accountId,
|
||||
},
|
||||
}
|
||||
);
|
||||
// resp is a file blob — use URL.createObjectURL() or trigger a download
|
||||
const blob = await resp.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Troubleshooting 403 Forbidden
|
||||
|
||||
If you receive a 403 on a valid ID:
|
||||
1. Verify `x-aether-api-key` is correct.
|
||||
|
||||
Reference in New Issue
Block a user