devcycle-python-server-sdk 2.0.1__py3-none-any.whl → 3.0.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.
- devcycle_python_sdk/VERSION.txt +1 -1
- devcycle_python_sdk/__init__.py +3 -3
- devcycle_python_sdk/api/backoff.py +11 -0
- devcycle_python_sdk/api/bucketing_client.py +17 -25
- devcycle_python_sdk/api/config_client.py +116 -0
- devcycle_python_sdk/api/event_client.py +95 -0
- devcycle_python_sdk/api/local_bucketing.py +429 -2
- devcycle_python_sdk/{dvc_cloud_client.py → cloud_client.py} +69 -41
- devcycle_python_sdk/exceptions.py +25 -3
- devcycle_python_sdk/local_client.py +220 -0
- devcycle_python_sdk/managers/config_manager.py +83 -15
- devcycle_python_sdk/managers/event_queue_manager.py +213 -8
- devcycle_python_sdk/models/bucketed_config.py +150 -0
- devcycle_python_sdk/models/event.py +111 -8
- devcycle_python_sdk/models/feature.py +12 -0
- devcycle_python_sdk/models/platform_data.py +33 -0
- devcycle_python_sdk/models/user.py +52 -7
- devcycle_python_sdk/models/variable.py +17 -5
- devcycle_python_sdk/options.py +106 -0
- devcycle_python_sdk/protobuf/__init__.py +0 -0
- devcycle_python_sdk/protobuf/utils.py +129 -0
- devcycle_python_sdk/protobuf/variableForUserParams_pb2.py +46 -0
- devcycle_python_sdk/util/version.py +1 -1
- {devcycle_python_server_sdk-2.0.1.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/METADATA +3 -1
- devcycle_python_server_sdk-3.0.0.dist-info/RECORD +55 -0
- example/{example.py → cloud_client_example.py} +24 -20
- example/local_bucketing_client_example.py +74 -0
- test/api/test_bucketing_client.py +10 -5
- test/api/test_config_client.py +133 -0
- test/api/test_event_client.py +97 -0
- test/api/test_local_bucketing.py +379 -4
- test/fixture/__init__.py +0 -0
- test/fixture/data.py +57 -0
- test/managers/test_config_manager.py +120 -5
- test/managers/test_event_queue_manager.py +259 -5
- test/models/__init__.py +0 -0
- test/models/test_bucketed_config.py +26 -0
- test/{test_dvc_cloud_client.py → test_cloud_client.py} +11 -17
- test/test_local_client.py +397 -0
- test/util/test_utils.py +183 -0
- test/util/test_version.py +2 -2
- devcycle_python_sdk/dvc_local_client.py +0 -152
- devcycle_python_sdk/dvc_options.py +0 -45
- devcycle_python_server_sdk-2.0.1.dist-info/RECORD +0 -41
- test/test_bucketing_client.py +0 -228
- test/test_dvc_local_client.py +0 -143
- test/test_version.py +0 -18
- {devcycle_python_server_sdk-2.0.1.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/LICENSE +0 -0
- {devcycle_python_server_sdk-2.0.1.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/WHEEL +0 -0
- {devcycle_python_server_sdk-2.0.1.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import platform
|
|
3
|
-
from typing import Any, Dict
|
|
4
|
-
|
|
5
|
-
from devcycle_python_sdk import DevCycleLocalOptions
|
|
6
|
-
from devcycle_python_sdk.api.local_bucketing import LocalBucketing
|
|
7
|
-
from devcycle_python_sdk.managers.config_manager import EnvironmentConfigManager
|
|
8
|
-
from devcycle_python_sdk.managers.event_queue_manager import EventQueueManager
|
|
9
|
-
from devcycle_python_sdk.models.event import Event
|
|
10
|
-
from devcycle_python_sdk.models.feature import Feature
|
|
11
|
-
from devcycle_python_sdk.models.user import User
|
|
12
|
-
from devcycle_python_sdk.models.variable import Variable
|
|
13
|
-
from devcycle_python_sdk.util.version import sdk_version
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class DevCycleLocalClient:
|
|
19
|
-
def __init__(self, sdk_key: str, options: DevCycleLocalOptions):
|
|
20
|
-
self._validate_sdk_key(sdk_key)
|
|
21
|
-
|
|
22
|
-
if options is None:
|
|
23
|
-
self.options = DevCycleLocalOptions()
|
|
24
|
-
else:
|
|
25
|
-
self.options = options
|
|
26
|
-
|
|
27
|
-
self.platform = "Python"
|
|
28
|
-
self.platform_version = platform.python_version()
|
|
29
|
-
self.sdk_version = sdk_version()
|
|
30
|
-
self.sdk_type = "local"
|
|
31
|
-
|
|
32
|
-
self.local_bucketing = LocalBucketing()
|
|
33
|
-
self.config_manager = EnvironmentConfigManager(sdk_key, self.options, self.local_bucketing)
|
|
34
|
-
self.event_queue_manager = EventQueueManager(sdk_key, self.options, self.local_bucketing)
|
|
35
|
-
|
|
36
|
-
def _add_platform_data_to_user(self, user: User) -> User:
|
|
37
|
-
user.platform = self.platform
|
|
38
|
-
user.platformVersion = self.platform_version
|
|
39
|
-
user.sdkVersion = self.sdk_version
|
|
40
|
-
user.sdkType = self.sdk_type
|
|
41
|
-
return user
|
|
42
|
-
|
|
43
|
-
def _validate_sdk_key(self, sdk_key: str) -> None:
|
|
44
|
-
if sdk_key is None or len(sdk_key) == 0:
|
|
45
|
-
raise ValueError("Missing SDK key! Call build with a valid server SDK key")
|
|
46
|
-
|
|
47
|
-
if not sdk_key.startswith("server") and not sdk_key.startswith("dvc_server"):
|
|
48
|
-
raise ValueError(
|
|
49
|
-
"Invalid SDK key provided. Call build with a valid server SDK key"
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
def _validate_user(self, user: User) -> None:
|
|
53
|
-
if user is None:
|
|
54
|
-
raise ValueError("User cannot be None")
|
|
55
|
-
|
|
56
|
-
if user.user_id is None or len(user.user_id) == 0:
|
|
57
|
-
raise ValueError("userId cannot be empty")
|
|
58
|
-
|
|
59
|
-
def _is_initialized(self) -> bool:
|
|
60
|
-
return self.config_manager and self.config_manager.is_initialized()
|
|
61
|
-
|
|
62
|
-
def set_client_custom_data(self, custom_data: Dict[str, Any]) -> None:
|
|
63
|
-
if not self._is_initialized():
|
|
64
|
-
logger.debug("set_client_custom_data called before client has initialized")
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
# TODO delegate to local bucketing api
|
|
68
|
-
pass
|
|
69
|
-
|
|
70
|
-
def variable_value(self, user: User, key: str, default_value: Any) -> Any:
|
|
71
|
-
return self.variable(user, key, default_value).value
|
|
72
|
-
|
|
73
|
-
def variable(self, user: User, key: str, default_value: Any) -> Variable:
|
|
74
|
-
self._validate_user(user)
|
|
75
|
-
user = self._add_platform_data_to_user(user)
|
|
76
|
-
|
|
77
|
-
if not key:
|
|
78
|
-
raise ValueError("Missing parameter: key")
|
|
79
|
-
|
|
80
|
-
if default_value is None:
|
|
81
|
-
raise ValueError("Missing parameter: defaultValue")
|
|
82
|
-
|
|
83
|
-
if not self._is_initialized():
|
|
84
|
-
logger.debug("variable called before client has initialized")
|
|
85
|
-
# TODO track aggregate event for default variable
|
|
86
|
-
return Variable.create_default_variable(key, default_value)
|
|
87
|
-
|
|
88
|
-
variable = Variable(key, default_value)
|
|
89
|
-
|
|
90
|
-
# TODO delegate to local bucketing api
|
|
91
|
-
|
|
92
|
-
return variable
|
|
93
|
-
|
|
94
|
-
def all_variables(self, user: User) -> Dict[str, Variable]:
|
|
95
|
-
self._validate_user(user)
|
|
96
|
-
user = self._add_platform_data_to_user(user)
|
|
97
|
-
|
|
98
|
-
if not self._is_initialized():
|
|
99
|
-
logger.debug("all_variables called before client has initialized")
|
|
100
|
-
return {}
|
|
101
|
-
|
|
102
|
-
variable_map: Dict[str, Variable] = {}
|
|
103
|
-
|
|
104
|
-
try:
|
|
105
|
-
# TODO delegate to local bucketing api
|
|
106
|
-
pass
|
|
107
|
-
except Exception as e:
|
|
108
|
-
logger.error("Error retrieving all features for a user: %s", e)
|
|
109
|
-
|
|
110
|
-
return variable_map
|
|
111
|
-
|
|
112
|
-
def all_features(self, user: User) -> Dict[str, Feature]:
|
|
113
|
-
self._validate_user(user)
|
|
114
|
-
user = self._add_platform_data_to_user(user)
|
|
115
|
-
|
|
116
|
-
if not self._is_initialized():
|
|
117
|
-
logger.debug("all_features called before client has initialized")
|
|
118
|
-
return {}
|
|
119
|
-
|
|
120
|
-
feature_map: Dict[str, Feature] = {}
|
|
121
|
-
try:
|
|
122
|
-
# TODO delegate to local bucketing api
|
|
123
|
-
pass
|
|
124
|
-
except Exception as e:
|
|
125
|
-
logger.error("Error retrieving all features for a user: %s", e)
|
|
126
|
-
|
|
127
|
-
return feature_map
|
|
128
|
-
|
|
129
|
-
def track(self, user: User, user_event: Event) -> None:
|
|
130
|
-
self._validate_user(user)
|
|
131
|
-
user = self._add_platform_data_to_user(user)
|
|
132
|
-
|
|
133
|
-
if user_event is None or not user_event.type:
|
|
134
|
-
raise ValueError("Invalid Event")
|
|
135
|
-
|
|
136
|
-
if not self._is_initialized():
|
|
137
|
-
logger.debug("track called before client has initialized")
|
|
138
|
-
return
|
|
139
|
-
|
|
140
|
-
# events = [user_event]
|
|
141
|
-
try:
|
|
142
|
-
# TODO delegate to local bucketing api
|
|
143
|
-
pass
|
|
144
|
-
except Exception as e:
|
|
145
|
-
logger.error("Error tracking event: %s", e)
|
|
146
|
-
|
|
147
|
-
def close(self) -> None:
|
|
148
|
-
if self.config_manager:
|
|
149
|
-
self.config_manager.close()
|
|
150
|
-
|
|
151
|
-
if self.event_queue_manager:
|
|
152
|
-
self.event_queue_manager.close()
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
class DevCycleCloudOptions:
|
|
2
|
-
def __init__(
|
|
3
|
-
self,
|
|
4
|
-
enable_edge_db: bool = False,
|
|
5
|
-
events_api_uri: str = "https://events.devcycle.com/",
|
|
6
|
-
config_cdn_uri: str = "https://config-cdn.devcycle.com/",
|
|
7
|
-
bucketing_api_uri: str = "https://bucketing-api.devcycle.com/",
|
|
8
|
-
request_timeout: int = 5, # seconds
|
|
9
|
-
request_retries: int = 5,
|
|
10
|
-
retry_delay: int = 200, # milliseconds
|
|
11
|
-
):
|
|
12
|
-
self.enable_edge_db = enable_edge_db
|
|
13
|
-
self.events_API_URI = events_api_uri
|
|
14
|
-
self.config_CDN_URI = config_cdn_uri
|
|
15
|
-
self.bucketing_API_URI = bucketing_api_uri
|
|
16
|
-
self.request_timeout = request_timeout
|
|
17
|
-
self.request_retries = request_retries
|
|
18
|
-
self.retry_delay = retry_delay
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class DevCycleLocalOptions:
|
|
22
|
-
|
|
23
|
-
def __init__(
|
|
24
|
-
self,
|
|
25
|
-
events_api_uri: str = "https://events.devcycle.com/",
|
|
26
|
-
config_cdn_uri: str = "https://config-cdn.devcycle.com/",
|
|
27
|
-
config_request_timeout_ms: int = 5000,
|
|
28
|
-
config_polling_interval_ms: int = 1000,
|
|
29
|
-
max_event_queue_size: int = 2000,
|
|
30
|
-
event_flush_interval_ms: int = 10000,
|
|
31
|
-
flush_event_queue_size: int = 1000,
|
|
32
|
-
event_request_chunk_size: int = 100,
|
|
33
|
-
disable_automatic_event_logging: bool = False,
|
|
34
|
-
disable_custom_event_logging: bool = False,
|
|
35
|
-
):
|
|
36
|
-
self.events_API_URI = events_api_uri
|
|
37
|
-
self.config_CDN_URI = config_cdn_uri
|
|
38
|
-
self.config_request_timeout_ms = config_request_timeout_ms
|
|
39
|
-
self.config_polling_interval_ms = config_polling_interval_ms
|
|
40
|
-
self.max_event_queue_size = max_event_queue_size
|
|
41
|
-
self.event_flush_interval_ms = event_flush_interval_ms
|
|
42
|
-
self.flush_event_queue_size = flush_event_queue_size
|
|
43
|
-
self.event_request_chunk_size = event_request_chunk_size
|
|
44
|
-
self.disable_automatic_event_logging = disable_automatic_event_logging
|
|
45
|
-
self.disable_custom_event_logging = disable_custom_event_logging
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
devcycle_python_sdk/VERSION.txt,sha256=A0Z0ibj7YgWYiwpSn5RFtnqM9RBJvuo7KuHdxyWz0lQ,5
|
|
2
|
-
devcycle_python_sdk/__init__.py,sha256=cZLyd8QsDjy_aD4rwPMAuMXIgRJYenlJ19gqnEKsIYg,277
|
|
3
|
-
devcycle_python_sdk/dvc_cloud_client.py,sha256=-L0J4OvnrN7SqnhP2fGkV-5xkCC9kSfpjE7TYAJ6NlQ,5637
|
|
4
|
-
devcycle_python_sdk/dvc_local_client.py,sha256=jZ2daxD2IZV_JJl22CyKZxDKC1fqJ1HkIMLD6OLPHOY,5357
|
|
5
|
-
devcycle_python_sdk/dvc_options.py,sha256=V1Dr7aTowY1FsQNo9QOaQiGppzb6zZioaJlSsL0Bj84,2028
|
|
6
|
-
devcycle_python_sdk/exceptions.py,sha256=MZWIzo4qNCYzu1dot_rkM4Z9PxoIwOKuEHOu_ApfP4k,522
|
|
7
|
-
devcycle_python_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
devcycle_python_sdk/api/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
-
devcycle_python_sdk/api/bucketing_client.py,sha256=OBNTgzHLniZGgBB4B_h6lrpTqSv0W6jRoaiU0_qZibo,5186
|
|
10
|
-
devcycle_python_sdk/api/local_bucketing.py,sha256=v-IauuGG6CYQJmkLX21lKEXDkrwysqXVryYbm6Hmu-k,115
|
|
11
|
-
devcycle_python_sdk/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
devcycle_python_sdk/managers/config_manager.py,sha256=7aeJenxzP4RuNxr17mFipD2gelaYJnKNYAcIyv7TyMc,720
|
|
13
|
-
devcycle_python_sdk/managers/event_queue_manager.py,sha256=Gq8Wnp-EtGkor66AlEK1T95hgXj1n6w4DLvhw-rOAc0,495
|
|
14
|
-
devcycle_python_sdk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
devcycle_python_sdk/models/error_response.py,sha256=r_dRUDAbral9UGaMYcnBsZOr9Hmkyi1SQbhGmZilgpA,442
|
|
16
|
-
devcycle_python_sdk/models/event.py,sha256=T1m6qL5luBMHwy-B_DX-YbIo5RSVn0vNGCFV5VOR3_w,551
|
|
17
|
-
devcycle_python_sdk/models/feature.py,sha256=poF_Pqiya_nZhrLzD9p_jY4Rz7aWAXm3173q2zzvLNI,480
|
|
18
|
-
devcycle_python_sdk/models/user.py,sha256=XIcEd4Vx-NOBe1kvv-efdX_tsMn0D3ROQmrEeN-RWa8,1002
|
|
19
|
-
devcycle_python_sdk/models/variable.py,sha256=5_OUsWLakAgNIc0ivnG57Ayi6KosD0qwiyEFmEhaw-0,1380
|
|
20
|
-
devcycle_python_sdk/util/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
21
|
-
devcycle_python_sdk/util/version.py,sha256=SvlG34bMCIlUdCX9ti8avuUDB4EZzwVUllqkIHrFOQ8,253
|
|
22
|
-
example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
example/example.py,sha256=K9Bqt_raIJ-IM4iGDwV53gel-DRoRTyKuAnVDPQU3Mw,2289
|
|
24
|
-
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
test/test_bucketing_client.py,sha256=S2fZDvXFk40nP26S-kA7jlZChmSMzrQNjIaxAjOhpU8,7285
|
|
26
|
-
test/test_dvc_cloud_client.py,sha256=EmRyjvTVXYCjLpvUixrLV6mQ2FYmvc1jZ5frM9UqoU4,11559
|
|
27
|
-
test/test_dvc_local_client.py,sha256=lmn79odSCn1PTuMVRFRHq_gAmtbfNjvE81r4f7DQGxE,4806
|
|
28
|
-
test/test_version.py,sha256=4Jd7wAW250kIgruzl0LRlyWEAr1zPlb0Jo96OQcu9r8,368
|
|
29
|
-
test/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
test/api/test_bucketing_client.py,sha256=S2fZDvXFk40nP26S-kA7jlZChmSMzrQNjIaxAjOhpU8,7285
|
|
31
|
-
test/api/test_local_bucketing.py,sha256=nFBCQ2lyORp7llCxVaGu-ctP-R4J8lsF3siOlE5U_dU,348
|
|
32
|
-
test/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
test/managers/test_config_manager.py,sha256=AdRA32xTubNle9_gZvXs96MU-uHe7JldEW7mJ_xBwGU,396
|
|
34
|
-
test/managers/test_event_queue_manager.py,sha256=vFb9Ws0vIv3UVMeFSsYqujEC6_ehyYp1mnx2oSXb8cw,372
|
|
35
|
-
test/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
test/util/test_version.py,sha256=Z-Kd5KRgn6oRcZQ-9NHl5VfU3gOXC6E-V02vRK0HzaI,368
|
|
37
|
-
devcycle_python_server_sdk-2.0.1.dist-info/LICENSE,sha256=tHQMqu-Jic2UpMGqb-vNFbGygysl8IG4l1clnnh0OFo,1071
|
|
38
|
-
devcycle_python_server_sdk-2.0.1.dist-info/METADATA,sha256=1NqQAarq1TPdStTpjUF98DxUYP9-q1-amK2TpW7CY4A,383
|
|
39
|
-
devcycle_python_server_sdk-2.0.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
40
|
-
devcycle_python_server_sdk-2.0.1.dist-info/top_level.txt,sha256=34f8zDzihAHoH7siAFlmscyO_QLNWWq9HApFlwtYL4k,33
|
|
41
|
-
devcycle_python_server_sdk-2.0.1.dist-info/RECORD,,
|
test/test_bucketing_client.py
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import requests
|
|
3
|
-
import responses
|
|
4
|
-
from responses.registries import OrderedRegistry
|
|
5
|
-
import unittest
|
|
6
|
-
import uuid
|
|
7
|
-
|
|
8
|
-
from devcycle_python_sdk.api.bucketing_client import BucketingAPIClient
|
|
9
|
-
from devcycle_python_sdk.exceptions import (
|
|
10
|
-
CloudClientError,
|
|
11
|
-
CloudClientUnauthorizedError,
|
|
12
|
-
NotFoundError,
|
|
13
|
-
)
|
|
14
|
-
from devcycle_python_sdk.dvc_options import DevCycleCloudOptions
|
|
15
|
-
from devcycle_python_sdk.models.event import Event
|
|
16
|
-
from devcycle_python_sdk.models.feature import Feature
|
|
17
|
-
from devcycle_python_sdk.models.user import User
|
|
18
|
-
from devcycle_python_sdk.models.variable import Variable
|
|
19
|
-
|
|
20
|
-
logger = logging.getLogger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class BucketingClientTest(unittest.TestCase):
|
|
24
|
-
def setUp(self) -> None:
|
|
25
|
-
sdk_key = "dvc_server_" + str(uuid.uuid4())
|
|
26
|
-
options = DevCycleCloudOptions(retry_delay=0)
|
|
27
|
-
self.test_client = BucketingAPIClient(sdk_key, options)
|
|
28
|
-
self.test_user = User(user_id="test_user_id")
|
|
29
|
-
|
|
30
|
-
@responses.activate
|
|
31
|
-
def test_variable(self):
|
|
32
|
-
responses.add(
|
|
33
|
-
responses.POST,
|
|
34
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
35
|
-
json={
|
|
36
|
-
"_id": "variable_id",
|
|
37
|
-
"key": "variable-key",
|
|
38
|
-
"type": "variable-type",
|
|
39
|
-
"value": "hello world",
|
|
40
|
-
},
|
|
41
|
-
)
|
|
42
|
-
result = self.test_client.variable("variable-key", self.test_user)
|
|
43
|
-
|
|
44
|
-
self.assertEqual(
|
|
45
|
-
result,
|
|
46
|
-
Variable(
|
|
47
|
-
_id="variable_id",
|
|
48
|
-
key="variable-key",
|
|
49
|
-
type="variable-type",
|
|
50
|
-
value="hello world",
|
|
51
|
-
),
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
@responses.activate(registry=OrderedRegistry)
|
|
55
|
-
def test_variable_retries(self):
|
|
56
|
-
for i in range(self.test_client.options.request_retries):
|
|
57
|
-
responses.add(
|
|
58
|
-
responses.POST,
|
|
59
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
60
|
-
status=500,
|
|
61
|
-
)
|
|
62
|
-
responses.add(
|
|
63
|
-
responses.POST,
|
|
64
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
65
|
-
json={
|
|
66
|
-
"_id": "variable_id",
|
|
67
|
-
"key": "variable-key",
|
|
68
|
-
"type": "variable-type",
|
|
69
|
-
"value": "hello world",
|
|
70
|
-
},
|
|
71
|
-
)
|
|
72
|
-
result = self.test_client.variable("variable-key", self.test_user)
|
|
73
|
-
self.assertEqual(
|
|
74
|
-
result,
|
|
75
|
-
Variable(
|
|
76
|
-
_id="variable_id",
|
|
77
|
-
key="variable-key",
|
|
78
|
-
type="variable-type",
|
|
79
|
-
value="hello world",
|
|
80
|
-
),
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
@responses.activate(registry=OrderedRegistry)
|
|
84
|
-
def test_variable_retries_network_error(self):
|
|
85
|
-
responses.add(
|
|
86
|
-
responses.POST,
|
|
87
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
88
|
-
body=requests.exceptions.ConnectionError("Network Error"),
|
|
89
|
-
)
|
|
90
|
-
responses.add(
|
|
91
|
-
responses.POST,
|
|
92
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
93
|
-
json={
|
|
94
|
-
"_id": "variable_id",
|
|
95
|
-
"key": "variable-key",
|
|
96
|
-
"type": "variable-type",
|
|
97
|
-
"value": "hello world",
|
|
98
|
-
},
|
|
99
|
-
)
|
|
100
|
-
result = self.test_client.variable("variable-key", self.test_user)
|
|
101
|
-
self.assertEqual(
|
|
102
|
-
result,
|
|
103
|
-
Variable(
|
|
104
|
-
_id="variable_id",
|
|
105
|
-
key="variable-key",
|
|
106
|
-
type="variable-type",
|
|
107
|
-
value="hello world",
|
|
108
|
-
),
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
@responses.activate
|
|
112
|
-
def test_variable_retries_exceeded(self):
|
|
113
|
-
for i in range(self.test_client.options.request_retries + 1):
|
|
114
|
-
responses.add(
|
|
115
|
-
responses.POST,
|
|
116
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
117
|
-
status=500,
|
|
118
|
-
)
|
|
119
|
-
with self.assertRaises(CloudClientError):
|
|
120
|
-
self.test_client.variable("variable-key", self.test_user)
|
|
121
|
-
|
|
122
|
-
@responses.activate
|
|
123
|
-
def test_variable_unauthorized(self):
|
|
124
|
-
for i in range(2):
|
|
125
|
-
responses.add(
|
|
126
|
-
responses.POST,
|
|
127
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
128
|
-
status=401,
|
|
129
|
-
)
|
|
130
|
-
with self.assertRaises(CloudClientUnauthorizedError):
|
|
131
|
-
self.test_client.variable("variable-key", self.test_user)
|
|
132
|
-
|
|
133
|
-
@responses.activate
|
|
134
|
-
def test_variable_not_found(self):
|
|
135
|
-
for i in range(2):
|
|
136
|
-
responses.add(
|
|
137
|
-
responses.POST,
|
|
138
|
-
"https://bucketing-api.devcycle.com/v1/variables/variable-key",
|
|
139
|
-
status=404,
|
|
140
|
-
)
|
|
141
|
-
with self.assertRaises(NotFoundError):
|
|
142
|
-
self.test_client.variable("variable-key", self.test_user)
|
|
143
|
-
|
|
144
|
-
@responses.activate
|
|
145
|
-
def test_variables(self):
|
|
146
|
-
responses.add(
|
|
147
|
-
responses.POST,
|
|
148
|
-
"https://bucketing-api.devcycle.com/v1/variables",
|
|
149
|
-
json={
|
|
150
|
-
"variable-1": {
|
|
151
|
-
"_id": "variable_id",
|
|
152
|
-
"key": "variable-key",
|
|
153
|
-
"type": "variable-type",
|
|
154
|
-
"value": "hello world",
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
)
|
|
158
|
-
result = self.test_client.variables(self.test_user)
|
|
159
|
-
|
|
160
|
-
self.assertEqual(
|
|
161
|
-
result,
|
|
162
|
-
{
|
|
163
|
-
"variable-1": Variable(
|
|
164
|
-
_id="variable_id",
|
|
165
|
-
key="variable-key",
|
|
166
|
-
type="variable-type",
|
|
167
|
-
value="hello world",
|
|
168
|
-
isDefaulted=None,
|
|
169
|
-
)
|
|
170
|
-
},
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
@responses.activate
|
|
174
|
-
def test_features(self):
|
|
175
|
-
responses.add(
|
|
176
|
-
responses.POST,
|
|
177
|
-
"https://bucketing-api.devcycle.com/v1/features",
|
|
178
|
-
json={
|
|
179
|
-
"feature-1": {
|
|
180
|
-
"_id": "variable_id",
|
|
181
|
-
"key": "variable-key",
|
|
182
|
-
"type": "feature-type",
|
|
183
|
-
"_variation": "variation",
|
|
184
|
-
"variationName": "variation-name",
|
|
185
|
-
"variationKey": "variation-key",
|
|
186
|
-
"evalReason": "eval-reason",
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
)
|
|
190
|
-
result = self.test_client.features(self.test_user)
|
|
191
|
-
|
|
192
|
-
self.assertEqual(
|
|
193
|
-
result,
|
|
194
|
-
{
|
|
195
|
-
"feature-1": Feature(
|
|
196
|
-
_id="variable_id",
|
|
197
|
-
key="variable-key",
|
|
198
|
-
type="feature-type",
|
|
199
|
-
_variation="variation",
|
|
200
|
-
variationName="variation-name",
|
|
201
|
-
variationKey="variation-key",
|
|
202
|
-
evalReason="eval-reason",
|
|
203
|
-
)
|
|
204
|
-
},
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
@responses.activate
|
|
208
|
-
def test_track(self):
|
|
209
|
-
responses.add(
|
|
210
|
-
responses.POST,
|
|
211
|
-
"https://bucketing-api.devcycle.com/v1/track",
|
|
212
|
-
json={
|
|
213
|
-
"message": "success",
|
|
214
|
-
},
|
|
215
|
-
)
|
|
216
|
-
result = self.test_client.track(
|
|
217
|
-
self.test_user,
|
|
218
|
-
events=[
|
|
219
|
-
Event(
|
|
220
|
-
type="sample-event",
|
|
221
|
-
)
|
|
222
|
-
],
|
|
223
|
-
)
|
|
224
|
-
self.assertEqual(result, "success")
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if __name__ == "__main__":
|
|
228
|
-
unittest.main()
|
test/test_dvc_local_client.py
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import unittest
|
|
3
|
-
import uuid
|
|
4
|
-
from time import time
|
|
5
|
-
|
|
6
|
-
from devcycle_python_sdk import DevCycleLocalClient, DevCycleLocalOptions
|
|
7
|
-
from devcycle_python_sdk.models.event import Event
|
|
8
|
-
from devcycle_python_sdk.models.user import User
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class DVCLocalClientTest(unittest.TestCase):
|
|
14
|
-
def setUp(self) -> None:
|
|
15
|
-
sdk_key = "dvc_server_" + str(uuid.uuid4())
|
|
16
|
-
options = DevCycleLocalOptions()
|
|
17
|
-
self.test_client = DevCycleLocalClient(sdk_key, options)
|
|
18
|
-
self.test_user = User(user_id="test_user_id")
|
|
19
|
-
self.test_user_no_id = User(user_id=None)
|
|
20
|
-
self.test_user_empty_id = User(user_id="")
|
|
21
|
-
|
|
22
|
-
def tearDown(self) -> None:
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
def test_create_client_invalid_sdk_key(self):
|
|
26
|
-
with self.assertRaises(ValueError):
|
|
27
|
-
DevCycleLocalClient(None, None)
|
|
28
|
-
|
|
29
|
-
with self.assertRaises(ValueError):
|
|
30
|
-
DevCycleLocalClient("", None)
|
|
31
|
-
|
|
32
|
-
with self.assertRaises(ValueError):
|
|
33
|
-
DevCycleLocalClient("no prefix in key", None)
|
|
34
|
-
|
|
35
|
-
def test_create_client_diff_sdk_keys(self):
|
|
36
|
-
# ensure no exception is generated
|
|
37
|
-
client = DevCycleLocalClient("dvc_server_" + str(uuid.uuid4()), None)
|
|
38
|
-
self.assertIsNotNone(client)
|
|
39
|
-
client = DevCycleLocalClient("server_" + str(uuid.uuid4()), None)
|
|
40
|
-
self.assertIsNotNone(client)
|
|
41
|
-
|
|
42
|
-
def test_create_client_no_options(self):
|
|
43
|
-
sdk_key = "dvc_server_" + str(uuid.uuid4())
|
|
44
|
-
empty_options = DevCycleLocalOptions()
|
|
45
|
-
client = DevCycleLocalClient(sdk_key, empty_options)
|
|
46
|
-
self.assertIsNotNone(client)
|
|
47
|
-
|
|
48
|
-
option_with_data = DevCycleLocalOptions(
|
|
49
|
-
events_api_uri="https://localhost:8080",
|
|
50
|
-
config_cdn_uri="https://localhost:8080",
|
|
51
|
-
flush_event_queue_size=10000,
|
|
52
|
-
max_event_queue_size=2500
|
|
53
|
-
)
|
|
54
|
-
client = DevCycleLocalClient(sdk_key, option_with_data)
|
|
55
|
-
self.assertIsNotNone(client)
|
|
56
|
-
|
|
57
|
-
def test_variable_bad_user(self):
|
|
58
|
-
with self.assertRaises(ValueError):
|
|
59
|
-
self.test_client.variable(None, "strKey", "default_value")
|
|
60
|
-
|
|
61
|
-
with self.assertRaises(ValueError):
|
|
62
|
-
self.test_client.variable(self.test_user_no_id, "strKey", "default_value")
|
|
63
|
-
|
|
64
|
-
with self.assertRaises(ValueError):
|
|
65
|
-
self.test_client.variable(
|
|
66
|
-
self.test_user_empty_id, "strKey", "default_value"
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def test_variable_bad_key_and_value(self):
|
|
70
|
-
with self.assertRaises(ValueError):
|
|
71
|
-
self.test_client.variable(self.test_user, None, "default_value")
|
|
72
|
-
|
|
73
|
-
with self.assertRaises(ValueError):
|
|
74
|
-
self.test_client.variable(self.test_user, "", "default_value")
|
|
75
|
-
|
|
76
|
-
with self.assertRaises(ValueError):
|
|
77
|
-
self.test_client.variable(self.test_user, "strKey", None)
|
|
78
|
-
|
|
79
|
-
def test_all_variables_bad_user(self):
|
|
80
|
-
with self.assertRaises(ValueError):
|
|
81
|
-
self.test_client.all_variables(None)
|
|
82
|
-
|
|
83
|
-
with self.assertRaises(ValueError):
|
|
84
|
-
self.test_client.all_variables(self.test_user_no_id)
|
|
85
|
-
|
|
86
|
-
with self.assertRaises(ValueError):
|
|
87
|
-
self.test_client.all_variables(self.test_user_empty_id)
|
|
88
|
-
|
|
89
|
-
def test_all_features_bad_user(self):
|
|
90
|
-
with self.assertRaises(ValueError):
|
|
91
|
-
self.test_client.all_features(None)
|
|
92
|
-
|
|
93
|
-
with self.assertRaises(ValueError):
|
|
94
|
-
self.test_client.all_features(self.test_user_no_id)
|
|
95
|
-
|
|
96
|
-
with self.assertRaises(ValueError):
|
|
97
|
-
self.test_client.all_features(self.test_user_empty_id)
|
|
98
|
-
|
|
99
|
-
def test_track_event_bad_user(self):
|
|
100
|
-
event = Event(
|
|
101
|
-
type="user",
|
|
102
|
-
target="test_target",
|
|
103
|
-
date=time(),
|
|
104
|
-
value=42,
|
|
105
|
-
metaData={"key": "value"},
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
with self.assertRaises(ValueError):
|
|
109
|
-
self.test_client.track(None, event)
|
|
110
|
-
|
|
111
|
-
with self.assertRaises(ValueError):
|
|
112
|
-
self.test_client.track(self.test_user_no_id, event)
|
|
113
|
-
|
|
114
|
-
with self.assertRaises(ValueError):
|
|
115
|
-
self.test_client.track(self.test_user_empty_id, event)
|
|
116
|
-
|
|
117
|
-
def test_track_bad_event(self):
|
|
118
|
-
with self.assertRaises(ValueError):
|
|
119
|
-
self.test_client.track(self.test_user, None)
|
|
120
|
-
|
|
121
|
-
event = Event(
|
|
122
|
-
type=None,
|
|
123
|
-
target="test_target",
|
|
124
|
-
date=time(),
|
|
125
|
-
value=42,
|
|
126
|
-
metaData={"key": "value"},
|
|
127
|
-
)
|
|
128
|
-
with self.assertRaises(ValueError):
|
|
129
|
-
self.test_client.track(self.test_user, event)
|
|
130
|
-
|
|
131
|
-
event = Event(
|
|
132
|
-
type="",
|
|
133
|
-
target="test_target",
|
|
134
|
-
date=time(),
|
|
135
|
-
value=42,
|
|
136
|
-
metaData={"key": "value"},
|
|
137
|
-
)
|
|
138
|
-
with self.assertRaises(ValueError):
|
|
139
|
-
self.test_client.track(self.test_user, event)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if __name__ == "__main__":
|
|
143
|
-
unittest.main()
|
test/test_version.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from devcycle_python_sdk.util.version import sdk_version
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger(__name__)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class VersionTest(unittest.TestCase):
|
|
11
|
-
def test_sdk_version(self):
|
|
12
|
-
expected_version = "2.0.0"
|
|
13
|
-
version = sdk_version()
|
|
14
|
-
self.assertEqual(version, expected_version)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if __name__ == '__main__':
|
|
18
|
-
unittest.main()
|
{devcycle_python_server_sdk-2.0.1.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/LICENSE
RENAMED
|
File without changes
|
{devcycle_python_server_sdk-2.0.1.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|