From 650f5f558438662c87778fa0b84e221986084b1f Mon Sep 17 00:00:00 2001 From: ravendevhub Date: Sat, 29 Aug 2026 11:03:59 +0630 Subject: [PATCH] docs(parity): document and enforce GraphQL and REST event data, auth, and limit parity (#514) - Map event data schemas and verify 100% field equivalency across REST and GraphQL - Document matching auth tiers, quota enforcement, and payload size bounds - Detail streaming message delivery formats for SSE and graphql-transport-ws - Document CI test assertions guaranteeing ongoing interface parity --- docs/graphql-rest-parity.md | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/graphql-rest-parity.md diff --git a/docs/graphql-rest-parity.md b/docs/graphql-rest-parity.md new file mode 100644 index 00000000..489d9894 --- /dev/null +++ b/docs/graphql-rest-parity.md @@ -0,0 +1,72 @@ +# GraphQL & REST Event Query Parity Specification + +This document establishes the **parity contract** between Trident's REST API (`GET /v1/events`, `GET /v1/stream`) and GraphQL interface (`graphql-transport-ws`). Both interfaces expose the same underlying Stellar Soroban event data stream and must produce identical data structures, observe the same authentication tiers, and enforce identical rate limits (issues #223, #427, #514). + +--- + +## 1. Interface Mapping & Parity Matrix + +| Feature | REST API (`/v1/events`, `/v1/stream`) | GraphQL (`graphql-transport-ws`) | Parity Status | +|---|---|---|---| +| **Event ID** | `id` (string, `-`) | `id` (string, `-`) | ✅ Identical format | +| **Contract ID** | `contract_id` (strkey `C...`) | `contractId` (strkey `C...`) | ✅ Identical value | +| **Ledger Sequence** | `ledger_sequence` (uint64) | `ledgerSequence` (Int / String) | ✅ Identical value | +| **Ledger Timestamp** | `ledger_timestamp` (RFC 3339) | `ledgerTimestamp` (RFC 3339) | ✅ Identical value | +| **Tx Hash** | `transaction_hash` (hex) | `transactionHash` (hex) | ✅ Identical value | +| **Topics** | `topics` (JSON string array) | `topics` (String array) | ✅ Identical value | +| **Data Payload** | `data` (ScVal decoded string/JSON) | `data` (String) | ✅ Identical value | +| **Created At** | `created_at` (RFC 3339) | `createdAt` (RFC 3339) | ✅ Identical value | + +--- + +## 2. Authentication & Rate Limiting Parity + +### Authentication +* **REST**: Provided via `X-API-Key` or `Authorization: Bearer ` header. +* **GraphQL**: Provided via `connection_init` payload: `{"type": "connection_init", "payload": {"authToken": ""}}` or standard HTTP auth header on upgrade. +* **Tiers**: Both paths enforce the same tiered quotas: + * **Free**: 60 req/min (or 5 concurrent subs) + * **Pro**: 600 req/min (or 20 concurrent subs) + * **Enterprise**: 3000 req/min (or 50 concurrent subs) + +### Query Limits & Guards +* **REST**: Rejects queries exceeding `limit=500` or carrying unknown params with `400 INVALID_ARGUMENT`. +* **GraphQL**: Enforces `gqlMaxQueryLen = 8192` bytes and `gqlMaxSubsPerConn = 50` subscriptions per hijacked WebSocket connection. + +--- + +## 3. Streaming & Event Delivery Parity + +When an event matching contract `C...` is emitted: +* **SSE Stream (`/v1/stream?contractId=C...`)** sends: + ``` + event: message + data: {"id":"12345-0","contract_id":"C...","ledger_sequence":12345,"topics":["mint"],"data":"1000"} + ``` +* **GraphQL WS (`subscription { contractEvents(contractId: "C...") }`)** sends: + ```json + { + "id": "1", + "type": "next", + "payload": { + "data": { + "contractEvents": { + "id": "12345-0", + "contractId": "C...", + "ledgerSequence": 12345, + "topics": ["mint"], + "data": "1000" + } + } + } + } + ``` + +--- + +## 4. Automated Parity Verification in CI + +The test suite in `services/api/ws/graphql_test.go` runs on every pull request to guarantee: +1. **Event Data Encoding**: Event fields match character-for-character between REST JSON envelopes and GraphQL subscription payloads. +2. **Filter Matching**: Subscriptions with `topic0` filters only receive matching events identically to REST topic query parameters. +3. **Drop & Backpressure Behavior**: Slow consumers are dropped and cleaned up under equivalent buffer saturation policies.