Repository navigation
fix(api): decode unknown server enum values to UNKNOWN everywhere - #1027
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (19)
Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour. 📝 WalkthroughWalkthroughAPI enum deserialization now maps unrecognized values to ChangesUnknown enum handling
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The supplied evidence identifies no issue that needs resolution before merging. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
f8cee6d to
8c146e8
Compare
3e2b563 to
feed29c
Compare
8c146e8 to
3c38a6f
Compare
f73037c to
e253b2c
Compare
9279665 to
851854a
Compare
Server enums handled unrecognized values five different ways:
coerceInputValues with a property default, hand-written fromSerializedValue
surrogates, SessionTaskKey.fromRaw, nullable defaults, and no fallback at
all (OrganizationInvitation.Status failed the whole response).
Add UnknownFallbackEnumSerializer as the single policy: every enum decoded
from Clerk API responses keeps its plugin-generated serializer
(@KeepGeneratedSerializer, so @SerialName stays the only source of wire
names) and maps any unrecognized value to its UNKNOWN entry, independent of
the Json configuration or property defaults. This now also covers
PreferredSignInStrategy, and the UNKNOWN entries of SignIn.Status and
BiometricCredential.Platform/Status get @SerialName("unknown") so they
round-trip.
OrganizationInvitation.Status gains an Unknown entry for this. It only
exists for decoding, so Organization.getInvitations(status = Unknown)
returns a local failure instead of sending "unknown" as a filter.
e253b2c to
8ced4dc
Compare
) ClerkTelemetryEnvironment looked the wire name up with InstanceEnvironmentType.serializer().descriptor.getElementName(ordinal). Since #1027 that serializer is the unknown-fallback serializer, whose descriptor is a primitive string with no elements, so the lookup threw IllegalStateException whenever telemetry read the instance type. Encode the value through the serializer instead, which works for any serializer shape.
Why
Architecture review finding #11 says "Unknown server enums are handled five different ways." Before this PR they were:
coerceInputValuesplus a property default (most models).fromSerializedValuesurrogates (SignIn.Status,BiometricCredential.PlatformandBiometricCredential.Status).SessionTaskKey.fromRaw.OrganizationInvitation.Statushad noUNKNOWNentry and no default, so a new invitation status from the server failed the whole invitations response. This is the same client-decode failure class fix(api): keep response body when client sync fails #998 dealt with.Path 1 only works through
ClerkApi.jsonand only for properties that have a default. Standalone decoding, lists, and any otherJsoninstance still throw.This is PR 2 of 3 for #11 and is stacked on #1025 (typed
Strategy).Scope
UnknownFallbackEnumSerializer(internal). It is the documented single policy: an enum decoded from Clerk API responses maps any unrecognized wire value to itsUNKNOWNentry, whatever theJsonconfiguration or property default.@KeepGeneratedSerializer. Wire names therefore still come only from@SerialName, with no second hand-written mapping that could drift; that drift is how theVerification.Statusswap (fix(api): correct swapped Verification.Status serial names #986) happened.SignIn.Status,SignUp.Status,Verification.Status,InstanceEnvironmentType,PreferredSignInStrategyBiometricCredential.Platform,BiometricCredential.StatusSession.SessionStatus,SessionVerification.Status,SessionVerification.LevelOrganizationInvitation.Status, which gains anUnknownentryBiometricCredentialPolicy(local store),ForPayerType(request parameter only),OrganizationSystemPermission(never decoded),SharedSessionSyncSnapshot.State(app-to-app payload).CredentialType,ResultType,MfaType,VerificationType,IdTokenProvider,HostedAuthMode.SessionTaskKeyalready falls back toUNKNOWNfrom a raw string.UNKNOWNentries ofSignIn.Status,BiometricCredential.PlatformandBiometricCredential.Statusget@SerialName("unknown"), so they encode as"unknown"(matching theirserializedValue) instead of"UNKNOWN".Organization.getInvitations(status = OrganizationInvitation.Status.Unknown)returns a localClerkResultfailure (invitation_status_invalid) without calling Clerk.Unknownonly exists for decoding, and sending"unknown"as a filter would be meaningless.fromSerializedValuesurrogates stay. They also keep the raw value for round-tripping, which is a separate concern.UnknownFallbackEnumSerializerTest:"brand_new_value_from_the_server"to its fallback, both with a plainJson(nocoerceInputValues) and withClerkApi.json.UNKNOWNentry andPreferredSignInStrategy.OrganizationInvitationwith an unrecognized status now decodes. Before this change it threw.OrganizationGetInvitationsTest:getInvitations(status = Unknown)fails locally and never touches the API.Serial names. The review notes that 228 of 246
@SerialNameannotations repeat the snake_case naming. I did not remove them.JsonNamingStrategy.SnakeCaseis set only onClerkApi.json. OtherJsoninstances that decode the same models have no naming strategy, and so would silently stop decoding renamed fields:BiometricCredentialLegacyStore, the native magic-link store, and test JSON. Removing the annotations safely needs a per-model fixture round-trip suite first. That belongs in its own PR and is deferred.Blast Radius
UNKNOWNinstead of throwing, in every position and with everyJsoninstance. Known values encode and decode exactly as before; the round-trip test covers every entry.OrganizationInvitation.Statusgains anUnknownentry. A consumer's exhaustivewhenexpression over that enum needs an extra branch. That is the price of not failing the whole response. All other enums already hadUNKNOWN.generatedSerializer(). Opting into@KeepGeneratedSerializer(an experimental API) stays inside the SDK through@OptIn.Testing
./gradlew spotlessApply :source:api:detekt :source:ui:detekt :source:api:testDebugUnitTest :source:ui:testDebugUnitTest :samples:quickstart:compileDebugKotlin :samples:custom-flows:compileDebugKotlin :e2e:compileDebugKotlin: all pass.🤖 Generated with Claude Code
Summary by CodeRabbit
UNKNOWN, rather than failing.