Skip to content

[AI-FSSDK] [FSSDK-12813] Relax campaign_id/entity_id validation to non-empty string per updated spec#645

Closed
jaeopt wants to merge 1 commit into
masterfrom
ai/jaeopt/FSSDK-12813-relax-validation
Closed

[AI-FSSDK] [FSSDK-12813] Relax campaign_id/entity_id validation to non-empty string per updated spec#645
jaeopt wants to merge 1 commit into
masterfrom
ai/jaeopt/FSSDK-12813-relax-validation

Conversation

@jaeopt

@jaeopt jaeopt commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to the merged #642 (released as 5.4.1), which normalized decisions[].campaign_id, decisions[].variation_id, and events[].entity_id on outgoing decision impression events.

The FSSDK-12813 spec was updated on 2026-06-24 to relax the validation contract for campaign_id and entity_id only. This PR brings the Swift SDK in line with the relaxed contract.

What the spec relaxed

  • decisions[].campaign_id and events[].entity_id MUST be non-empty strings. IDs may be opaque (e.g. "layer_abc", "default-12345") — any non-empty string is valid regardless of character content.
  • The fallback to experiment_id now fires only when the source value is the empty string "" (or nil / missing). Previously it fired on any non-numeric input.

What is unchanged

  • decisions[].variation_id keeps the stricter numeric-string-only contract per spec FR-003. Empty, whitespace, or non-numeric variation_id values still normalize to nil (JSON null).
  • No change to Sources/Data Model/DispatchEvents/BatchEvent.swiftDecision.variationID: String? and its explicit-null encoder from [AI-FSSDK] [FSSDK-12813] normalize campaign_id, variation_id, and entity_id on decision events #642 remain correct.
  • No change to FR-006 (events never dropped) or FR-007 (no warnings on the normalization path).
  • No public API change, no datafile schema change, no SDK version bump (5.4.1 already released; this rides the next release ticket).

Changes

  • Sources/Implementation/Events/BatchEventBuilder.swift
    • normalizeDecisionIds(...) now gates campaign_id with a new isNonEmptyString(_:) helper instead of isValidNumericIdString(_:). The variation_id branch is untouched.
    • Adds isNonEmptyString(_:) -> Bool (returns !value.isEmpty).
    • Retains isValidNumericIdString(_:) — still used by the variation_id branch per spec FR-003.
    • Doc comments updated to cite the 2026-06-24 spec relaxation.
  • Tests/OptimizelyTests-Common/BatchEventBuilderTests_HoldoutIds.swift
    • Flipped: testNormalize_whitespaceCampaignFallsBackToExperimentIdtestNormalize_whitespaceCampaignPassesThrough (whitespace is non-empty, so it passes through unchanged).
    • Flipped: testNormalize_nonNumericCampaignFallsBackToExperimentIdtestNormalize_nonNumericCampaignPassesThrough (non-numeric is non-empty, so it passes through unchanged).
    • Added: testNormalize_opaqueCampaignPassesThrough_FSSDK12813_relaxation — direct proof that an opaque ID "layer_abc" passes through campaign_id unchanged with no fallback to experiment_id.
    • Added: testIsNonEmptyString_acceptsAnyNonEmptyContent and testIsNonEmptyString_rejectsEmptyOnly — unit coverage for the new helper.
    • All variation_id tests unchanged.
    • All isValidNumericIdString tests unchanged (still used by variation_id).
    • Header comment updated to describe the relaxed contract.

Verification

  • xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-iOS -destination 'platform=iOS Simulator,name=iPhone 17' testnot executed in this run; CoreSimulator was unavailable in the sandboxed environment. Local reviewers should run the command above. The change is mechanical: one validator swap on one branch of normalizeDecisionIds, plus tests flipped/added in lockstep.
  • Pre-existing impression-event regression suites referenced in [AI-FSSDK] [FSSDK-12813] normalize campaign_id, variation_id, and entity_id on decision events #642 (BatchEventBuilderTests_Events, BatchEventBuilderTests_Attributes, BatchEventBuilderTests_EventTags, BatchEventBuilderTests_Region, DecisionServiceTests_Holdouts) all used non-empty placeholders for campaign_id and so are unaffected by this relaxation (FR-011 audit).

Jira Ticket

FSSDK-12813

Related

…ing per updated spec

The updated spec (2026-06-24) relaxes the validation contract for
`decisions[].campaign_id` and `events[].entity_id`: any non-empty
string is now valid, including opaque IDs like "layer_abc" or
"default-12345". The fallback to `experiment_id` now fires only when
the source value is the empty string (previously fired on any
non-numeric input).

`decisions[].variation_id` is unchanged: it keeps the stricter
numeric-string-only contract and still falls back to nil (JSON null)
for empty / whitespace / non-numeric values.

Changes:
- BatchEventBuilder.normalizeDecisionIds now uses isNonEmptyString
  (new helper) for the campaign_id branch.
- isValidNumericIdString is retained and still gates variation_id.
- Tests flipped: whitespace and non-numeric campaign_id now assert
  passthrough (was: fallback). Added explicit opaque-ID passthrough
  test as proof of the relaxation. Added isNonEmptyString unit tests.
  variation_id tests unchanged.
@jaeopt jaeopt closed this Jun 26, 2026
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