|
| 1 | +# Error Handling Fix Plan — Linq2GraphQL.Client |
| 2 | + |
| 3 | +## Overview |
| 4 | +Fix 6 GraphQL error handling deficiencies. 4 phases, 12 steps. Each step leaves codebase buildable and backward-compatible. |
| 5 | + |
| 6 | +## Phase 1: Foundation (Issues 1, 6, 3) — Data model changes only |
| 7 | + |
| 8 | +### Step 1.1: Add `Extensions` property to `GraphQueryError` |
| 9 | +- **File**: `src/Linq2GraphQL.Client/Exceptions/GraphQueryExecutionException.cs` |
| 10 | +- **Action**: Add `[JsonPropertyName("extensions")] public Dictionary<string, object>? Extensions { get; set; }` to `GraphQueryError` |
| 11 | +- **Effort**: 2 min | **Risk**: Low | **BC**: 100% |
| 12 | + |
| 13 | +### Step 1.2: Create `GraphErrorCode` enum + classifier |
| 14 | +- **New File**: `src/Linq2GraphQL.Client/Exceptions/GraphErrorCode.cs` |
| 15 | +- **Action**: Enum with standard codes (UNAUTHENTICATED, FORBIDDEN, VALIDATION, etc.) + `GraphErrorCodeClassifier` static helper |
| 16 | +- **Also**: Add `ErrorCode` computed property to `GraphQueryError` |
| 17 | +- **Effort**: 10 min | **Risk**: Low | **BC**: 100% |
| 18 | + |
| 19 | +### Step 1.3: Create `GraphResult<T>` wrapper |
| 20 | +- **New File**: `src/Linq2GraphQL.Client/GraphResult.cs` |
| 21 | +- **Action**: `GraphResult<T>` with `Data`, `Errors`, `Extensions`, `HasErrors`, `HasData` |
| 22 | +- **Effort**: 5 min | **Risk**: Low | **BC**: 100% |
| 23 | + |
| 24 | +## Phase 2: Core Query/Mutation Error Handling (Issues 2, 3) |
| 25 | + |
| 26 | +### Step 2.1: Refactor `QueryExecutor` — internal `ProcessResponseFull` |
| 27 | +- **File**: `src/Linq2GraphQL.Client/QueryExecutor.cs` |
| 28 | +- **Action**: New `internal GraphResult<T> ProcessResponseFull(...)` that parses data+errors+extensions. Keep existing `ProcessResponse` as backward-compat wrapper. |
| 29 | +- **Effort**: 15 min | **Risk**: Medium | **BC**: 100% |
| 30 | + |
| 31 | +### Step 2.2: Add `ExecuteWithResultAsync` to `GraphQueryExecute` |
| 32 | +- **File**: `src/Linq2GraphQL.Client/GraphQueryExecute.cs` + `QueryExecutor.cs` |
| 33 | +- **Action**: Add `ExecuteRawAsync` to QueryExecutor, add `ExecuteBaseWithResultAsync` + `ExecuteWithResultAsync` to GraphQueryExecute |
| 34 | +- **Effort**: 15 min | **Risk**: Medium | **BC**: 100% |
| 35 | + |
| 36 | +### Step 2.3: Add `ExecuteWithResultAsync` to cursor paging |
| 37 | +- **File**: `src/Linq2GraphQL.Client/GraphQueryExecute.cs` + `GraphCursorPager.cs` |
| 38 | +- **Action**: Same pattern for `GraphCursorQueryExecute` and pager |
| 39 | +- **Effort**: 10 min | **Risk**: Low | **BC**: 100% |
| 40 | + |
| 41 | +## Phase 3: Subscription Error Handling (Issues 4, 5) |
| 42 | + |
| 43 | +### Step 3.1: Handle WS `type: "error"` and `type: "complete"` messages |
| 44 | +- **Files**: `WebsocketRequestTypes.cs`, `WebsocketResponse.cs`, `WSClient.cs` |
| 45 | +- **Action**: Add message type constants, handle error/complete in WSClient message routing, propagate via OnError/OnCompleted |
| 46 | +- **Effort**: 25 min | **Risk**: Medium | **BC**: Behavioral fix |
| 47 | + |
| 48 | +### Step 3.2: Add error resilience to subscription pipeline |
| 49 | +- **File**: `GraphSubscriptionExecute.cs` |
| 50 | +- **Action**: Replace `Select` with `SelectMany` + try/catch — skip bad messages, don't kill stream |
| 51 | +- **Effort**: 15 min | **Risk**: Medium | **BC**: Behavioral fix |
| 52 | + |
| 53 | +### Step 3.3: SSE client error handling |
| 54 | +- **File**: `SSEClient.cs` |
| 55 | +- **Action**: Wrap HTTP errors in `GraphQueryRequestException`, null checks, parse `event: error` SSE frames |
| 56 | +- **Effort**: 10 min | **Risk**: Low | **BC**: 100% |
| 57 | + |
| 58 | +## Phase 4: Testing & Verification |
| 59 | + |
| 60 | +### Step 4.1: Add error handling unit tests |
| 61 | +- **New File**: `test/Linq2GraphQL.Tests/ErrorHandlingTests.cs` |
| 62 | +- **Action**: Test extensions deserialization, GraphResult partial data, error codes, subscription error resilience |
| 63 | +- **Effort**: 20 min | **Risk**: Low |
| 64 | + |
| 65 | +### Step 4.2: Run full test suite |
| 66 | +- **Action**: `dotnet build` + `dotnet test` |
| 67 | +- **Effort**: 5 min |
| 68 | + |
| 69 | +## Dependency Graph |
| 70 | +``` |
| 71 | +1.1 → 1.2 → 1.3 → 2.1 → 2.2 → 2.3 |
| 72 | + ↘ |
| 73 | +1.1 → 3.1 → 3.2 |
| 74 | + ↘ 3.3 |
| 75 | +→ 4.1 → 4.2 |
| 76 | +``` |
| 77 | + |
| 78 | +## Risk Summary |
| 79 | +| Step | Risk | Mitigation | |
| 80 | +|------|------|------------| |
| 81 | +| 1.1-1.3 | Low | Additive only, no behavioral change | |
| 82 | +| 2.1-2.3 | Medium | Keep old APIs, add new opt-in APIs | |
| 83 | +| 3.1-3.2 | Medium | Test with both WS protocols | |
| 84 | +| 3.3 | Low | Defensive coding | |
| 85 | +| 4.1-4.2 | Low | Tests are additive | |
0 commit comments