Skip to content

Commit f59444f

Browse files
committed
[FSSDK-12839] Relax campaign_id/entity_id to non-empty string per spec
1 parent 620014a commit f59444f

4 files changed

Lines changed: 27 additions & 13 deletions

File tree

optimizely/event/event_factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def _create_visitor(cls, event: Optional[user_event.UserEvent], logger: Logger)
136136

137137
campaign_id: str = (
138138
experiment_layerId
139-
if validator.is_numeric_string_id(experiment_layerId)
139+
if validator.is_non_empty_string(experiment_layerId)
140140
else experiment_id
141141
)
142142
normalized_variation_id: Optional[str] = (

optimizely/event_builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def _get_required_params_for_impression(
189189
Dict consisting of decisions and events info for impression event.
190190
"""
191191
campaign_id: str = (
192-
experiment.layerId if validator.is_numeric_string_id(experiment.layerId) else experiment.id
192+
experiment.layerId if validator.is_non_empty_string(experiment.layerId) else experiment.id
193193
)
194194
normalized_variation_id: Optional[str] = (
195195
variation_id if validator.is_numeric_string_id(variation_id) else None

tests/test_event_builder.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,17 +1057,24 @@ def test_campaign_id_none_falls_back_to_experiment_id(self):
10571057
snapshot = self._build_impression(experiment, '111129')
10581058
self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id)
10591059

1060-
def test_campaign_id_non_numeric_string_falls_back_to_experiment_id(self):
1061-
experiment = self._with_layer_id('abc')
1060+
def test_campaign_id_opaque_string_passes_through(self):
1061+
# Per FR-001: any non-empty string (numeric or opaque like "default-12345"
1062+
# or "layer_abc") is valid and passes through unchanged.
1063+
experiment = self._with_layer_id('default-12345')
10621064
snapshot = self._build_impression(experiment, '111129')
1063-
self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id)
1065+
self.assertEqual(self._decision(snapshot)['campaign_id'], 'default-12345')
10641066

1065-
def test_campaign_id_whitespace_falls_back_to_experiment_id(self):
1067+
def test_campaign_id_whitespace_string_passes_through(self):
1068+
# Per FR-001 / spec Assumptions: a non-empty string of any character
1069+
# content is valid; whitespace-only strings are non-empty and accepted.
10661070
experiment = self._with_layer_id(' ')
10671071
snapshot = self._build_impression(experiment, '111129')
1068-
self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id)
1072+
self.assertEqual(self._decision(snapshot)['campaign_id'], ' ')
10691073

10701074
def test_campaign_id_integer_value_falls_back_to_experiment_id(self):
1075+
# Non-string types are out of scope per spec Assumptions; the
1076+
# is_non_empty_string predicate returns False for them, so the
1077+
# fallback to experiment_id fires.
10711078
experiment = self._with_layer_id(111182)
10721079
snapshot = self._build_impression(experiment, '111129')
10731080
self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id)
@@ -1102,10 +1109,13 @@ def test_entity_id_empty_falls_back_to_experiment_id(self):
11021109
snapshot = self._build_impression(experiment, '111129')
11031110
self.assertEqual(self._event(snapshot)['entity_id'], experiment.id)
11041111

1105-
def test_entity_id_non_numeric_falls_back_to_experiment_id(self):
1106-
experiment = self._with_layer_id('abc')
1112+
def test_entity_id_opaque_layer_id_passes_through(self):
1113+
# Per FR-009: entity_id accepts any non-empty string (numeric or opaque
1114+
# like "layer_abc") and passes through unchanged. The fallback to
1115+
# experiment_id fires only when the value is empty, null, or missing.
1116+
experiment = self._with_layer_id('layer_abc')
11071117
snapshot = self._build_impression(experiment, '111129')
1108-
self.assertEqual(self._event(snapshot)['entity_id'], experiment.id)
1118+
self.assertEqual(self._event(snapshot)['entity_id'], 'layer_abc')
11091119

11101120
def test_entity_id_equals_campaign_id_when_layer_invalid(self):
11111121
experiment = self._with_layer_id('')

tests/test_event_factory.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,9 +1289,13 @@ def test_campaign_id_empty_layer_id_falls_back_to_experiment_id(self):
12891289
decision, _event, experiment = self._build_visitor('', '111129')
12901290
self.assertEqual(decision.campaign_id, experiment.id)
12911291

1292-
def test_campaign_id_non_numeric_layer_id_falls_back_to_experiment_id(self):
1293-
decision, _event, experiment = self._build_visitor('abc', '111129')
1294-
self.assertEqual(decision.campaign_id, experiment.id)
1292+
def test_campaign_id_opaque_layer_id_passes_through(self):
1293+
# Per FR-001: any non-empty string (numeric or opaque like
1294+
# "default-12345" / "layer_abc") is valid and passes through unchanged.
1295+
decision, snapshot_event, _exp = self._build_visitor('layer_abc', '111129')
1296+
self.assertEqual(decision.campaign_id, 'layer_abc')
1297+
# FR-009: entity_id MUST stay byte-equal to campaign_id even for opaque IDs.
1298+
self.assertEqual(snapshot_event.entity_id, 'layer_abc')
12951299

12961300
def test_variation_id_empty_becomes_none(self):
12971301
decision, _event, _exp = self._build_visitor('111182', '')

0 commit comments

Comments
 (0)