[APIView] Extract shared ICopilotHttpService for apiview-copilot calls#15619
[APIView] Extract shared ICopilotHttpService for apiview-copilot calls#15619helen229 wants to merge 2 commits into
Conversation
…calls Centralizes endpoint resolution, bearer-token injection, HttpClient lifecycle, and JSON (de)serialization into a single service. Migrates 8 call sites across 5 files: - ReviewManager (StartCopilotReviewJobAsync x2, GetCopilotReviewJobAsync) - CommentsManager (agent mention x2) - CopilotJobProcessor (polling) - APIRevisionsController (drop-detection poll) - ReviewSearch (resolve-package) Drops IConfiguration / IHttpClientFactory / ICopilotAuthenticationService from constructors of these classes (only used for the duplicated copilot plumbing). Updates unit tests to construct CopilotHttpService from existing mocks so HttpMessageHandler-level test plumbing keeps working unchanged. A follow-up will migrate the AgentController.ReportIssueAsync call site once #15347 (the PR introducing AgentController) merges.
There was a problem hiding this comment.
Pull request overview
Extracts a shared ICopilotHttpService that centralizes endpoint resolution, bearer-token injection, HttpClient lifecycle, and JSON (de)serialization for apiview-copilot calls, removing duplicated plumbing from 8 call sites across 5 files.
Changes:
- Adds
ICopilotHttpService/CopilotHttpService(withGetAsync<T>,PostAsync[<T>], and a rawSendAsyncescape-hatch) and registers it as a singleton inStartup. - Migrates
ReviewManager,CommentsManager,CopilotJobProcessor,APIRevisionsController, andReviewSearchto the new service, dropping the now-unusedIConfiguration/IHttpClientFactory/ICopilotAuthenticationServicector params. - Updates 9 unit-test files to construct a real
CopilotHttpServicefrom the existing mocks, preserving the existingHttpMessageHandlersetup with no test-logic changes.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
Services/Interfaces/ICopilotHttpService.cs |
New interface for centralized copilot HTTP calls |
Services/CopilotHttpService.cs |
Implementation owning endpoint + auth + JSON plumbing |
Startup.cs |
DI registration of ICopilotHttpService as singleton |
Managers/ReviewManager.cs |
Replaces 3 hand-rolled copilot HTTP calls with the new service |
Managers/CommentsManager.cs |
Replaces 2 mention-endpoint calls; drops unused deps |
HostedServices/CopilotJobProcessor.cs |
Polling loop now uses GetAsync<T> |
LeanControllers/APIRevisionsController.cs |
Drop-detection poll uses GetAsync<T> |
Managers/ReviewSearch.cs |
resolve-package POST routed through SendAsync |
APIViewUnitTests/*.cs (9 files) |
Test ctors updated to wrap mocks in a real CopilotHttpService |
| _mockCopilotAuthService.Object, | ||
| _mockHttpClientFactory.Object, | ||
| _mockConfiguration.Object, | ||
| new CopilotHttpService( |
There was a problem hiding this comment.
why are not you doing a mock of ICopilotHttpService instead of creating a new CopilotHttpService(?
There was a problem hiding this comment.
-
Minimal-diff refactor. These tests already had full HTTP-layer wiring (HttpMessageHandler mock + HttpClient + IHttpClientFactory + ICopilotAuthenticationService). Wrapping that existing setup in new CopilotHttpService(...) was a one-line change per ctor site. Switching to Mock would have required rewriting every arrange block
-
Exercises the real seam. The real instance runs actual JSON serialization, URL composition (BuildUrl), and bearer-token header injection. The mock at HttpMessageHandler is the most realistic interception point — closer to the wire — so regressions in CopilotHttpService itself get caught by these tests.
-
No behavior change risk. Using the real type guarantees the test outcomes are identical to pre-refactor behavior.
There was a problem hiding this comment.
but you are not exercising the http-layer on any of that test, on top of that we are removing the idea of those tests being unit isolated tests, if you break something meaningful on that layer, is every test going to break?
Summary
Extracts a single ICopilotHttpService that owns endpoint resolution, bearer-token injection,
HttpClientlifecycle, and JSON (de)serialization for talking to the apiview-copilot service. Removes the duplicated plumbing from 8 call sites across 5 files.Motivated by review feedback on #15347 — the new
AgentControllerwould have been the 6th place duplicating this same dance.Changes
New
src/dotnet/APIView/APIViewWeb/Services/Interfaces/ICopilotHttpService.cs— interface withGetAsync<T>,PostAsync<T>,PostAsync, and a rawSendAsyncescape-hatch.src/dotnet/APIView/APIViewWeb/Services/CopilotHttpService.cs— implementation. ReadsCopilotServiceEndpoint, attaches bearer fromICopilotAuthenticationService, usesIHttpClientFactory.CreateClient().DI
Startup.cs— registersICopilotHttpServiceas a singleton next toICopilotAuthenticationService.Migrated call sites (8 across 5 files)
Managers/ReviewManager.csPOST api-review/start×2,GET api-review/{jobId}Managers/CommentsManager.csPOST api-review/mention×2HostedServices/CopilotJobProcessor.csGET api-review/{jobId}(poll loop)LeanControllers/APIRevisionsController.csGET api-review/{jobId}(drop-detection)Managers/ReviewSearch.csPOST api-review/resolve-packageFor each class, dropped the ctor params that are now only used through the new service:
IConfiguration,IHttpClientFactory,ICopilotAuthenticationService. Verified by grep that those deps weren't used for anything else in those classes (CommentsManagerkeepsSystem.Net.Http.Headers— it's used by an unrelated GitHub call).Tests
9 test files updated. To minimize churn, each ctor call now constructs a real
CopilotHttpServicefrom the existingConfiguration/HttpClientFactory/CopilotAuthmocks, so the existingHttpMessageHandler.Protected().Setup(SendAsync, ...)plumbing keeps working unchanged. No test logic was modified.Verification
dotnet build APIViewWeb.csproj— clean.dotnet test APIViewUnitTests.csproj— 790 passed.Out of scope (intentional follow-ups)
AgentController.ReportIssueAsync(added by [APIView] Add "Report an Issue" dialog component #15347) will adopt this service in a follow-up commit on that branch —AgentControllerdoesn't exist onmainyet, so it's not migrated here.CommentsManager.LoadTaggableUsersstill doesnew HttpClient()for the GitHub public-members API. That's a different concern (not copilot) and would benefit from its own factory-based refactor.CopilotJobProcessorstill has a hand-rolledPoller; could be Polly. Not in scope.