feat(#571): search-only recipient picker source #576
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix-571b"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
GET /api/v1/recipients?q=— the source behind the share panel's recipient picker.Why not the endpoint the issue named. #571 said to reuse
GET /api/v1/admin/groups/candidates. It is registered asrequireAdmin, so the picker would 403 for every non-admin — which is almost everyone who shares a file. It also answers "list everything" with no query at all: the right shape for an admin screen, the wrong shape for a control every session touches. Exposing it would have added a user-enumeration surface on purpose.So this one cannot be asked for everything. A query is mandatory and must be at least 2 characters; a shorter one is a 400 that says so, rather than an empty list. That is deliberate — an empty list would let a client treat
""as "list everyone" and get a plausible-looking answer.Prefix-only, but on token boundaries. You approved "prefix matches only". A strict whole-name prefix would make anyone with a
firstname.surnameaccount unfindable by surname, so a prefix of any token (.,-,_, space,@) counts. Mid-token matching stays out:anikadoes not matchni, so nobody walks the directory two characters at a time.TestRecipientMatchesIsPrefixOnlyandTestRecipientMatchesFindsTokenPrefixespin both halves.Ranking, then the cap. Exact match, then whole-name prefix, then token prefix, then alphabetical. The cap of 20 is applied after ranking, so truncation drops the worst matches rather than whichever sorted first — and
truncatedcomes back in the response so the UI can ask for more characters instead of quietly understating how many people matched.Smaller decisions, each with a test: the caller is excluded (sharing with yourself is not a thing anyone means to do); a name offered by two sources is one recipient, but a user and a group of the same name are two, because they are two different things to share with; an unreachable directory degrades the picker to what it can see rather than failing it.
Registration sits before the store guard in
registerGroups, so the picker still works on a local-only instance where the accounts alone are the picker.Two notes on how this went, since both are recorded in the workflow comments. The one-shot detects the group lister from
*GroupStore's own method set rather than from call sites — the first attempt scannedgroups.goforh.Groups.Store.X(and found only mutators, because the listing happens through another path. And the first green failed on my test, not the code: interpolating a query containing raw spaces into a URL makeshttptest.NewRequestpanic on the request line. All nine pure-function tests had already passed; the fix wasurl.QueryEscape.