Skip to content

S-003: IWindowsWorkerClient contract + null-impl + worker-absence detection#712

Open
benhegartysefe wants to merge 1 commit into
mainfrom
feat/423-s003-worker-client
Open

S-003: IWindowsWorkerClient contract + null-impl + worker-absence detection#712
benhegartysefe wants to merge 1 commit into
mainfrom
feat/423-s003-worker-client

Conversation

@benhegartysefe

Copy link
Copy Markdown
Contributor

Summary

  • IS step S-003 from the API split work. Establishes the seam every later worker-move step (S-004 / S-005 / S-006) extends.
  • New IWindowsWorkerClient interface (empty marker for now; later steps add methods).
  • Two implementations: real HttpWindowsWorkerClient (typed HttpClient with WorkerKeyDelegatingHandler injecting X-Worker-Key), and WorkerUnavailableClient (null impl that throws WorkerUnavailableException → global filter renders 503 {"error":"windows_worker_unavailable","endpoint":"…"}).
  • New WindowsWorker:Enabled config flag selects the implementation. Defaults to false so existing Windows installs behave unchanged at this commit (flag flips true in S-008 when the installer wires the worker).
  • Resolves HLPS U-6 (URL discovery — config-file), U-7 (contract via shared DTOs in Dorc.ApiModel — to be exercised by later steps), U-11 (worker-absence detection — explicit config flag).

Test plan

  • Dorc.Api.Tests/WindowsWorkerClientTests.cs — 4 new tests covering the delegating handler's header injection (with and without configured secret) and the global exception filter's behaviour (translate + ignore).
  • Dorc.Api.Tests: 194 / 194 pass (4 new + 190 existing).
  • Dorc.Api builds clean.

Independent of PR #706 / #710 / #711 per IS multi-PR plan. S-004 / S-005 / S-006 stack on this once it merges.

🤖 Generated with Claude Code

…ection

Establishes the seam every later worker-move step (S-004 / S-005 / S-006)
extends. Resolves HLPS U-6 (URL discovery via config), U-7 (contract DTO
location: Dorc.ApiModel — future steps), and U-11 (worker-absence
detection: explicit WindowsWorker:Enabled config flag).

New types:
- Dorc.Api/Interfaces/IWindowsWorkerClient.cs — empty marker interface;
  later S-steps add concrete worker methods.
- Dorc.Api/Services/HttpWindowsWorkerClient.cs — real HTTP impl; takes
  injected typed HttpClient.
- Dorc.Api/Services/WorkerUnavailableClient.cs — null impl; methods (added
  by later steps) throw WorkerUnavailableException.
- Dorc.Api/Services/WorkerKeyDelegatingHandler.cs — DelegatingHandler that
  attaches the X-Worker-Key shared secret to every outbound call.
- Dorc.Api/Services/WorkerUnavailableExceptionFilter.cs — global
  IExceptionFilter that translates WorkerUnavailableException to a
  documented 503 body {"error":"windows_worker_unavailable","endpoint":"..."}.
- Dorc.Api/Exceptions/WorkerUnavailableException.cs — typed exception.

DI wiring in Program.cs:
- Reads WindowsWorker:Enabled bool. When true: AddHttpClient<...> for
  the typed client with WorkerKeyDelegatingHandler. When false:
  AddSingleton<IWindowsWorkerClient, WorkerUnavailableClient>.
- WorkerUnavailableExceptionFilter registered globally on controllers.

Config: appsettings.json gains WindowsWorker section. Enabled defaults
to false so existing Windows-install topology is unchanged at this commit
(no worker process running). Flag flips true in S-008 (installer).

Tests (Dorc.Api.Tests/WindowsWorkerClientTests.cs) — 4 pass:
- WorkerKeyDelegatingHandler adds the X-Worker-Key header from config.
- WorkerKeyDelegatingHandler omits the header when secret is empty
  (lets the worker's auth scheme emit the documented 401 cleanly).
- WorkerUnavailableExceptionFilter translates WorkerUnavailableException
  → 503 + documented body shape.
- WorkerUnavailableExceptionFilter ignores other exception types.

Test results:
- Dorc.Api.Tests: 194 / 194 pass (4 new + 190 existing).

Branched off main; independent of #706 / #710 / #711 per IS multi-PR plan.
S-004/S-005/S-006 stack on this once it merges (they add methods to the
interface that both implementations cover).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LastRequest = request;
return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
Assert.AreEqual(StatusCodes.Status503ServiceUnavailable, result!.StatusCode);

// Body is anonymous: {error="windows_worker_unavailable", endpoint="reset-password"}
var body = result.Value!.GetType();
@github-actions

Copy link
Copy Markdown

Test Results

314 tests  +4   314 ✅ +4   7s ⏱️ -4s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 198f07f. ± Comparison against base commit f0fbf91.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant