Skip to content

Add V2FollowerController so /api/v2/followers/* is routed - #1626

Open
toddmitchell wants to merge 9 commits into
mainfrom
add-v2-followers-controller
Open

Add V2FollowerController so /api/v2/followers/* is routed#1626
toddmitchell wants to merge 9 commits into
mainfrom
add-v2-followers-controller

Conversation

@toddmitchell

Copy link
Copy Markdown
Member

Fixes #1611

What

There was no followers controller anywhere under UnifiedV2/, so every follow-related call from a v2 bearer-token client 404'd. FollowerControllerBase was exposed on owner, app-v1 and guest only.

Adds V2FollowerController at /api/v2/followers, plus the Followers route constant.

All six routes the KMP feed client calls are now served, along with GET /follower:

Route Permission asserted by FollowerService
POST /api/v2/followers/follow ManageFeed
POST /api/v2/followers/unfollow ManageFeed
POST /api/v2/followers/sync-feed-history ManageFeed
GET /api/v2/followers/IdentitiesIFollow ReadWhoIFollow
GET /api/v2/followers/IdentityIFollow ReadWhoIFollow
GET /api/v2/followers/followingme ReadMyFollowers
GET /api/v2/followers/follower ReadMyFollowers

Design note

The controller subclasses FollowerControllerBase rather than re-declaring the actions in the usual v2 style. The actions and their route templates live on the base, so this keeps v2 in lockstep with the owner/app/guest controllers instead of leaving a second copy to drift. Trade-off: inherited actions can't carry per-action [SwaggerOperation(Tags = ...)] like the other v2 controllers, so these land under the default V2Follower tag. [ApiExplorerSettings(GroupName = "v2")] still puts them in the right Swagger doc, which is what DocInclusionPredicate keys on.

Policy is OwnerOrApp, per the issue.

Tests

New FollowerV2Tests (7 tests, Odin.Hosting.Tests.V2), driving two identities through the real perimeter follow flow:

  • follow → IdentitiesIFollow contains the target
  • follow → recipient's followingme contains the follower
  • IdentityIFollow / follower return the definition from each side
  • unfollow removes it from both sides
  • sync-feed-history succeeds
  • AllFollowerRoutes_AreRouted_AndDoNotReturnNotFound — asserts the regression itself, that none of the seven routes 404s

Supporting test infra: IFollowerHttpClientApiV2 + V2FollowerClient (in Odin.Hosting.Tests/_V2/ApiClient/, alongside the other v2 clients) and a FollowersHandle on OwnerSession.

New tests pass 7/7; the full Odin.Hosting.Tests.V2 project passes 418/418 (3 pre-existing skips).

Not verified

The client-side call sites live in homebase-id/chat-kmp / homebase-api and were not exercised against this branch — route paths were matched against the table in #1611. ASP.NET routing is case-insensitive, so the mixed-case paths (IdentitiesIFollow, IdentityIFollow) resolve regardless of casing.

🤖 Generated with Claude Code

toddmitchell and others added 9 commits July 31, 2026 11:23
…ction

Every message is prefixed "Preflight" for grepping.

The important line is on the sender: one Information entry per not-ready
recipient carrying the fields the recipient reported, so a failure can be
diagnosed from the sender's own logs without access to the recipient's
server. Ready outcomes stay at Debug to keep the volume down.

The recipient logs its own refusal with a short greppable reason label
(auto-connection-not-confirmed, permission-not-granted,
caller-not-recognized, connection-needs-repair, not-configured,
requires-upgrade) alongside the raw fields.

Also raises two swallowed Debug logs to Warning -- an unusable local ICR
and a failed caller-state lookup are both real faults that were
invisible -- and an unexpected probe failure to Error.

Refs #1613

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Preflight is advisory; a Ready result can still end in nothing happening
later, and those phases were largely silent.

- SendIntroductions logs recipients that could not be enqueued (Warning).
- ReceiveIntroductions logs the new/already-connected/blocked breakdown,
  so "the introduction arrived but nothing happened" can be told apart
  from one that never arrived. Suppressing that detail matters only in
  the response to the introducer, not in our own logs.
- SendOutstandingConnectionRequests logs each batch abandonment.

That last one exposes an apparent bug left unchanged here: both skip
conditions `break` rather than `continue`, so one already-connected or
already-requested introducee abandons every remaining introduction in the
batch, while the log message describes a per-recipient skip. Changing
background-worker control flow is out of scope for this branch; the log
now says how many were dropped.

Refs #1613

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
No followers controller existed under UnifiedV2/, so every follow-related
call from a v2 bearer-token client 404'd -- follow, unfollow,
IdentitiesIFollow, followingme, IdentityIFollow and sync-feed-history.
FollowerControllerBase was exposed on owner, app-v1 and guest only.

V2FollowerController subclasses FollowerControllerBase for the same
reason those three do: the actions and their route templates live on the
base, so v2 stays in lockstep instead of drifting behind a second copy.
Policy is OwnerOrApp -- the service asserts ManageFeed for
follow/unfollow/sync, ReadWhoIFollow for the I-follow reads and
ReadMyFollowers for the follower reads, none of which a guest carries on
this surface.

Adds V2FollowerClient / IFollowerHttpClientApiV2 and a FollowersHandle on
OwnerSession, plus FollowerV2Tests covering follow -> list -> unfollow
round trips across two identities and asserting explicitly that none of
the seven routes returns 404.

Fixes #1611

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

No followers controller on unified v2 — all /api/v2/followers/* routes 404

1 participant