Skip to content

feat(audience): enforce required properties on Track(IEvent) predefined events (SDK-225)#697

Merged
ImmutableJeffrey merged 1 commit into
mainfrom
feat/sdk-225-track-ievent-enforce-required-fields
Apr 22, 2026
Merged

feat(audience): enforce required properties on Track(IEvent) predefined events (SDK-225)#697
ImmutableJeffrey merged 1 commit into
mainfrom
feat/sdk-225-track-ievent-enforce-required-fields

Conversation

@ImmutableJeffrey
Copy link
Copy Markdown
Collaborator

Summary

  • Flips required value-typed fields on the typed IEvent implementations to nullable — Progression.Status, Resource.Flow, Resource.Amount, Purchase.Value — so an unset caller is distinguishable from the enum / zero default.
  • Each ToProperties() throws ArgumentException naming the missing field when a required property is null. The existing try/catch in ImmutableAudience.Track(IEvent) catches, logs, and drops the event — consistent with the never-crash convention on the game thread.
  • Required fields already validated (Resource.Currency, Purchase.Currency via ISO 4217, MilestoneReached.Name) stay as-is — no behavior change there.
  • Tightens the Track(string) doc comment with a concrete example (purchase without currency / value) so the attribution / conversion reporting risk is explicit, per Natalie's follow-up on the merged PR.
  • 7 new tests: a missing-required-field case per predefined event, plus an integration test on Track(IEvent) confirming a malformed Purchase is caught, logged, and dropped (161 → 168).

Rationale

Attribution and conversion pipelines depend on predefined events carrying their required fields. The C# compiler enforces field types but does not enforce that required fields are set to a non-default value, so Track(new Purchase { Currency = "USD" }) was silently shipping a zero-value purchase and breaking downstream reporting.

Web SDK sidesteps this with TS conditional types at compile time. Unity C# cannot — required fields must be enforced at runtime instead. Natalie's explicit direction on Slack: "for Unity maybe we can only call out in the docs & comments for Track(string...) and enforce it in Track(IEvent)."

Field mapping matches the canonical schema in Unity SDK Event Reference v1.

Breaking change

Callers that read a required property directly — e.g. decimal v = purchase.Value; — need purchase.Value!.Value after this change. Construction patterns like new Purchase { Value = 9.99m } are unaffected because of implicit nullable conversion.

Out of scope

  • Runtime validation for Track(string) with a predefined event name — docs-only per Natalie's direction.
  • Compile-time enforcement analogous to the Web SDK's TS conditional types — not possible in C#.

Linear: SDK-225
Sister ticket: SDK-222 (PR #696)
Parent: SDK-99 Unity SDK v1

…ed events

Attribution and conversion pipelines depend on predefined events
carrying their required fields. The C# compiler enforces field types
but does not enforce that required fields are set to a non-default
value, so a caller like `Track(new Purchase { Currency = "USD" })`
was silently shipping a zero-value purchase and breaking downstream
reporting.

Make each required value-typed field nullable on the typed IEvent
implementations so an unset caller is distinguishable from a
zero / default value, and throw a clear `ArgumentException` from
`ToProperties()` when a required field is null. The existing
try/catch inside `ImmutableAudience.Track(IEvent)` catches the
throw, logs a warning naming the event and the missing field, and
drops the event — consistent with the never-crash convention on the
game thread.

- Progression.Status: ProgressionStatus -> ProgressionStatus?
- Resource.Flow: ResourceFlow -> ResourceFlow?
- Resource.Amount: float -> float?
- Purchase.Value: decimal -> decimal?

Track(string) is explicitly out of scope for runtime validation —
callers using the string overload opt out of the typed path. Tighten
the existing doc comment with a concrete example (purchase without
currency / value) so the gap is unambiguous.

Tests: each predefined event gets a missing-required-field case
asserting the throw; an integration test on Track(IEvent) confirms
the throw is caught, logged, and the event dropped rather than
shipping a malformed payload.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ImmutableJeffrey ImmutableJeffrey requested review from a team as code owners April 22, 2026 09:14
@ImmutableJeffrey ImmutableJeffrey merged commit d142092 into main Apr 22, 2026
19 checks passed
@ImmutableJeffrey ImmutableJeffrey deleted the feat/sdk-225-track-ievent-enforce-required-fields branch April 22, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants