Skip to content

refactor: split the lock routes into their own handler - #34

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/split-lock-route-handler
Sep 16, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/split-lock-route-handler

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #4

What changed

GitLfsCacheHandler took 14 constructor parameters against SonarQube's allowed 7. It was built around a preamble the object routes genuinely share — resolve the upstream, validate the token, decide whether bytes come from the store or upstream — but had since taken on lock listing, snapshot invalidation and lock fan-out, which share none of it.

The shared preamble (route parsing, upstream resolution, the allow-list check) stays in GitLfsCacheHandler, which now only dispatches. Past that point the routes divide into two groups, each with its own handler:

Class Holds Params
GitLfsCacheHandler dispatch preamble only 14 → 7
LockRouteHandler (new) LockListService, ILockSnapshotStore, LockFanOut 5
ObjectRouteHandler (new) batch, transfer, verify — tokens and the store 9
UpstreamRelay (new) the verbatim relay and response copy both groups fall back to 2

No behaviour change: every route reaches the same code under the same conditions. The dependencies were not bundled into an options object — the issue calls that out as the wrong fix, and it would have left one class doing two jobs.

Two things worth a reviewer's eye

ObjectRouteHandler sits at 9 parameters, not 7. Batch, transfer and verify stay together because that is the grouping the original design comment argued for, and the issue endorses it ("that argument held when it handled batch, transfers and verify"). Splitting batch out would land every class at ≤7, but it goes against that reasoning, so I left it. Say the word if you'd rather have it. The reported class is fixed either way, and the worst class drops from 14 to 9.

GitLfsCacheHandler is now internal. It has to be, since its constructor takes the new internal collaborators — and it matches AssemblyInfo's own statement that "the endpoint handlers, store internals, and token codec details are internal". It is referenced only from MapGitLfsCache inside this assembly, so the supported surface (AddGitLfsCache, MapGitLfsCache) is unchanged. If you'd rather not touch visibility at all, the alternative is making the three new types public instead.

Tagged [patch] on the basis that the supported surface is unchanged; happy to re-tag if you read the visibility change differently.

Testing

  • Full suite green: 326 passed, 0 failed, build clean with 0 warnings (dotnet test GitLfsCache.slnx).
  • The 321 pre-existing tests are untouched. Lock behaviour is already covered end-to-end through the real DI pipeline by LockListCachingTests and LockFanOutTests — including snapshot invalidation on create, the disabled-subsystem paths, pagination and fan-out partial failure — so they verify both handlers post-split.
  • New HandlerCompositionTests (5 tests) guards the boundary. Verified it fails against the previous shape by temporarily putting the lock dependencies back on the dispatcher:
failed Dispatcher_StaysWithinTheConstructorParameterBudget
  GitLfsCacheHandler dispatches; it does not do the work. It now takes 10 dependencies
  (registry, allowList, objects, locks, lockLists, lockSnapshots, lockFanOut, relay, options, logger).

failed Dispatcher_RoutesLocksThroughTheLockHandlerRatherThanHoldingItsDependencies
  GitLfsCacheHandler holds LockListService directly; it belongs to LockRouteHandler.

Both pass after the split. The tests assert the dependency boundary rather than behaviour, since the behaviour either side is already covered — the point is to notice the next time an unrelated dependency lands on the dispatcher, which is exactly how it reached 14.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BQfgoVAD3B5hEqPbSUHVUB


Generated by Claude Code

GitLfsCacheHandler took 14 constructor parameters against SonarQube's
allowed 7. The handler was built around a preamble the object routes
genuinely share -- resolve the upstream, validate the token, decide
whether bytes come from the store or upstream -- but it had since taken
on lock listing, snapshot invalidation and lock fan-out, which share
none of it.

The shared preamble (route parsing, upstream resolution, the allow-list
check) stays in GitLfsCacheHandler, which now only dispatches. Past that
point the routes divide into two groups with their own handlers:

- LockRouteHandler holds LockListService, ILockSnapshotStore and
  LockFanOut, and nothing about tokens or the object store.
- ObjectRouteHandler keeps batch, transfer and verify together, which is
  the grouping the original design argued for.
- UpstreamRelay carries the verbatim relay and response copy that both
  groups fall back to, rather than duplicating it.

No behaviour change: every route reaches the same code by the same
conditions, and the existing 321 tests pass untouched.

The dependencies were not bundled into an options object, which would
have satisfied the analyzer while leaving one class doing two jobs.

GitLfsCacheHandler is now internal, matching AssemblyInfo's statement
that the endpoint handlers are internal. It is referenced only from
MapGitLfsCache inside this assembly, so nothing in the supported surface
(AddGitLfsCache, MapGitLfsCache) changes.

Adds HandlerCompositionTests, which fails against the previous shape:
it holds the dispatcher to the 7-parameter budget and asserts the lock
dependencies reach it through LockRouteHandler rather than directly.

Fixes #4

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQfgoVAD3B5hEqPbSUHVUB
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit c962782 into main Sep 16, 2026
13 checks passed
@matt-edmondson
matt-edmondson deleted the claude/split-lock-route-handler branch September 16, 2026 02:53
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.

GitLfsCacheHandler takes 14 constructor parameters: split the locks routes into their own handler

1 participant