feat(audience): validate required properties for reserved events at runtime#823
Merged
Merged
Conversation
Audience SDK — Build Size
SDK Size = build minus empty app. Change = vs baseline. Fails if any platform exceeds its absolute size limit. |
nattb8
marked this pull request as ready for review
July 23, 2026 06:15
nattb8
force-pushed
the
feat/sdk-687-reserved-event-validation
branch
from
July 23, 2026 06:41
6dfb6de to
5025759
Compare
…untime Track(string, Dictionary) let a caller send purchase/progression/resource/ achievement_unlocked/milestone_reached with no properties at all, bypassing every check the typed IEvent classes enforce. It now validates the same required properties those classes do, throwing ArgumentException the same way an empty event name already does in that method. Also fixes an inconsistency this change would otherwise have made worse: Track(IEvent) caught every ToProperties() exception and warned instead of throwing, including for Immutable's own built-in events. That catch exists to stop a buggy third-party IEvent from crashing the game, but a caller forgetting a required field on Purchase/Progression/etc is a caller bug like any other, and should throw the same way Identify()/Alias() already do for equivalent mistakes. A new internal IBuiltInEvent marker lets Track(IEvent) tell the two cases apart: built-in events now throw straight through, a consumer's own custom IEvent implementation is still caught and dropped with a warning. A new test reflects over every IBuiltInEvent implementation to guard against a future one being added without a matching ReservedEvents.RequiredProperties entry. Purchase.Value changes from decimal to string: a decimal can't precisely hold very large or high-precision amounts, and this keeps it consistent with the TS SDK's equivalent change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nattb8
force-pushed
the
feat/sdk-687-reserved-event-validation
branch
from
July 23, 2026 08:44
5025759 to
29d6623
Compare
JCSanPedro
approved these changes
Jul 23, 2026
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.
Summary
Ticket: SDK-687
Track(string, Dictionary)now validates required properties forpurchase,progression,resource,achievement_unlocked, andmilestone_reached, throwingArgumentExceptionif any are missing — previously it silently accepted these reserved events with no properties at all, bypassing every check the typed classes enforce.IBuiltInEventmarker interface letsTrack(IEvent)tell Immutable's own typed classes apart from a consumer's customIEvent. Built-in events now throw straight through on a validation failure (matchingIdentify()/Alias()'s existing behavior for caller mistakes); a consumer's own customIEventis still caught and dropped with a warning, since a third-party bug shouldn't crash the game.IBuiltInEventimplementation to catch a future one being added without a matchingReservedEvents.RequiredPropertiesentry.Purchase.Valuechanges fromdecimaltostring, to avoid precision loss on very large or high-precision amounts.Track("purchase", new Dictionary<string,object>())Track(new Purchase{Currency="USD"})(missingValue)Track(IEvent)and warn-droppedIdentify()/Alias()Track(new SomeThirdPartyCustomEvent())whoseToProperties()throwsPurchase.ValuedecimalstringBreaking changes to flag before release: existing code setting
Purchase.Valuewith a decimal literal (e.g.9.99m) will fail to compile. Existing code with a latent bug that sends an incomplete built-in typed event will now throw instead of silently warning — worth a major version bump and changelog callout, not a silent release.Test plan
dotnet build,dotnet testpass: 394 passed, 1 skipped (pre-existing), 0 faileddotnet format --verify-no-changescleanmilestone_reached), all-required-props-present does not throw, unrecognised event names still unvalidated, a built-in typed event (Purchase) missing a required field now throws instead of warn-dropping, a genuinely customIEventwhoseToProperties()throws is still caught and warn-dropped, everyIBuiltInEventimplementation has aReservedEventsentry🤖 Generated with Claude Code