devcycle-python-server-sdk 3.3.5__py3-none-any.whl → 3.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. devcycle_python_sdk/VERSION.txt +1 -1
  2. devcycle_python_sdk/__init__.py +1 -0
  3. devcycle_python_sdk/api/bucketing_client.py +2 -2
  4. devcycle_python_sdk/api/config_client.py +4 -2
  5. devcycle_python_sdk/api/event_client.py +2 -2
  6. devcycle_python_sdk/bucketing-lib.release.wasm +0 -0
  7. devcycle_python_sdk/cloud_client.py +19 -2
  8. devcycle_python_sdk/devcycle_client.py +40 -0
  9. devcycle_python_sdk/local_client.py +16 -2
  10. devcycle_python_sdk/models/bucketed_config.py +2 -0
  11. devcycle_python_sdk/models/user.py +86 -3
  12. devcycle_python_sdk/open_feature_provider/__init__.py +0 -0
  13. devcycle_python_sdk/open_feature_provider/provider.py +123 -0
  14. devcycle_python_sdk/util/strings.py +6 -0
  15. {devcycle_python_server_sdk-3.3.5.dist-info → devcycle_python_server_sdk-3.5.0.dist-info}/METADATA +2 -1
  16. {devcycle_python_server_sdk-3.3.5.dist-info → devcycle_python_server_sdk-3.5.0.dist-info}/RECORD +33 -23
  17. {devcycle_python_server_sdk-3.3.5.dist-info → devcycle_python_server_sdk-3.5.0.dist-info}/WHEEL +1 -1
  18. example/local_bucketing_client_example.py +1 -1
  19. example/openfeature_example.py +77 -0
  20. test/api/test_bucketing_client.py +5 -1
  21. test/api/test_config_client.py +5 -2
  22. test/api/test_event_client.py +3 -0
  23. test/api/test_local_bucketing.py +15 -12
  24. test/fixture/data.py +5 -5
  25. test/models/test_user.py +188 -0
  26. test/openfeature_test/__init__.py +0 -0
  27. test/openfeature_test/test_provider.py +219 -0
  28. test/openfeature_test/test_provider_local_sdk.py +136 -0
  29. test/test_local_client.py +10 -0
  30. test/util/test_strings.py +25 -0
  31. test/util/test_version.py +1 -1
  32. {devcycle_python_server_sdk-3.3.5.dist-info → devcycle_python_server_sdk-3.5.0.dist-info}/LICENSE +0 -0
  33. {devcycle_python_server_sdk-3.3.5.dist-info → devcycle_python_server_sdk-3.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,136 @@
1
+ import logging
2
+ import time
3
+ import unittest
4
+
5
+ import responses
6
+
7
+ from devcycle_python_sdk import DevCycleLocalClient, DevCycleLocalOptions
8
+ from test.fixture.data import small_config_json
9
+
10
+ from openfeature.provider import EvaluationContext
11
+ from openfeature.flag_evaluation import Reason
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
17
+ """
18
+ Tests the DevCycleProvider with actual data from the Local SDK client to confirm data translations work
19
+ """
20
+
21
+ def setUp(self) -> None:
22
+ self.sdk_key = "dvc_server_949e4962-c624-4d20-a1ea-7f2501b2ba79"
23
+ self.test_config_json = small_config_json()
24
+ self.test_etag = "2f71454e-3279-4ca7-a8e7-802ce97bef43"
25
+
26
+ config_url = "http://localhost/config/v1/server/" + self.sdk_key + ".json"
27
+
28
+ responses.add(
29
+ responses.GET,
30
+ config_url,
31
+ headers={"ETag": self.test_etag},
32
+ json=self.test_config_json,
33
+ status=200,
34
+ )
35
+
36
+ self.options = DevCycleLocalOptions(
37
+ config_polling_interval_ms=5000,
38
+ config_cdn_uri="http://localhost/",
39
+ disable_custom_event_logging=True,
40
+ disable_automatic_event_logging=True,
41
+ )
42
+ self.client = None
43
+
44
+ def tearDown(self) -> None:
45
+ if self.client:
46
+ self.client.close()
47
+ responses.reset()
48
+
49
+ def setup_client(self):
50
+ self.client = DevCycleLocalClient(self.sdk_key, self.options)
51
+ while not self.client.is_initialized():
52
+ time.sleep(0.05)
53
+
54
+ @responses.activate
55
+ def test_resolve_boolean_details(self):
56
+ self.setup_client()
57
+ provider = self.client.get_openfeature_provider()
58
+
59
+ key = "a-cool-new-feature"
60
+ expected_value = True
61
+ default_value = False
62
+
63
+ context = EvaluationContext(targeting_key="1234")
64
+ details = provider.resolve_boolean_details(key, default_value, context)
65
+
66
+ self.assertIsNotNone(details)
67
+ self.assertEqual(details.value, expected_value)
68
+ self.assertEqual(details.reason, Reason.TARGETING_MATCH)
69
+
70
+ @responses.activate
71
+ def test_resolve_integer_details(self):
72
+ self.setup_client()
73
+ provider = self.client.get_openfeature_provider()
74
+
75
+ key = "num-var"
76
+ expected_value = 12345
77
+ default_value = 0
78
+
79
+ context = EvaluationContext(targeting_key="1234")
80
+ details = provider.resolve_integer_details(key, default_value, context)
81
+
82
+ self.assertIsNotNone(details)
83
+ self.assertEqual(details.value, expected_value)
84
+ self.assertEqual(details.reason, Reason.TARGETING_MATCH)
85
+
86
+ @responses.activate
87
+ def test_resolve_float_details(self):
88
+ self.setup_client()
89
+ provider = self.client.get_openfeature_provider()
90
+
91
+ key = "float-var"
92
+ expected_value = 3.14159
93
+ default_value = 0.0001
94
+
95
+ context = EvaluationContext(targeting_key="1234")
96
+ details = provider.resolve_float_details(key, default_value, context)
97
+
98
+ self.assertIsNotNone(details)
99
+ self.assertEqual(details.value, expected_value)
100
+ self.assertEqual(details.reason, Reason.TARGETING_MATCH)
101
+
102
+ @responses.activate
103
+ def test_resolve_string_details(self):
104
+ self.setup_client()
105
+ provider = self.client.get_openfeature_provider()
106
+
107
+ key = "string-var"
108
+ expected_value = "variationOn"
109
+ default_value = "default_value"
110
+
111
+ context = EvaluationContext(targeting_key="1234")
112
+ details = provider.resolve_string_details(key, default_value, context)
113
+
114
+ self.assertIsNotNone(details)
115
+ self.assertEqual(details.value, expected_value)
116
+ self.assertEqual(details.reason, Reason.TARGETING_MATCH)
117
+
118
+ @responses.activate
119
+ def test_resolve_object_details(self):
120
+ self.setup_client()
121
+ provider = self.client.get_openfeature_provider()
122
+
123
+ key = "json-var"
124
+ expected_value = {
125
+ "displayText": "This variation is on",
126
+ "showDialog": True,
127
+ "maxUsers": 100,
128
+ }
129
+ default_value = {"default": "value"}
130
+
131
+ context = EvaluationContext(targeting_key="1234")
132
+ details = provider.resolve_string_details(key, default_value, context)
133
+
134
+ self.assertIsNotNone(details)
135
+ self.assertEqual(details.value, expected_value)
136
+ self.assertEqual(details.reason, Reason.TARGETING_MATCH)
test/test_local_client.py CHANGED
@@ -217,6 +217,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
217
217
  ("string-var", "default_value", "variationOn", TypeEnum.STRING),
218
218
  ("a-cool-new-feature", False, True, TypeEnum.BOOLEAN),
219
219
  ("num-var", 0, 12345, TypeEnum.NUMBER),
220
+ ("float-var", 0, 3.14159, TypeEnum.NUMBER),
220
221
  (
221
222
  "json-var",
222
223
  {"default": "value"},
@@ -333,6 +334,15 @@ class DevCycleLocalClientTest(unittest.TestCase):
333
334
  defaultValue=None,
334
335
  evalReason=None,
335
336
  ),
337
+ "float-var": Variable(
338
+ _id="61200363125123fca69d3a7a",
339
+ key="float-var",
340
+ type="Number",
341
+ value=3.14159,
342
+ isDefaulted=None,
343
+ defaultValue=None,
344
+ evalReason=None,
345
+ ),
336
346
  }
337
347
  self.assertEqual(result, expected_variables)
338
348
 
@@ -0,0 +1,25 @@
1
+ import logging
2
+
3
+ import unittest
4
+
5
+ from devcycle_python_sdk.util.strings import slash_join
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class StringsUtilTest(unittest.TestCase):
11
+ def test_slash_join_no_args(self):
12
+ result = slash_join()
13
+ self.assertEqual(result, "")
14
+
15
+ def test_slash_join_url_no_slashes(self):
16
+ result = slash_join("http://example.com", "hello", "world")
17
+ self.assertEqual(result, "http://example.com/hello/world")
18
+
19
+ def test_slash_join_url_components_with_slashes(self):
20
+ result = slash_join("http://example.com", "/hello", "world/")
21
+ self.assertEqual(result, "http://example.com/hello/world")
22
+
23
+ def test_slash_join_url_components_with_numbers(self):
24
+ result = slash_join("http://example.com", "v1", "variable", 1234)
25
+ self.assertEqual(result, "http://example.com/v1/variable/1234")
test/util/test_version.py CHANGED
@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
10
10
  class VersionTest(unittest.TestCase):
11
11
  def test_sdk_version(self):
12
12
  version = sdk_version()
13
- self.assertRegexpMatches(version, r"^\d+\.\d+\.\d+$")
13
+ self.assertRegex(version, r"^\d+\.\d+\.\d+$")
14
14
 
15
15
 
16
16
  if __name__ == "__main__":