From 1af87dd18e83fff4fcd95f8f2032fba90c448424 Mon Sep 17 00:00:00 2001 From: phaym <1431593+phaym@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:29:11 -0400 Subject: [PATCH 1/6] update wasm for eval events, set flagMetadata for openfeature --- .../bucketing-lib.release.wasm | Bin 221800 -> 223167 bytes devcycle_python_sdk/local_client.py | 2 +- .../open_feature_provider/provider.py | 25 ++++++++++++++---- update_wasm_lib.sh | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/devcycle_python_sdk/bucketing-lib.release.wasm b/devcycle_python_sdk/bucketing-lib.release.wasm index 3e4843f09f8fc54cfb8f4d852f88f3c32a39d3e2..80994caaf48cfdb6c52d2eee9d4df25d479899fd 100644 GIT binary patch delta 48401 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 delta 47846 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 Variable try: self.event_queue_manager.queue_aggregate_event( event=DevCycleEvent( - type=EventType.AggVariableDefaulted, target=key, value=1 + type=EventType.AggVariableDefaulted, target=key, value=1, metaData={"evalReason": EvalReasons.DEFAULT} ), bucketed_config=None, ) diff --git a/devcycle_python_sdk/open_feature_provider/provider.py b/devcycle_python_sdk/open_feature_provider/provider.py index 8de69de..babe0f9 100644 --- a/devcycle_python_sdk/open_feature_provider/provider.py +++ b/devcycle_python_sdk/open_feature_provider/provider.py @@ -80,13 +80,28 @@ def _resolve( reason=Reason.DEFAULT, ) else: + # TODO: once eval enabled from cloud bucketing, eval reason won't be null unless defaulted + if variable.eval and variable.eval.reason: + reason = variable.eval.reason + elif variable.isDefaulted: + reason = Reason.DEFAULT + else: + reason = Reason.TARGETING_MATCH + + # Build flag metadata if eval details exist + flag_metadata = {} + if variable.eval: + if variable.eval.details: + flag_metadata["evalReasonDetails"] = variable.eval.details + if variable.eval.target_id: + flag_metadata["evalReasonTargetId"] = ( + variable.eval.target_id + ) + return FlagResolutionDetails( value=variable.value, - reason=( - Reason.DEFAULT - if variable.isDefaulted - else Reason.TARGETING_MATCH - ), + reason=reason, + flag_metadata=flag_metadata if flag_metadata else None, ) except ValueError as e: # occurs if the key or default value is None diff --git a/update_wasm_lib.sh b/update_wasm_lib.sh index 93b0472..1200e7a 100755 --- a/update_wasm_lib.sh +++ b/update_wasm_lib.sh @@ -1,6 +1,6 @@ #!/bin/bash -BUCKETING_LIB_VERSION="1.40.2" +BUCKETING_LIB_VERSION="1.41.0" if [[ -n "$1" ]]; then BUCKETING_LIB_VERSION="$1" From 90bddb6cfaf88ee4e392ffffa6734265f656d283 Mon Sep 17 00:00:00 2001 From: phaym <1431593+phaym@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:07:36 -0400 Subject: [PATCH 2/6] update tests for provider --- devcycle_python_sdk/local_client.py | 5 +- devcycle_python_sdk/models/variable.py | 16 ++++ .../open_feature_provider/provider.py | 12 +-- test/openfeature_test/test_provider.py | 82 ++++++++++++++++++- .../test_provider_local_sdk.py | 25 ++++++ 5 files changed, 126 insertions(+), 14 deletions(-) diff --git a/devcycle_python_sdk/local_client.py b/devcycle_python_sdk/local_client.py index cc68864..6f687ff 100644 --- a/devcycle_python_sdk/local_client.py +++ b/devcycle_python_sdk/local_client.py @@ -135,7 +135,10 @@ def variable(self, user: DevCycleUser, key: str, default_value: Any) -> Variable try: self.event_queue_manager.queue_aggregate_event( event=DevCycleEvent( - type=EventType.AggVariableDefaulted, target=key, value=1, metaData={"evalReason": EvalReasons.DEFAULT} + type=EventType.AggVariableDefaulted, + target=key, + value=1, + metaData={"evalReason": EvalReasons.DEFAULT}, ), bucketed_config=None, ) diff --git a/devcycle_python_sdk/models/variable.py b/devcycle_python_sdk/models/variable.py index 3bace9b..5928989 100644 --- a/devcycle_python_sdk/models/variable.py +++ b/devcycle_python_sdk/models/variable.py @@ -85,3 +85,19 @@ def create_default_variable( isDefaulted=True, eval=eval_reason, ) + + def get_flag_meta_data(self) -> dict: + """ + Returns metadata dictionary for OpenFeature flag resolution. + + Returns: + dict: Dictionary containing evalReasonDetails and evalReasonTargetId + if they exist, empty dict otherwise. + """ + meta_data = {} + if self.eval: + if self.eval.details: + meta_data["evalReasonDetails"] = self.eval.details + if self.eval.target_id: + meta_data["evalReasonTargetId"] = self.eval.target_id + return meta_data diff --git a/devcycle_python_sdk/open_feature_provider/provider.py b/devcycle_python_sdk/open_feature_provider/provider.py index babe0f9..aad750c 100644 --- a/devcycle_python_sdk/open_feature_provider/provider.py +++ b/devcycle_python_sdk/open_feature_provider/provider.py @@ -88,20 +88,10 @@ def _resolve( else: reason = Reason.TARGETING_MATCH - # Build flag metadata if eval details exist - flag_metadata = {} - if variable.eval: - if variable.eval.details: - flag_metadata["evalReasonDetails"] = variable.eval.details - if variable.eval.target_id: - flag_metadata["evalReasonTargetId"] = ( - variable.eval.target_id - ) - return FlagResolutionDetails( value=variable.value, reason=reason, - flag_metadata=flag_metadata if flag_metadata else None, + flag_metadata=variable.get_flag_meta_data(), ) except ValueError as e: # occurs if the key or default value is None diff --git a/test/openfeature_test/test_provider.py b/test/openfeature_test/test_provider.py index 0fb7a54..44d1031 100644 --- a/test/openfeature_test/test_provider.py +++ b/test/openfeature_test/test_provider.py @@ -12,6 +12,11 @@ ) from devcycle_python_sdk.models.variable import Variable, TypeEnum +from devcycle_python_sdk.models.eval_reason import ( + EvalReason, + EvalReasons, + DefaultReasonDetails, +) from devcycle_python_sdk.open_feature_provider.provider import ( DevCycleProvider, @@ -69,7 +74,9 @@ def test_resolve_details_client_returns_none(self): def test_resolve_details_client_returns_default_variable(self): self.client.variable.return_value = Variable.create_default_variable( - key="test-flag", default_value=False + key="test-flag", + default_value=False, + default_reason_detail=DefaultReasonDetails.USER_NOT_TARGETED, ) context = EvaluationContext(targeting_key="user-1234") details = self.provider._resolve("test-flag", False, context) @@ -77,6 +84,9 @@ def test_resolve_details_client_returns_default_variable(self): self.assertIsNotNone(details) self.assertEqual(details.value, False) self.assertEqual(details.reason, Reason.DEFAULT) + self.assertEqual( + details.flag_metadata["evalReasonDetails"], "User Not Targeted" + ) def test_resolve_boolean_details(self): key = "test-flag" @@ -90,6 +100,9 @@ def test_resolve_boolean_details(self): type=TypeEnum.BOOLEAN, isDefaulted=False, defaultValue=False, + eval=EvalReason( + reason="TARGETING_MATCH", details="All Users", target_id="targetId" + ), ) context = EvaluationContext(targeting_key="user-1234") @@ -98,6 +111,9 @@ def test_resolve_boolean_details(self): self.assertIsNotNone(details) self.assertEqual(details.value, variable_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId") def test_resolve_string_details(self): key = "test-flag" @@ -111,6 +127,9 @@ def test_resolve_string_details(self): type=TypeEnum.STRING, isDefaulted=False, defaultValue=False, + eval=EvalReason( + reason="TARGETING_MATCH", details="All Users", target_id="targetId" + ), ) context = EvaluationContext(targeting_key="user-1234") @@ -120,6 +139,8 @@ def test_resolve_string_details(self): self.assertEqual(details.value, variable_value) self.assertIsInstance(details.value, str) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId") def test_resolve_integer_details(self): key = "test-flag" @@ -133,6 +154,9 @@ def test_resolve_integer_details(self): type=TypeEnum.STRING, isDefaulted=False, defaultValue=False, + eval=EvalReason( + reason="TARGETING_MATCH", details="All Users", target_id="targetId" + ), ) context = EvaluationContext(targeting_key="user-1234") @@ -142,6 +166,8 @@ def test_resolve_integer_details(self): self.assertIsInstance(details.value, int) self.assertEqual(details.value, variable_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId") def test_resolve_float_details(self): key = "test-flag" @@ -155,6 +181,7 @@ def test_resolve_float_details(self): type=TypeEnum.STRING, isDefaulted=False, defaultValue=False, + eval=EvalReason(reason="SPLIT", details="Rollout", target_id="targetId"), ) context = EvaluationContext(targeting_key="user-1234") @@ -163,7 +190,9 @@ def test_resolve_float_details(self): self.assertIsNotNone(details) self.assertIsInstance(details.value, float) self.assertEqual(details.value, variable_value) - self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertEqual(details.reason, Reason.SPLIT) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "Rollout") + self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId") def test_resolve_object_details_verify_default_value(self): key = "test-flag" @@ -204,6 +233,9 @@ def test_resolve_object_details(self): type=TypeEnum.STRING, isDefaulted=False, defaultValue=False, + eval=EvalReason( + reason="TARGETING_MATCH", details="Rollout", target_id="targetId" + ), ) context = EvaluationContext(targeting_key="user-1234") @@ -213,6 +245,52 @@ def test_resolve_object_details(self): self.assertIsInstance(details.value, dict) self.assertDictEqual(details.value, variable_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertIsNotNone(details.flag_metadata) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "Rollout") + self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId") + + def test_resolve_string_details_null_eval(self): + key = "test-flag" + variable_value = "some string" + default_value = "default string" + + self.client.variable.return_value = Variable( + _id=None, + value=variable_value, + key=key, + type=TypeEnum.STRING, + isDefaulted=False, + defaultValue=False, + ) + + context = EvaluationContext(targeting_key="user-1234") + details = self.provider.resolve_string_details(key, default_value, context) + + self.assertIsNotNone(details) + self.assertEqual(details.value, variable_value) + self.assertIsInstance(details.value, str) + self.assertEqual(details.reason, Reason.TARGETING_MATCH) + + def test_default_string_details_null_eval(self): + key = "test-flag" + default_value = "default string" + + self.client.variable.return_value = Variable( + _id=None, + value=default_value, + key=key, + type=TypeEnum.STRING, + isDefaulted=True, + defaultValue=False, + ) + + context = EvaluationContext(targeting_key="user-1234") + details = self.provider.resolve_string_details(key, default_value, context) + + self.assertIsNotNone(details) + self.assertEqual(details.value, default_value) + self.assertIsInstance(details.value, str) + self.assertEqual(details.reason, Reason.DEFAULT) if __name__ == "__main__": diff --git a/test/openfeature_test/test_provider_local_sdk.py b/test/openfeature_test/test_provider_local_sdk.py index 04c3223..933f4b0 100644 --- a/test/openfeature_test/test_provider_local_sdk.py +++ b/test/openfeature_test/test_provider_local_sdk.py @@ -66,6 +66,11 @@ def test_resolve_boolean_details(self): self.assertIsNotNone(details) self.assertEqual(details.value, expected_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertIsNotNone(details.flag_metadata) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual( + details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc" + ) @responses.activate def test_resolve_integer_details(self): @@ -82,6 +87,11 @@ def test_resolve_integer_details(self): self.assertIsNotNone(details) self.assertEqual(details.value, expected_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertIsNotNone(details.flag_metadata) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual( + details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc" + ) @responses.activate def test_resolve_float_details(self): @@ -98,6 +108,11 @@ def test_resolve_float_details(self): self.assertIsNotNone(details) self.assertEqual(details.value, expected_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertIsNotNone(details.flag_metadata) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual( + details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc" + ) @responses.activate def test_resolve_string_details(self): @@ -114,6 +129,11 @@ def test_resolve_string_details(self): self.assertIsNotNone(details) self.assertEqual(details.value, expected_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertIsNotNone(details.flag_metadata) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual( + details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc" + ) @responses.activate def test_resolve_object_details(self): @@ -134,3 +154,8 @@ def test_resolve_object_details(self): self.assertIsNotNone(details) self.assertEqual(details.value, expected_value) self.assertEqual(details.reason, Reason.TARGETING_MATCH) + self.assertIsNotNone(details.flag_metadata) + self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users") + self.assertEqual( + details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc" + ) From a4d73e1a53d81f572250dd7e5d680ff5d6c379e9 Mon Sep 17 00:00:00 2001 From: phaym <1431593+phaym@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:08:51 -0400 Subject: [PATCH 3/6] fix lint --- test/openfeature_test/test_provider.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/openfeature_test/test_provider.py b/test/openfeature_test/test_provider.py index 44d1031..4277f07 100644 --- a/test/openfeature_test/test_provider.py +++ b/test/openfeature_test/test_provider.py @@ -14,7 +14,6 @@ from devcycle_python_sdk.models.variable import Variable, TypeEnum from devcycle_python_sdk.models.eval_reason import ( EvalReason, - EvalReasons, DefaultReasonDetails, ) From 61f9076bc862d63057f151d8b7245a68bd544375 Mon Sep 17 00:00:00 2001 From: phaym <1431593+phaym@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:12:21 -0400 Subject: [PATCH 4/6] update test-harness --- .github/workflows/run-test-harness.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-test-harness.yml b/.github/workflows/run-test-harness.yml index b2b1cb1..b6ddb8b 100644 --- a/.github/workflows/run-test-harness.yml +++ b/.github/workflows/run-test-harness.yml @@ -16,4 +16,4 @@ jobs: with: sdks-to-test: python sdk-github-sha: ${{github.event.pull_request.head.sha}} - sdk-capabilities: '["cloud", "edgeDB", "clientCustomData","v2Config", "allVariables", "allFeatures", "evalReason", "cloudEvalReason"]' + sdk-capabilities: '["cloud", "edgeDB", "clientCustomData","v2Config", "allVariables", "allFeatures", "evalReason", "cloudEvalReason", "eventsEvalReason"]' From be8812fb948b279e28b9ec6e12dc7c203d874709 Mon Sep 17 00:00:00 2001 From: phaym <1431593+phaym@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:13:53 -0400 Subject: [PATCH 5/6] update test-harness --- .github/workflows/run-test-harness.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-test-harness.yml b/.github/workflows/run-test-harness.yml index b6ddb8b..68e8fca 100644 --- a/.github/workflows/run-test-harness.yml +++ b/.github/workflows/run-test-harness.yml @@ -2,7 +2,7 @@ name: Run Test Harness on: pull_request: - branches: [main] + branches: [feat/eval-reasons, main] permissions: contents: read From 84d4b0b4e871481f083bb54d013e9be115da74f8 Mon Sep 17 00:00:00 2001 From: phaym <1431593+phaym@users.noreply.github.com> Date: Tue, 29 Jul 2025 16:19:49 -0400 Subject: [PATCH 6/6] revert testharness branches --- .github/workflows/run-test-harness.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-test-harness.yml b/.github/workflows/run-test-harness.yml index 68e8fca..b6ddb8b 100644 --- a/.github/workflows/run-test-harness.yml +++ b/.github/workflows/run-test-harness.yml @@ -2,7 +2,7 @@ name: Run Test Harness on: pull_request: - branches: [feat/eval-reasons, main] + branches: [main] permissions: contents: read