fix(api): let optional fields be cleared on member, group and system edits - #70
Open
SiteRelEnby wants to merge 1 commit into
Open
fix(api): let optional fields be cleared on member, group and system edits#70SiteRelEnby wants to merge 1 commit into
SiteRelEnby wants to merge 1 commit into
Conversation
…edits
Emptying an optional field and saving did nothing: the old value came back.
This affected a member's display name, pronouns, colour, birthday and
description; a group's description and colour; and a system's description, tag
and colour. Removing an avatar or banner was broken the same way, on both a
member and the system profile.
These endpoints read the body with exclude_unset, so presence is the contract:
omitted leaves a field alone, an explicit null clears it. Moshi omits null
fields, so a cleared field was simply absent from the request and the server
correctly left it as it was. Confirmed by probing the serialiser, which turned
a member edit with five cleared fields into {"name":"Alex"}.
The obvious shortcut does not work. Turning on null serialisation for these
bodies would also null the fields backed by NOT NULL columns that the server
explicitly rejects an explicit null for, failing the whole save instead of one
field. So each field is either clearable or omit-when-null, matching the
server's _reject_explicit_null lists, and the two helpers are named after which
is which so a field added later has to make the choice.
No ViewModel changes: they already produce null for an emptied field, which now
means what it always should have.
Found by auditing for the failure mode behind the custom-field-value fix. That
one is the same bug in a different shape, where the server requires the key
rather than treating its absence as unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by auditing for the failure mode behind #68. It was not a one-off.
What was broken
Emptying an optional field and saving did nothing - the old value came back.
Pronouns is probably the one people noticed; avatar removal is the one that looks most like the app ignoring you.
Why
These endpoints read the body with
exclude_unset, so presence is the contract:Moshi omits null fields, which expresses the first two-thirds and leaves no way to clear anything. The ViewModels already produce
nullfor an emptied field; it just never reached the wire.Probed rather than assumed - a member edit with five cleared fields serialised to:
The server then correctly left everything alone.
Why not just turn on null serialisation
That was the tempting one-liner, and it would have been worse. Each of these bodies also carries fields backed by NOT NULL columns that the server explicitly rejects an explicit null for - a member's
nameandprivacy, a system'sdate_format,privacy,show_member_created_dateand the front defaults. Blanket nulls would start sending those and fail the entire save rather than one field.So each field is either clearable or omit-when-null, matching the server's
_reject_explicit_nulllists. The two helpers are namedclears(...)andomitsWhenNull(...)so a field added later has to make the choice rather than inheriting a default.No ViewModel changes
The call sites already do
takeIf { it.isNotBlank() }, producing null for an emptied field. That now means what it always should have.Follow-up when #69 lands
#69 adds
emojitoMemberUpdateand works around this by sending an empty string. Once both are in, addclears("emoji", ...)to the member adapter and simplify that workaround to a plain null. There's a comment at the spot so it isn't lost. Nothing breaks in either merge order - the workaround keeps working regardless.Testing
11 unit tests, built through the app's own Moshi via
NetworkModule.provideMoshi()so a missing registration fails here rather than only in the field. They pin both directions: fields that must clear, and fields that must stay absent because a null would fail the save.:app:assemblePlayRelease,:app:assembleOpenRelease,:app:testPlayReleaseUnitTestgreen.Device checklist: