Files
qbit-sql-workbench/docs/UI-API.md
james.maesandClaude Opus 5.5 84a7033ae4 feat(api): serve the sql-workbench routes as spec'd UI API extension endpoints
Replace SqlWorkbenchRouteProvider (21 raw /qqq/v1/sql-workbench Javalin
routes) with one MiddlewareSpecExtension "sql-workbench" (prefix
/sql-workbench). JSON operations use AbstractJsonEndpointSpec; the result
export stream extends AbstractEndpointSpec.

Paths, methods and payloads are unchanged, and so are the permission
checks, read-only gates, limits and audit. The generated fragment is
committed at openapi/qqq-v1/sql-workbench.yaml and checked by a contract
test. The server tests run in strict raw-route mode.

The core baseline becomes 4.1.0-RC.6, the first RC with the extension
point (QRun-IO/qqq#1128).

Closes #20 (architecture review QRun-IO/qqq#1150, finding F2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:48:18 -05:00

94 lines
4.9 KiB
Markdown

# SQL Workbench UI API contract
All 21 operations are registered by `SqlWorkbenchQBitProducer` through
`SqlWorkbenchEndpoints` as one `MiddlewareSpecExtension` named `sql-workbench`,
version `1.0.0`, with the established `/sql-workbench` prefix. Full URLs remain
`/qqq/v1/sql-workbench/...`. QQQ `4.1.0-RC.6` is the minimum core version.
`registerRoutes=false` disables extension registration; middleware remains optional.
The producer registers no raw routes. Twenty JSON operations extend
`AbstractJsonEndpointSpec` through `SqlWorkbenchJsonSpec`; `ExportResultSpec`
extends `AbstractEndpointSpec` directly and streams CSV, JSON or XLSX with the
existing attachment filename and `Cache-Control: no-store` headers.
The checked-in fragment is
[`src/main/resources/openapi/qqq-v1/sql-workbench.yaml`](../src/main/resources/openapi/qqq-v1/sql-workbench.yaml).
It is also included in a host's `/qqq/v1/openapi.yaml` and `.json`, and the
extension appears in `/qqq/v1/version`. All component names begin with
`SqlWorkbench`. Schemas explicitly describe the existing map payloads rather
than changing their serialization. SQL results include nullable scalar values,
binary previews and nested arrays; execute responses vary by execution status.
## Compatibility and security
No existing operation paths, methods, request or response shapes, status codes,
or export headers change. Next UI can vendor the fragment without adapting its
existing calls. New metadata consists of the extension/version advertisement,
OpenAPI schemas and stable operation identifiers.
Core authenticates the caller before each spec runs and clears `QContext` after
the request. Workbench access is checked before parsing bodies, path integers or
export formats. JSON object parsing retains `BigDecimal` precision, JSON-only
content-type checks, empty/null-body behavior, and existing error messages.
Duplicate execution UUIDs still return 409. SQL failures remain HTTP 200 with a
`FAILED`, `TIMED_OUT` or `CANCELLED` execution status.
Specs call the same `SqlWorkbenchApi`, notebook service and execution engine.
Write/EXPLAIN ANALYZE gates, sharing permissions, caller record locks, notebook
ownership, cached-result ownership, cancellation ownership, per-user concurrency
limits, audit-before-SQL and deferred audit updates remain in place. Client IPs
continue into execution audits. No request bodies or result data are logged by
the transport.
## Operations
| Method | Path (after `/qqq/v1/sql-workbench`) | operationId |
|---|---|---|
| GET | `/capabilities` | `sqlWorkbenchGetCapabilities` |
| POST | `/execute` | `sqlWorkbenchExecute` |
| POST | `/executions/{uuid}/cancel` | `sqlWorkbenchCancelExecution` |
| GET | `/executions/{uuid}/export` | `sqlWorkbenchExportResult` |
| GET | `/executions/{uuid}/rows` | `sqlWorkbenchGetRows` |
| POST | `/explain` | `sqlWorkbenchExplain` |
| GET | `/history` | `sqlWorkbenchGetHistory` |
| GET | `/notebooks` | `sqlWorkbenchListNotebooks` |
| POST | `/notebooks` | `sqlWorkbenchCreateNotebook` |
| DELETE | `/notebooks/{id}` | `sqlWorkbenchDeleteNotebook` |
| GET | `/notebooks/{id}` | `sqlWorkbenchGetNotebook` |
| PUT | `/notebooks/{id}` | `sqlWorkbenchUpdateNotebook` |
| POST | `/notebooks/{id}/duplicate` | `sqlWorkbenchDuplicateNotebook` |
| POST | `/notebooks/{id}/publish` | `sqlWorkbenchPublishNotebook` |
| GET | `/notebooks/{id}/snapshots` | `sqlWorkbenchListSnapshots` |
| POST | `/notebooks/{id}/snapshots` | `sqlWorkbenchCreateSnapshot` |
| DELETE | `/notebooks/{id}/snapshots/{snapshotId}` | `sqlWorkbenchDeleteSnapshot` |
| GET | `/notebooks/{id}/versions` | `sqlWorkbenchListVersions` |
| GET | `/notebooks/{id}/versions/{n}` | `sqlWorkbenchGetVersion` |
| POST | `/notebooks/{id}/versions/{n}/restore` | `sqlWorkbenchRestoreVersion` |
| GET | `/schema/{backendName}` | `sqlWorkbenchGetSchema` |
## Verification and regeneration
`SqlWorkbenchApiContractTest` regenerates the fragment in memory and checks it
against the checked-in file using core's deterministic `UiApiContract` tooling.
It also checks the namespace, secured specs, prefix, media types and 21 unique
operation IDs. Intentional contract changes can regenerate the file with:
```sh
./mvnw -B test -Dtest=SqlWorkbenchApiContractTest -DsqlWorkbench.openapi.update=true
```
Before RC.6 is released, local verification uses the separately installed core:
```sh
./mvnw -B verify -Dqqq.snapshot.version=4.1.0-ARCHRC6-SNAPSHOT
```
The POM must continue to name `4.1.0-RC.6`; the local override is never a release
dependency. On PowerShell, quote each `-D...` argument and use `mvnw.cmd`.
`SqlWorkbenchEndpointSpecTest` runs real HTTP through
`QApplicationJavalinServer.withStrictUiApiRoutes(true)`. It preserves the former
route-provider tests and adds 401/403 coverage for every operation, mutation
denials, execution limits, audited rejection, exact decimals, SQL failures and
all three exports. Engine tests continue to verify audit and transaction safety.