Skip to content

Commit d2afb34

Browse files
authored
fix: custom context properties in root are now respected during evaluation (#333)
1 parent 6dfcec2 commit d2afb34

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

UnleashClient/__init__.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@
3333
from .utils import LOGGER, InstanceAllowType, InstanceCounter
3434

3535
INSTANCES = InstanceCounter()
36+
_BASE_CONTEXT_FIELDS = [
37+
"userId",
38+
"sessionId",
39+
"environment",
40+
"appName",
41+
"currentTime",
42+
"remoteAddress",
43+
"properties",
44+
]
3645

3746

3847
# pylint: disable=dangerous-default-value
@@ -438,7 +447,7 @@ def _safe_context(self, context) -> dict:
438447
if "currentTime" not in new_context:
439448
new_context["currentTime"] = datetime.now(timezone.utc).isoformat()
440449

441-
safe_properties = new_context.get("properties", {})
450+
safe_properties = self._extract_properties(new_context)
442451
safe_properties = {
443452
k: self._safe_context_value(v) for k, v in safe_properties.items()
444453
}
@@ -452,6 +461,14 @@ def _safe_context(self, context) -> dict:
452461

453462
return safe_context
454463

464+
def _extract_properties(self, context: dict) -> dict:
465+
properties = context.get("properties", {})
466+
extracted_fields = {
467+
k: v for k, v in context.items() if k not in _BASE_CONTEXT_FIELDS
468+
}
469+
extracted_fields.update(properties)
470+
return extracted_fields
471+
455472
def _safe_context_value(self, value):
456473
if isinstance(value, datetime):
457474
return value.isoformat()

tests/unit_tests/test_client.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
MOCK_FEATURE_ENABLED_NO_VARIANTS_RESPONSE,
1515
MOCK_FEATURE_RESPONSE,
1616
MOCK_FEATURE_RESPONSE_PROJECT,
17+
MOCK_FEATURE_WITH_CUSTOM_CONTEXT_REQUIREMENTS,
1718
MOCK_FEATURE_WITH_DATE_AFTER_CONSTRAINT,
1819
MOCK_FEATURE_WITH_DEPENDENCIES_RESPONSE,
1920
MOCK_FEATURE_WITH_NUMERIC_CONSTRAINT,
@@ -976,3 +977,58 @@ def test_context_adds_current_time_if_not_set():
976977
)
977978

978979
assert unleash_client.is_enabled("DateConstraint")
980+
981+
982+
def test_context_moves_properties_fields_to_properties():
983+
unleash_client = UnleashClient(
984+
URL,
985+
APP_NAME,
986+
disable_metrics=True,
987+
disable_registration=True,
988+
)
989+
990+
context = {"myContext": "1234"}
991+
992+
assert "myContext" in unleash_client._safe_context(context)["properties"]
993+
994+
995+
def test_existing_properties_are_retained_when_custom_context_properties_are_in_the_root():
996+
unleash_client = UnleashClient(
997+
URL,
998+
APP_NAME,
999+
disable_metrics=True,
1000+
disable_registration=True,
1001+
)
1002+
1003+
context = {"myContext": "1234", "properties": {"yourContext": "1234"}}
1004+
1005+
assert "myContext" in unleash_client._safe_context(context)["properties"]
1006+
assert "yourContext" in unleash_client._safe_context(context)["properties"]
1007+
1008+
1009+
def test_base_context_properties_are_retained_in_root():
1010+
unleash_client = UnleashClient(
1011+
URL,
1012+
APP_NAME,
1013+
disable_metrics=True,
1014+
disable_registration=True,
1015+
)
1016+
1017+
context = {"userId": "1234"}
1018+
1019+
assert "userId" in unleash_client._safe_context(context)
1020+
1021+
1022+
def test_is_enabled_works_with_properties_field_in_the_context_root():
1023+
cache = FileCache("MOCK_CACHE")
1024+
cache.bootstrap_from_dict(MOCK_FEATURE_WITH_CUSTOM_CONTEXT_REQUIREMENTS)
1025+
unleash_client = UnleashClient(
1026+
URL,
1027+
APP_NAME,
1028+
disable_metrics=True,
1029+
cache=cache,
1030+
disable_registration=True,
1031+
)
1032+
1033+
context = {"myContext": "1234"}
1034+
assert unleash_client.is_enabled("customContextToggle", context)

tests/utilities/mocks/mock_features.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,30 @@
352352
},
353353
],
354354
}
355+
356+
MOCK_FEATURE_WITH_CUSTOM_CONTEXT_REQUIREMENTS = {
357+
"version": 1,
358+
"features": [
359+
{
360+
"name": "customContextToggle",
361+
"description": "Feature toggle with custom context constraint",
362+
"enabled": True,
363+
"strategies": [
364+
{
365+
"name": "default",
366+
"parameters": {},
367+
"constraints": [
368+
{
369+
"contextName": "myContext",
370+
"operator": "IN",
371+
"values": ["1234"],
372+
"inverted": False,
373+
}
374+
],
375+
}
376+
],
377+
"createdAt": "2018-10-09T06:04:05.667Z",
378+
"impressionData": False,
379+
},
380+
],
381+
}

0 commit comments

Comments
 (0)