Skip to content

Host team avatars instead of only linking them - #1280

Merged
CollinBeczak merged 3 commits into
mainfrom
team-avatars-2
Sep 15, 2026
Merged

CollinBeczak merged 3 commits into
mainfrom
team-avatars-2

Conversation

@CollinBeczak

Copy link
Copy Markdown
Contributor

Split from #1268
dependent on: #1272

Replays the team avatar work onto main, which now carries the team images feature from #1272. That landed as a squash, so the avatar branch's own copy of team images conflicted with the shipped version rather than merging with it; only the avatar half is unique and is what is replayed here.

The avatar migration moves from 124 to 125, since main's 124 is the team owner role and challenge ownership. It only references groups and users, so it transplants unchanged.

Replays the team avatar work onto main, which now carries the team
images feature from #1272. That landed as a squash, so the avatar
branch's own copy of team images conflicted with the shipped version
rather than merging with it; only the avatar half is unique and is
what is replayed here.

The avatar migration moves from 124 to 125, since main's 124 is the
team owner role and challenge ownership. It only references groups and
users, so it transplants unchanged.
The avatar endpoints were written before the team images feature landed
and ended up restating pieces of it. Where the two now sit side by side,
the avatar side defers to what is already there.

The repository drops its hand-written row parser for the macro parser the
query's aliases already suit, and its inline copy of TeamImageRepository's
dataParser for the shared one. TeamAvatar's aliases for the format and
size rules are gone; callers read them off TeamImage, which is where the
single rule lives.

The multipart parser bound moves to TeamImage.MAX_UPLOAD_BYTES. The image
endpoint had reasoned its way to MAX_SIZE_BYTES * 2 and written down why;
the avatar endpoint had no bound at all and fell back to the 15MB disk
buffer, spooling an oversized upload in full before rejecting it for
exceeding 2MB.

Serving an avatar now builds the ETag from the metadata row and only
reads the bytes on a cache miss, so revalidating no longer pulls up to
2MB out of Postgres to answer 304. That query existed already and was
simply never wired up. Deleting an avatar commits the delete and the url
clear together, as uploading already did, and the serving path is built
once rather than spelled out in both urlFor and isStoredAvatarUrl.
Four things that were wrong with the avatar endpoints, all of them
visible only once the team images feature landed beside them.

updateTeam announced itself over the websocket even when running inside
a caller's transaction, so a rollback broadcast an update that never
committed. Old callers always committed immediately, but both avatar
paths now wrap it, which makes the bug reachable. The broadcast is now
the caller's to make once its transaction has committed.

Reading an upload and writing image bytes back out were spelled out
twice, once per controller, down to three identical error strings and
the ETag and cache headers. They move to a mixin both controllers use.
Duplicating the two security headers is the part worth avoiding: a
change made in one place and missed in the other fails silently.

The avatar rules - check ordering, the transaction, which url is ours -
lived in Action bodies where nothing else could reach them and no test
could reach them either. They move to TeamAvatarService, matching the
route the image endpoints already take through TeamImageService.

Removing an avatar decided whether the stored url was ours by matching
a route prefix, which the frontend then had to reimplement. It now
reconstructs the url from the stored row and compares exactly, so a team
that has since typed their own url keeps it. Serving an avatar is also
cacheable indefinitely rather than for a day, since the url carries the
upload timestamp and the bytes behind one can never change.
@sonarqubecloud

Copy link
Copy Markdown

@CollinBeczak
CollinBeczak merged commit 508e1d3 into main Sep 15, 2026
9 checks passed
@CollinBeczak
CollinBeczak deleted the team-avatars-2 branch September 15, 2026 19:41
CollinBeczak added a commit that referenced this pull request Sep 15, 2026
Main squash-merged the team images (#1272) and team avatars (#1280)
work this branch was sitting on top of, so its own copies of both
collided with the shipped versions rather than merging with them. Every
one of those conflicts resolves to main's side: the branch carried the
pre-review originals, and main has the evolved versions, including the
avatar service and shared image mixin that landed with #1280.

The challenge reports evolution moves from 125 to 126, since main's 125
is now the team_avatars table.

The only genuine merges were the three test registries where both sides
had added an entry - the master suite, the helper's tags and TestSpec's
service mocks - which take both.
CollinBeczak added a commit that referenced this pull request Sep 15, 2026
…ers-that-never-stack

Main gained team images (#1272), team avatars (#1280) and challenge
reports (#1270). None of them touch the explore tile queries, so the
only collision was the evolution number: #1270 took 126, so the tile
migration moves to 127.
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