chore: extract JSON-RPC response construction into a factory#1653
Conversation
…xtraction Freezes the exact serialized JSON string for the 5 JSON-RPC response shapes (success / error / dispatch-accepted / heartbeat / protocol-mismatch) before moving them out of JsonRpcRequestProcessor, so the upcoming pure move to JsonRpcResponseFactory can be verified byte-equal.
JsonRpcRequestProcessor mixed request parse/dispatch with success, error, dispatch-accepted, heartbeat, and protocol-mismatch response construction, plus 10 co-located response DTOs. Move all response construction into a new stateless JsonRpcResponseFactory and split the DTOs into class-per-file, so the processor is left owning only parse, dispatch, notification, and cancellation handling. CreateSuccessResponse/CreateErrorResponse/CreateCliProtocolMismatchResponse/ AppendTimingIfRequested widen from private to internal since the processor now calls them across the class boundary; the two already- internal CreateDispatchAcceptedResponse/CreateHeartbeatResponse and the DTO visibility are unchanged. Refs: Refactor round 3 ToDo R3-5
📝 WalkthroughWalkthroughThis PR extracts JSON-RPC response construction from ChangesResponse factory extraction
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant JsonRpcRequestProcessor
participant JsonRpcResponseFactory
participant UserFriendlyErrorConverter
JsonRpcRequestProcessor->>JsonRpcResponseFactory: CreateSuccessResponse(result)
JsonRpcResponseFactory-->>JsonRpcRequestProcessor: success JSON
JsonRpcRequestProcessor->>JsonRpcResponseFactory: CreateErrorResponse(exception)
JsonRpcResponseFactory->>UserFriendlyErrorConverter: convert exception
UserFriendlyErrorConverter-->>JsonRpcResponseFactory: friendly message
JsonRpcResponseFactory-->>JsonRpcRequestProcessor: error JSON
JsonRpcRequestProcessor->>JsonRpcResponseFactory: CreateDispatchAcceptedResponse(heartbeatIntervalSeconds)
JsonRpcResponseFactory-->>JsonRpcRequestProcessor: accepted JSON
JsonRpcRequestProcessor->>JsonRpcResponseFactory: CreateCliProtocolMismatchResponse(currentProtocolVersion)
JsonRpcResponseFactory-->>JsonRpcRequestProcessor: cli_update_required JSON
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
Packages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs (2)
97-125: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider extracting dispatch-accepted and heartbeat response shapes into named DTOs.
CreateDispatchAcceptedResponseandCreateHeartbeatResponsebuild wire shapes via anonymous types, whileCreateSuccessResponse,CreateErrorResponse, andCreateCliProtocolMismatchResponseuse named DTOs (JsonRpcSuccessResponse,JsonRpcErrorResponse). The anonymous approach works but makes theuloopmetadata contract implicit rather than explicit, and the shapes can't be referenced in tests or documentation by type.If the anonymous types are intentional to preserve exact wire shape (e.g., the
uloopfield isn't part of the standard DTOs), consider adding lightweight named DTOs for these two shapes in a future PR.Also applies to: 127-145
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Packages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs` around lines 97 - 125, The dispatch-accepted and heartbeat response payloads are currently built with anonymous types, which makes the `uloop` contract implicit and hard to reuse or test. Refactor `CreateDispatchAcceptedResponse` and `CreateHeartbeatResponse` in `JsonRpcResponseFactory` to use lightweight named DTOs for the wire shapes, similar to `JsonRpcSuccessResponse` and `JsonRpcErrorResponse`, while preserving the exact serialized JSON structure for `accepted`, `phase`, and `heartbeatIntervalSeconds`.
37-52: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winConsider logging serialization failures in the fallback catch block.
The broad
catch (Exception)silently swallows serialization errors and returns a fallback response. While the fallback behavior is correct (client always gets a valid JSON-RPC frame), the lack of any logging makes serialization issues impossible to diagnose in production. Based on learnings, the Fail Fast policy encourages surfacing unexpected errors rather than silently swallowing them.A
Debug.LogWarninghere would preserve the fallback behavior while making failures observable.🔊 Suggested addition
catch (Exception ex) { + Debug.LogWarning($"JSON-RPC serialization failed for id={id}, falling back to safe response: {ex}"); // Return safe fallback response for any serialization errors object fallbackResult = new🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Packages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs` around lines 37 - 52, The JsonRpcResponseFactory serialization fallback in the catch block is swallowing exceptions without any observability; keep the safe fallback response but add a Debug.LogWarning when serialization fails. Use the existing JsonRpcResponseFactory logic around the JsonConvert.SerializeObject path to log the exception details and relevant context (such as the response type or commandType) before returning the fallback JSON-RPC frame.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@Packages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs`:
- Around line 97-125: The dispatch-accepted and heartbeat response payloads are
currently built with anonymous types, which makes the `uloop` contract implicit
and hard to reuse or test. Refactor `CreateDispatchAcceptedResponse` and
`CreateHeartbeatResponse` in `JsonRpcResponseFactory` to use lightweight named
DTOs for the wire shapes, similar to `JsonRpcSuccessResponse` and
`JsonRpcErrorResponse`, while preserving the exact serialized JSON structure for
`accepted`, `phase`, and `heartbeatIntervalSeconds`.
- Around line 37-52: The JsonRpcResponseFactory serialization fallback in the
catch block is swallowing exceptions without any observability; keep the safe
fallback response but add a Debug.LogWarning when serialization fails. Use the
existing JsonRpcResponseFactory logic around the JsonConvert.SerializeObject
path to log the exception details and relevant context (such as the response
type or commandType) before returning the fallback JSON-RPC frame.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 670816db-3b86-4f12-afab-81d23056023e
⛔ Files ignored due to path filters (12)
Assets/Tests/Editor/JsonRpcResponseFactoryWireShapeCharacterizationTests.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/CliUpdateRequiredErrorData.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/InternalErrorData.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcError.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcErrorData.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcErrorResponse.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcErrorTypes.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcResponsePhases.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/JsonRpcSuccessResponse.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/SecurityBlockedErrorData.cs.metais excluded by none and included by nonePackages/src/Editor/Infrastructure/Api/ServerBusyErrorData.cs.metais excluded by none and included by none
📒 Files selected for processing (14)
Assets/Tests/Editor/JsonRpcHeartbeatTests.csAssets/Tests/Editor/JsonRpcResponseFactoryWireShapeCharacterizationTests.csPackages/src/Editor/Infrastructure/Api/CliUpdateRequiredErrorData.csPackages/src/Editor/Infrastructure/Api/InternalErrorData.csPackages/src/Editor/Infrastructure/Api/JsonRpcError.csPackages/src/Editor/Infrastructure/Api/JsonRpcErrorData.csPackages/src/Editor/Infrastructure/Api/JsonRpcErrorResponse.csPackages/src/Editor/Infrastructure/Api/JsonRpcErrorTypes.csPackages/src/Editor/Infrastructure/Api/JsonRpcRequestProcessor.csPackages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.csPackages/src/Editor/Infrastructure/Api/JsonRpcResponsePhases.csPackages/src/Editor/Infrastructure/Api/JsonRpcSuccessResponse.csPackages/src/Editor/Infrastructure/Api/SecurityBlockedErrorData.csPackages/src/Editor/Infrastructure/Api/ServerBusyErrorData.cs
|
Thanks for the review, CodeRabbit. Both nitpicks are valid design ideas but out of scope for this PR:
No code changes made for either point. |
Refs: Refactor round 3 ToDo R3-5 (
2026-07-09_Unity CLI Loop リファクタ第3弾 ToDo.md)Summary
JsonRpcRequestProcessormixed request parse/dispatch with JSON-RPC response construction (success / error / dispatch-accepted / heartbeat / protocol-mismatch) and 10 co-located response DTOs.JsonRpcResponseFactoryand split the 10 DTOs into class-per-file. The processor now owns only parse / dispatch / notification / cancellation handling.CreateSuccessResponse/CreateErrorResponse/CreateCliProtocolMismatchResponse/AppendTimingIfRequestedwiden fromprivatetointernalbecause the processor now calls them across the class boundary. The already-internalCreateDispatchAcceptedResponse/CreateHeartbeatResponseand all DTO visibility are unchanged (byte-equal).Verification
JsonRpcResponseFactoryand stayed green unchanged, proving byte equality.JsonRpcResponseFactory., and 4 methods widenedprivate→internal). No logic/body differences.StaticFacadeStateGuardTeststracked paths are unchanged.Test plan
uloop compile-- 0 errors / 0 warningsuloop run-testson JSON-RPC related suites -- all green