devcycle-python-server-sdk 3.11.4__py3-none-any.whl → 3.12.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/api/bucketing_client.py +12 -0
- devcycle_python_sdk/api/local_bucketing.py +0 -8
- devcycle_python_sdk/bucketing-lib.release.wasm +0 -0
- devcycle_python_sdk/cloud_client.py +12 -3
- devcycle_python_sdk/local_client.py +28 -13
- devcycle_python_sdk/managers/sse_manager.py +1 -1
- devcycle_python_sdk/models/eval_reason.py +40 -0
- devcycle_python_sdk/models/variable.py +44 -6
- devcycle_python_sdk/open_feature_provider/provider.py +10 -5
- devcycle_python_sdk/protobuf/utils.py +18 -0
- devcycle_python_sdk/protobuf/variableForUserParams_pb2.py +43 -30
- devcycle_python_sdk/protobuf/variableForUserParams_pb2.pyi +100 -78
- {devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/METADATA +1 -1
- {devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/RECORD +23 -22
- test/api/test_local_bucketing.py +11 -0
- test/openfeature_test/test_provider.py +79 -2
- test/openfeature_test/test_provider_local_sdk.py +25 -0
- test/test_cloud_client.py +7 -0
- test/test_local_client.py +16 -1
- {devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/WHEEL +0 -0
- {devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/licenses/LICENSE +0 -0
- {devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/top_level.txt +0 -0
devcycle_python_sdk/VERSION.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.12.0
|
|
@@ -15,6 +15,7 @@ from devcycle_python_sdk.models.event import DevCycleEvent
|
|
|
15
15
|
from devcycle_python_sdk.models.feature import Feature
|
|
16
16
|
from devcycle_python_sdk.models.user import DevCycleUser
|
|
17
17
|
from devcycle_python_sdk.models.variable import Variable
|
|
18
|
+
from devcycle_python_sdk.models.eval_reason import EvalReason
|
|
18
19
|
from devcycle_python_sdk.util.strings import slash_join
|
|
19
20
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
@@ -91,11 +92,17 @@ class BucketingAPIClient:
|
|
|
91
92
|
def variable(self, key: str, user: DevCycleUser) -> Variable:
|
|
92
93
|
data = self.request("POST", self._url("variables", key), json=user.to_json())
|
|
93
94
|
|
|
95
|
+
eval_data = data.get("eval")
|
|
96
|
+
eval_reason = None
|
|
97
|
+
if eval_data is not None and isinstance(eval_data, dict):
|
|
98
|
+
eval_reason = EvalReason.from_json(eval_data)
|
|
99
|
+
|
|
94
100
|
return Variable(
|
|
95
101
|
_id=data.get("_id"),
|
|
96
102
|
key=data.get("key", ""),
|
|
97
103
|
type=data.get("type", ""),
|
|
98
104
|
value=data.get("value"),
|
|
105
|
+
eval=eval_reason,
|
|
99
106
|
)
|
|
100
107
|
|
|
101
108
|
def variables(self, user: DevCycleUser) -> Dict[str, Variable]:
|
|
@@ -109,6 +116,11 @@ class BucketingAPIClient:
|
|
|
109
116
|
type=str(value.get("type")),
|
|
110
117
|
value=value.get("value"),
|
|
111
118
|
isDefaulted=None,
|
|
119
|
+
eval=(
|
|
120
|
+
EvalReason.from_json(value.get("eval"))
|
|
121
|
+
if value.get("eval")
|
|
122
|
+
else None
|
|
123
|
+
),
|
|
112
124
|
)
|
|
113
125
|
|
|
114
126
|
return result
|
|
@@ -22,7 +22,6 @@ from wasmtime import (
|
|
|
22
22
|
import devcycle_python_sdk.protobuf.utils as pb_utils
|
|
23
23
|
import devcycle_python_sdk.protobuf.variableForUserParams_pb2 as pb2
|
|
24
24
|
from devcycle_python_sdk.exceptions import (
|
|
25
|
-
VariableTypeMismatchError,
|
|
26
25
|
MalformedConfigError,
|
|
27
26
|
)
|
|
28
27
|
from devcycle_python_sdk.models.bucketed_config import BucketedConfig
|
|
@@ -324,13 +323,6 @@ class LocalBucketing:
|
|
|
324
323
|
sdk_variable = pb2.SDKVariable_PB()
|
|
325
324
|
sdk_variable.ParseFromString(var_bytes)
|
|
326
325
|
|
|
327
|
-
if sdk_variable.type != pb_variable_type:
|
|
328
|
-
# this situation should never actually happen because the WASM handles
|
|
329
|
-
# it internally and returns a null value from the WASM function
|
|
330
|
-
# This check is here just in case that logic changes in the future
|
|
331
|
-
raise VariableTypeMismatchError(
|
|
332
|
-
f"Variable returned does not match requested type: {pb_variable_type}"
|
|
333
|
-
)
|
|
334
326
|
return pb_utils.create_variable(sdk_variable, default_value)
|
|
335
327
|
|
|
336
328
|
def generate_bucketed_config(self, user: DevCycleUser) -> BucketedConfig:
|
|
Binary file
|
|
@@ -14,6 +14,9 @@ from devcycle_python_sdk.managers.eval_hooks_manager import (
|
|
|
14
14
|
BeforeHookError,
|
|
15
15
|
AfterHookError,
|
|
16
16
|
)
|
|
17
|
+
from devcycle_python_sdk.models.eval_reason import (
|
|
18
|
+
DefaultReasonDetails,
|
|
19
|
+
)
|
|
17
20
|
from devcycle_python_sdk.models.eval_hook import EvalHook
|
|
18
21
|
from devcycle_python_sdk.models.eval_hook_context import HookContext
|
|
19
22
|
from devcycle_python_sdk.models.user import DevCycleUser
|
|
@@ -121,7 +124,9 @@ class DevCycleCloudClient(AbstractDevCycleClient):
|
|
|
121
124
|
except NotFoundError:
|
|
122
125
|
logger.warning(f"DevCycle: Variable not found: {key}")
|
|
123
126
|
return Variable.create_default_variable(
|
|
124
|
-
key=key,
|
|
127
|
+
key=key,
|
|
128
|
+
default_value=default_value,
|
|
129
|
+
default_reason_detail=DefaultReasonDetails.MISSING_VARIABLE,
|
|
125
130
|
)
|
|
126
131
|
except BeforeHookError as e:
|
|
127
132
|
self.eval_hooks_manager.run_error(context, e)
|
|
@@ -130,7 +135,9 @@ class DevCycleCloudClient(AbstractDevCycleClient):
|
|
|
130
135
|
except Exception as e:
|
|
131
136
|
logger.error(f"DevCycle: Error evaluating variable: {e}")
|
|
132
137
|
return Variable.create_default_variable(
|
|
133
|
-
key=key,
|
|
138
|
+
key=key,
|
|
139
|
+
default_value=default_value,
|
|
140
|
+
default_reason_detail=DefaultReasonDetails.ERROR,
|
|
134
141
|
)
|
|
135
142
|
finally:
|
|
136
143
|
self.eval_hooks_manager.run_finally(context, variable)
|
|
@@ -143,7 +150,9 @@ class DevCycleCloudClient(AbstractDevCycleClient):
|
|
|
143
150
|
f"DevCycle: Variable {key} is type {type(variable.value)}, but default value is type {type(default_value)}",
|
|
144
151
|
)
|
|
145
152
|
return Variable.create_default_variable(
|
|
146
|
-
key=key,
|
|
153
|
+
key=key,
|
|
154
|
+
default_value=default_value,
|
|
155
|
+
default_reason_detail=DefaultReasonDetails.TYPE_MISMATCH,
|
|
147
156
|
)
|
|
148
157
|
|
|
149
158
|
return variable
|
|
@@ -6,7 +6,6 @@ from typing import Any, Dict, Union, Optional
|
|
|
6
6
|
|
|
7
7
|
from devcycle_python_sdk import DevCycleLocalOptions, AbstractDevCycleClient
|
|
8
8
|
from devcycle_python_sdk.api.local_bucketing import LocalBucketing
|
|
9
|
-
from devcycle_python_sdk.exceptions import VariableTypeMismatchError
|
|
10
9
|
from devcycle_python_sdk.managers.config_manager import EnvironmentConfigManager
|
|
11
10
|
from devcycle_python_sdk.managers.eval_hooks_manager import (
|
|
12
11
|
EvalHooksManager,
|
|
@@ -17,6 +16,11 @@ from devcycle_python_sdk.managers.event_queue_manager import EventQueueManager
|
|
|
17
16
|
from devcycle_python_sdk.models.bucketed_config import BucketedConfig
|
|
18
17
|
from devcycle_python_sdk.models.eval_hook import EvalHook
|
|
19
18
|
from devcycle_python_sdk.models.eval_hook_context import HookContext
|
|
19
|
+
from devcycle_python_sdk.models.eval_reason import (
|
|
20
|
+
DefaultReasonDetails,
|
|
21
|
+
EvalReason,
|
|
22
|
+
EvalReasons,
|
|
23
|
+
)
|
|
20
24
|
from devcycle_python_sdk.models.event import DevCycleEvent, EventType
|
|
21
25
|
from devcycle_python_sdk.models.feature import Feature
|
|
22
26
|
from devcycle_python_sdk.models.platform_data import default_platform_data
|
|
@@ -131,7 +135,10 @@ class DevCycleLocalClient(AbstractDevCycleClient):
|
|
|
131
135
|
try:
|
|
132
136
|
self.event_queue_manager.queue_aggregate_event(
|
|
133
137
|
event=DevCycleEvent(
|
|
134
|
-
type=EventType.AggVariableDefaulted,
|
|
138
|
+
type=EventType.AggVariableDefaulted,
|
|
139
|
+
target=key,
|
|
140
|
+
value=1,
|
|
141
|
+
metaData={"evalReason": EvalReasons.DEFAULT},
|
|
135
142
|
),
|
|
136
143
|
bucketed_config=None,
|
|
137
144
|
)
|
|
@@ -139,7 +146,9 @@ class DevCycleLocalClient(AbstractDevCycleClient):
|
|
|
139
146
|
logger.warning(
|
|
140
147
|
f"DevCycle: Unable to track AggVariableDefaulted event for Variable {key}: {e}"
|
|
141
148
|
)
|
|
142
|
-
return Variable.create_default_variable(
|
|
149
|
+
return Variable.create_default_variable(
|
|
150
|
+
key, default_value, DefaultReasonDetails.MISSING_CONFIG
|
|
151
|
+
)
|
|
143
152
|
|
|
144
153
|
context = HookContext(key, user, default_value)
|
|
145
154
|
variable = Variable.create_default_variable(
|
|
@@ -159,22 +168,28 @@ class DevCycleLocalClient(AbstractDevCycleClient):
|
|
|
159
168
|
)
|
|
160
169
|
if bucketed_variable is not None:
|
|
161
170
|
variable = bucketed_variable
|
|
171
|
+
else:
|
|
172
|
+
variable.eval = EvalReason(
|
|
173
|
+
reason=EvalReasons.DEFAULT,
|
|
174
|
+
details=DefaultReasonDetails.USER_NOT_TARGETED,
|
|
175
|
+
)
|
|
162
176
|
|
|
163
177
|
if before_hook_error is None:
|
|
164
178
|
self.eval_hooks_manager.run_after(context, variable)
|
|
165
179
|
else:
|
|
166
180
|
raise before_hook_error
|
|
167
|
-
except VariableTypeMismatchError:
|
|
168
|
-
logger.debug("DevCycle: Variable type mismatch, returning default value")
|
|
169
|
-
return variable
|
|
170
|
-
except BeforeHookError as e:
|
|
171
|
-
self.eval_hooks_manager.run_error(context, e)
|
|
172
|
-
return variable
|
|
173
|
-
except AfterHookError as e:
|
|
174
|
-
self.eval_hooks_manager.run_error(context, e)
|
|
175
|
-
return variable
|
|
176
181
|
except Exception as e:
|
|
177
|
-
|
|
182
|
+
variable.eval = EvalReason(
|
|
183
|
+
reason=EvalReasons.DEFAULT, details=DefaultReasonDetails.ERROR
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if isinstance(e, BeforeHookError):
|
|
187
|
+
self.eval_hooks_manager.run_error(context, e)
|
|
188
|
+
elif isinstance(e, AfterHookError):
|
|
189
|
+
self.eval_hooks_manager.run_error(context, e)
|
|
190
|
+
else:
|
|
191
|
+
logger.warning(f"DevCycle: Error retrieving variable for user: {e}")
|
|
192
|
+
|
|
178
193
|
return variable
|
|
179
194
|
finally:
|
|
180
195
|
self.eval_hooks_manager.run_finally(context, variable)
|
|
@@ -43,7 +43,7 @@ class SSEManager:
|
|
|
43
43
|
elif isinstance(event, ld_eventsource.actions.Event):
|
|
44
44
|
handle_message(event)
|
|
45
45
|
except Exception as e:
|
|
46
|
-
logger.
|
|
46
|
+
logger.debug(f"DevCycle: failed to read SSE message: {e}")
|
|
47
47
|
|
|
48
48
|
def update(self, config: dict):
|
|
49
49
|
if self.use_new_config(config["sse"]):
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class EvalReasons:
|
|
6
|
+
"""Evaluation reasons constants"""
|
|
7
|
+
|
|
8
|
+
DEFAULT = "DEFAULT"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DefaultReasonDetails:
|
|
12
|
+
"""Default reason details constants"""
|
|
13
|
+
|
|
14
|
+
MISSING_CONFIG = "Missing Config"
|
|
15
|
+
USER_NOT_TARGETED = "User Not Targeted"
|
|
16
|
+
TYPE_MISMATCH = "Variable Type Mismatch"
|
|
17
|
+
MISSING_VARIABLE = "Missing Variable"
|
|
18
|
+
ERROR = "Error"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(order=False)
|
|
22
|
+
class EvalReason:
|
|
23
|
+
reason: str
|
|
24
|
+
details: Optional[str] = None
|
|
25
|
+
target_id: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
def to_json(self):
|
|
28
|
+
return {
|
|
29
|
+
key: getattr(self, key)
|
|
30
|
+
for key in self.__dataclass_fields__
|
|
31
|
+
if getattr(self, key) is not None
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_json(cls, data: dict) -> "EvalReason":
|
|
36
|
+
return cls(
|
|
37
|
+
reason=data["reason"],
|
|
38
|
+
details=data.get("details"),
|
|
39
|
+
target_id=data.get("target_id"),
|
|
40
|
+
)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import Optional, Any
|
|
4
4
|
|
|
5
|
+
from .eval_reason import EvalReason, EvalReasons
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class TypeEnum:
|
|
7
9
|
BOOLEAN = "Boolean"
|
|
@@ -32,16 +34,26 @@ class Variable:
|
|
|
32
34
|
isDefaulted: Optional[bool] = False
|
|
33
35
|
defaultValue: Any = None
|
|
34
36
|
evalReason: Optional[str] = None
|
|
37
|
+
eval: Optional[EvalReason] = None
|
|
35
38
|
|
|
36
39
|
def to_json(self):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if
|
|
41
|
-
|
|
40
|
+
result = {}
|
|
41
|
+
for key in self.__dataclass_fields__:
|
|
42
|
+
value = getattr(self, key)
|
|
43
|
+
if value is not None:
|
|
44
|
+
if key == "eval" and isinstance(value, EvalReason):
|
|
45
|
+
result[key] = value.to_json()
|
|
46
|
+
else:
|
|
47
|
+
result[key] = value
|
|
48
|
+
return result
|
|
42
49
|
|
|
43
50
|
@classmethod
|
|
44
51
|
def from_json(cls, data: dict) -> "Variable":
|
|
52
|
+
eval_data = data.get("eval")
|
|
53
|
+
eval_reason = None
|
|
54
|
+
if eval_data:
|
|
55
|
+
eval_reason = EvalReason.from_json(eval_data)
|
|
56
|
+
|
|
45
57
|
return cls(
|
|
46
58
|
_id=data["_id"],
|
|
47
59
|
key=data["key"],
|
|
@@ -50,11 +62,20 @@ class Variable:
|
|
|
50
62
|
isDefaulted=data.get("isDefaulted", None),
|
|
51
63
|
defaultValue=data.get("defaultValue"),
|
|
52
64
|
evalReason=data.get("evalReason"),
|
|
65
|
+
eval=eval_reason,
|
|
53
66
|
)
|
|
54
67
|
|
|
55
68
|
@staticmethod
|
|
56
|
-
def create_default_variable(
|
|
69
|
+
def create_default_variable(
|
|
70
|
+
key: str, default_value: Any, default_reason_detail: Optional[str] = None
|
|
71
|
+
) -> "Variable":
|
|
57
72
|
var_type = determine_variable_type(default_value)
|
|
73
|
+
if default_reason_detail is not None:
|
|
74
|
+
eval_reason = EvalReason(
|
|
75
|
+
reason=EvalReasons.DEFAULT, details=default_reason_detail
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
eval_reason = None
|
|
58
79
|
return Variable(
|
|
59
80
|
_id=None,
|
|
60
81
|
key=key,
|
|
@@ -62,4 +83,21 @@ class Variable:
|
|
|
62
83
|
value=default_value,
|
|
63
84
|
defaultValue=default_value,
|
|
64
85
|
isDefaulted=True,
|
|
86
|
+
eval=eval_reason,
|
|
65
87
|
)
|
|
88
|
+
|
|
89
|
+
def get_flag_meta_data(self) -> dict:
|
|
90
|
+
"""
|
|
91
|
+
Returns metadata dictionary for OpenFeature flag resolution.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
dict: Dictionary containing evalReasonDetails and evalReasonTargetId
|
|
95
|
+
if they exist, empty dict otherwise.
|
|
96
|
+
"""
|
|
97
|
+
meta_data = {}
|
|
98
|
+
if self.eval:
|
|
99
|
+
if self.eval.details:
|
|
100
|
+
meta_data["evalReasonDetails"] = self.eval.details
|
|
101
|
+
if self.eval.target_id:
|
|
102
|
+
meta_data["evalReasonTargetId"] = self.eval.target_id
|
|
103
|
+
return meta_data
|
|
@@ -80,13 +80,18 @@ class DevCycleProvider(AbstractProvider):
|
|
|
80
80
|
reason=Reason.DEFAULT,
|
|
81
81
|
)
|
|
82
82
|
else:
|
|
83
|
+
# TODO: once eval enabled from cloud bucketing, eval reason won't be null unless defaulted
|
|
84
|
+
if variable.eval and variable.eval.reason:
|
|
85
|
+
reason = variable.eval.reason
|
|
86
|
+
elif variable.isDefaulted:
|
|
87
|
+
reason = Reason.DEFAULT
|
|
88
|
+
else:
|
|
89
|
+
reason = Reason.TARGETING_MATCH
|
|
90
|
+
|
|
83
91
|
return FlagResolutionDetails(
|
|
84
92
|
value=variable.value,
|
|
85
|
-
reason=
|
|
86
|
-
|
|
87
|
-
if variable.isDefaulted
|
|
88
|
-
else Reason.TARGETING_MATCH
|
|
89
|
-
),
|
|
93
|
+
reason=reason,
|
|
94
|
+
flag_metadata=variable.get_flag_meta_data(),
|
|
90
95
|
)
|
|
91
96
|
except ValueError as e:
|
|
92
97
|
# occurs if the key or default value is None
|
|
@@ -5,6 +5,7 @@ import math
|
|
|
5
5
|
from typing import Any, Optional
|
|
6
6
|
|
|
7
7
|
from devcycle_python_sdk.models.variable import TypeEnum, Variable
|
|
8
|
+
from devcycle_python_sdk.models.eval_reason import EvalReason
|
|
8
9
|
from devcycle_python_sdk.models.user import DevCycleUser
|
|
9
10
|
|
|
10
11
|
import devcycle_python_sdk.protobuf.variableForUserParams_pb2 as pb2
|
|
@@ -82,7 +83,20 @@ def create_dvcuser_pb(user: DevCycleUser) -> pb2.DVCUser_PB: # type: ignore
|
|
|
82
83
|
)
|
|
83
84
|
|
|
84
85
|
|
|
86
|
+
def create_eval_reason_from_pb(eval_reason_pb: pb2.EvalReason_PB) -> EvalReason: # type: ignore
|
|
87
|
+
"""Convert EvalReason_PB protobuf message to EvalReason object"""
|
|
88
|
+
return EvalReason(
|
|
89
|
+
reason=eval_reason_pb.reason,
|
|
90
|
+
details=eval_reason_pb.details if eval_reason_pb.details else None,
|
|
91
|
+
target_id=eval_reason_pb.target_id if eval_reason_pb.target_id else None,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
85
95
|
def create_variable(sdk_variable: pb2.SDKVariable_PB, default_value: Any) -> Variable: # type: ignore
|
|
96
|
+
eval_reason_obj = None
|
|
97
|
+
if sdk_variable.HasField("eval"):
|
|
98
|
+
eval_reason_obj = create_eval_reason_from_pb(sdk_variable.eval)
|
|
99
|
+
|
|
86
100
|
if sdk_variable.type == pb2.VariableType_PB.Boolean: # type: ignore
|
|
87
101
|
return Variable(
|
|
88
102
|
_id=None,
|
|
@@ -91,6 +105,7 @@ def create_variable(sdk_variable: pb2.SDKVariable_PB, default_value: Any) -> Var
|
|
|
91
105
|
type=TypeEnum.BOOLEAN,
|
|
92
106
|
isDefaulted=False,
|
|
93
107
|
defaultValue=default_value,
|
|
108
|
+
eval=eval_reason_obj,
|
|
94
109
|
)
|
|
95
110
|
|
|
96
111
|
elif sdk_variable.type == pb2.VariableType_PB.String: # type: ignore
|
|
@@ -101,6 +116,7 @@ def create_variable(sdk_variable: pb2.SDKVariable_PB, default_value: Any) -> Var
|
|
|
101
116
|
type=TypeEnum.STRING,
|
|
102
117
|
isDefaulted=False,
|
|
103
118
|
defaultValue=default_value,
|
|
119
|
+
eval=eval_reason_obj,
|
|
104
120
|
)
|
|
105
121
|
|
|
106
122
|
elif sdk_variable.type == pb2.VariableType_PB.Number: # type: ignore
|
|
@@ -111,6 +127,7 @@ def create_variable(sdk_variable: pb2.SDKVariable_PB, default_value: Any) -> Var
|
|
|
111
127
|
type=TypeEnum.NUMBER,
|
|
112
128
|
isDefaulted=False,
|
|
113
129
|
defaultValue=default_value,
|
|
130
|
+
eval=eval_reason_obj,
|
|
114
131
|
)
|
|
115
132
|
|
|
116
133
|
elif sdk_variable.type == pb2.VariableType_PB.JSON: # type: ignore
|
|
@@ -123,6 +140,7 @@ def create_variable(sdk_variable: pb2.SDKVariable_PB, default_value: Any) -> Var
|
|
|
123
140
|
type=TypeEnum.JSON,
|
|
124
141
|
isDefaulted=False,
|
|
125
142
|
defaultValue=default_value,
|
|
143
|
+
eval=eval_reason_obj,
|
|
126
144
|
)
|
|
127
145
|
|
|
128
146
|
else:
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
3
4
|
# source: variableForUserParams.proto
|
|
5
|
+
# Protobuf Python Version: 5.29.3
|
|
4
6
|
"""Generated protocol buffer code."""
|
|
5
|
-
from google.protobuf.internal import builder as _builder
|
|
6
7
|
from google.protobuf import descriptor as _descriptor
|
|
7
8
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
8
10
|
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
5,
|
|
15
|
+
29,
|
|
16
|
+
3,
|
|
17
|
+
'',
|
|
18
|
+
'variableForUserParams.proto'
|
|
19
|
+
)
|
|
9
20
|
# @@protoc_insertion_point(imports)
|
|
10
21
|
|
|
11
22
|
_sym_db = _symbol_database.Default()
|
|
@@ -13,34 +24,36 @@ _sym_db = _symbol_database.Default()
|
|
|
13
24
|
|
|
14
25
|
|
|
15
26
|
|
|
16
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bvariableForUserParams.proto\"/\n\x0eNullableString\x12\r\n\x05value\x18\x01 \x01(\t\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"/\n\x0eNullableDouble\x12\r\n\x05value\x18\x01 \x01(\x01\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"m\n\x0f\x43ustomDataValue\x12\x1d\n\x04type\x18\x01 \x01(\x0e\x32\x0f.CustomDataType\x12\x11\n\tboolValue\x18\x02 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x03 \x01(\x01\x12\x13\n\x0bstringValue\x18\x04 \x01(\t\"\x93\x01\n\x12NullableCustomData\x12-\n\x05value\x18\x01 \x03(\x0b\x32\x1e.NullableCustomData.ValueEntry\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\x1a>\n\nValueEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1f\n\x05value\x18\x02 \x01(\x0b\x32\x10.CustomDataValue:\x02\x38\x01\"\x9c\x01\n\x18VariableForUserParams_PB\x12\x0e\n\x06sdkKey\x18\x01 \x01(\t\x12\x13\n\x0bvariableKey\x18\x02 \x01(\t\x12&\n\x0cvariableType\x18\x03 \x01(\x0e\x32\x10.VariableType_PB\x12\x19\n\x04user\x18\x04 \x01(\x0b\x32\x0b.DVCUser_PB\x12\x18\n\x10shouldTrackEvent\x18\x05 \x01(\x08\"\xe8\x02\n\nDVCUser_PB\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x1e\n\x05\x65mail\x18\x02 \x01(\x0b\x32\x0f.NullableString\x12\x1d\n\x04name\x18\x03 \x01(\x0b\x32\x0f.NullableString\x12!\n\x08language\x18\x04 \x01(\x0b\x32\x0f.NullableString\x12 \n\x07\x63ountry\x18\x05 \x01(\x0b\x32\x0f.NullableString\x12!\n\x08\x61ppBuild\x18\x06 \x01(\x0b\x32\x0f.NullableDouble\x12#\n\nappVersion\x18\x07 \x01(\x0b\x32\x0f.NullableString\x12$\n\x0b\x64\x65viceModel\x18\x08 \x01(\x0b\x32\x0f.NullableString\x12\'\n\ncustomData\x18\t \x01(\x0b\x32\x13.NullableCustomData\x12.\n\x11privateCustomData\x18\n \x01(\x0b\x32\x13.NullableCustomData\"\
|
|
17
|
-
|
|
18
|
-
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
|
19
|
-
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'variableForUserParams_pb2', globals())
|
|
20
|
-
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bvariableForUserParams.proto\"/\n\x0eNullableString\x12\r\n\x05value\x18\x01 \x01(\t\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"/\n\x0eNullableDouble\x12\r\n\x05value\x18\x01 \x01(\x01\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"m\n\x0f\x43ustomDataValue\x12\x1d\n\x04type\x18\x01 \x01(\x0e\x32\x0f.CustomDataType\x12\x11\n\tboolValue\x18\x02 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x03 \x01(\x01\x12\x13\n\x0bstringValue\x18\x04 \x01(\t\"\x93\x01\n\x12NullableCustomData\x12-\n\x05value\x18\x01 \x03(\x0b\x32\x1e.NullableCustomData.ValueEntry\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\x1a>\n\nValueEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1f\n\x05value\x18\x02 \x01(\x0b\x32\x10.CustomDataValue:\x02\x38\x01\"\x9c\x01\n\x18VariableForUserParams_PB\x12\x0e\n\x06sdkKey\x18\x01 \x01(\t\x12\x13\n\x0bvariableKey\x18\x02 \x01(\t\x12&\n\x0cvariableType\x18\x03 \x01(\x0e\x32\x10.VariableType_PB\x12\x19\n\x04user\x18\x04 \x01(\x0b\x32\x0b.DVCUser_PB\x12\x18\n\x10shouldTrackEvent\x18\x05 \x01(\x08\"\xe8\x02\n\nDVCUser_PB\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x1e\n\x05\x65mail\x18\x02 \x01(\x0b\x32\x0f.NullableString\x12\x1d\n\x04name\x18\x03 \x01(\x0b\x32\x0f.NullableString\x12!\n\x08language\x18\x04 \x01(\x0b\x32\x0f.NullableString\x12 \n\x07\x63ountry\x18\x05 \x01(\x0b\x32\x0f.NullableString\x12!\n\x08\x61ppBuild\x18\x06 \x01(\x0b\x32\x0f.NullableDouble\x12#\n\nappVersion\x18\x07 \x01(\x0b\x32\x0f.NullableString\x12$\n\x0b\x64\x65viceModel\x18\x08 \x01(\x0b\x32\x0f.NullableString\x12\'\n\ncustomData\x18\t \x01(\x0b\x32\x13.NullableCustomData\x12.\n\x11privateCustomData\x18\n \x01(\x0b\x32\x13.NullableCustomData\"\xed\x01\n\x0eSDKVariable_PB\x12\x0b\n\x03_id\x18\x01 \x01(\t\x12\x1e\n\x04type\x18\x02 \x01(\x0e\x32\x10.VariableType_PB\x12\x0b\n\x03key\x18\x03 \x01(\t\x12\x11\n\tboolValue\x18\x04 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x05 \x01(\x01\x12\x13\n\x0bstringValue\x18\x06 \x01(\t\x12#\n\nevalReason\x18\x07 \x01(\x0b\x32\x0f.NullableString\x12!\n\x08_feature\x18\x08 \x01(\x0b\x32\x0f.NullableString\x12\x1c\n\x04\x65val\x18\t \x01(\x0b\x32\x0e.EvalReason_PB\"C\n\rEvalReason_PB\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65tails\x18\x02 \x01(\t\x12\x11\n\ttarget_id\x18\x03 \x01(\t*@\n\x0fVariableType_PB\x12\x0b\n\x07\x42oolean\x10\x00\x12\n\n\x06Number\x10\x01\x12\n\n\x06String\x10\x02\x12\x08\n\x04JSON\x10\x03*6\n\x0e\x43ustomDataType\x12\x08\n\x04\x42ool\x10\x00\x12\x07\n\x03Num\x10\x01\x12\x07\n\x03Str\x10\x02\x12\x08\n\x04Null\x10\x03\x42X\n&com.devcycle.sdk.server.local.protobufP\x01Z\x07./proto\xaa\x02\"DevCycle.SDK.Server.Local.Protobufb\x06proto3')
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'variableForUserParams_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
_globals['DESCRIPTOR']._loaded_options = None
|
|
34
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n&com.devcycle.sdk.server.local.protobufP\001Z\007./proto\252\002\"DevCycle.SDK.Server.Local.Protobuf'
|
|
35
|
+
_globals['_NULLABLECUSTOMDATA_VALUEENTRY']._loaded_options = None
|
|
36
|
+
_globals['_NULLABLECUSTOMDATA_VALUEENTRY']._serialized_options = b'8\001'
|
|
37
|
+
_globals['_VARIABLETYPE_PB']._serialized_start=1221
|
|
38
|
+
_globals['_VARIABLETYPE_PB']._serialized_end=1285
|
|
39
|
+
_globals['_CUSTOMDATATYPE']._serialized_start=1287
|
|
40
|
+
_globals['_CUSTOMDATATYPE']._serialized_end=1341
|
|
41
|
+
_globals['_NULLABLESTRING']._serialized_start=31
|
|
42
|
+
_globals['_NULLABLESTRING']._serialized_end=78
|
|
43
|
+
_globals['_NULLABLEDOUBLE']._serialized_start=80
|
|
44
|
+
_globals['_NULLABLEDOUBLE']._serialized_end=127
|
|
45
|
+
_globals['_CUSTOMDATAVALUE']._serialized_start=129
|
|
46
|
+
_globals['_CUSTOMDATAVALUE']._serialized_end=238
|
|
47
|
+
_globals['_NULLABLECUSTOMDATA']._serialized_start=241
|
|
48
|
+
_globals['_NULLABLECUSTOMDATA']._serialized_end=388
|
|
49
|
+
_globals['_NULLABLECUSTOMDATA_VALUEENTRY']._serialized_start=326
|
|
50
|
+
_globals['_NULLABLECUSTOMDATA_VALUEENTRY']._serialized_end=388
|
|
51
|
+
_globals['_VARIABLEFORUSERPARAMS_PB']._serialized_start=391
|
|
52
|
+
_globals['_VARIABLEFORUSERPARAMS_PB']._serialized_end=547
|
|
53
|
+
_globals['_DVCUSER_PB']._serialized_start=550
|
|
54
|
+
_globals['_DVCUSER_PB']._serialized_end=910
|
|
55
|
+
_globals['_SDKVARIABLE_PB']._serialized_start=913
|
|
56
|
+
_globals['_SDKVARIABLE_PB']._serialized_end=1150
|
|
57
|
+
_globals['_EVALREASON_PB']._serialized_start=1152
|
|
58
|
+
_globals['_EVALREASON_PB']._serialized_end=1219
|
|
46
59
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -4,117 +4,139 @@ from google.protobuf import descriptor as _descriptor
|
|
|
4
4
|
from google.protobuf import message as _message
|
|
5
5
|
from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
|
6
6
|
|
|
7
|
-
Bool: CustomDataType
|
|
8
|
-
Boolean: VariableType_PB
|
|
9
7
|
DESCRIPTOR: _descriptor.FileDescriptor
|
|
8
|
+
|
|
9
|
+
class VariableType_PB(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
10
|
+
__slots__ = ()
|
|
11
|
+
Boolean: _ClassVar[VariableType_PB]
|
|
12
|
+
Number: _ClassVar[VariableType_PB]
|
|
13
|
+
String: _ClassVar[VariableType_PB]
|
|
14
|
+
JSON: _ClassVar[VariableType_PB]
|
|
15
|
+
|
|
16
|
+
class CustomDataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
17
|
+
__slots__ = ()
|
|
18
|
+
Bool: _ClassVar[CustomDataType]
|
|
19
|
+
Num: _ClassVar[CustomDataType]
|
|
20
|
+
Str: _ClassVar[CustomDataType]
|
|
21
|
+
Null: _ClassVar[CustomDataType]
|
|
22
|
+
Boolean: VariableType_PB
|
|
23
|
+
Number: VariableType_PB
|
|
24
|
+
String: VariableType_PB
|
|
10
25
|
JSON: VariableType_PB
|
|
11
|
-
|
|
26
|
+
Bool: CustomDataType
|
|
12
27
|
Num: CustomDataType
|
|
13
|
-
Number: VariableType_PB
|
|
14
28
|
Str: CustomDataType
|
|
15
|
-
|
|
29
|
+
Null: CustomDataType
|
|
30
|
+
|
|
31
|
+
class NullableString(_message.Message):
|
|
32
|
+
__slots__ = ("value", "isNull")
|
|
33
|
+
VALUE_FIELD_NUMBER: _ClassVar[int]
|
|
34
|
+
ISNULL_FIELD_NUMBER: _ClassVar[int]
|
|
35
|
+
value: str
|
|
36
|
+
isNull: bool
|
|
37
|
+
def __init__(self, value: _Optional[str] = ..., isNull: bool = ...) -> None: ...
|
|
38
|
+
|
|
39
|
+
class NullableDouble(_message.Message):
|
|
40
|
+
__slots__ = ("value", "isNull")
|
|
41
|
+
VALUE_FIELD_NUMBER: _ClassVar[int]
|
|
42
|
+
ISNULL_FIELD_NUMBER: _ClassVar[int]
|
|
43
|
+
value: float
|
|
44
|
+
isNull: bool
|
|
45
|
+
def __init__(self, value: _Optional[float] = ..., isNull: bool = ...) -> None: ...
|
|
16
46
|
|
|
17
47
|
class CustomDataValue(_message.Message):
|
|
18
|
-
__slots__ =
|
|
48
|
+
__slots__ = ("type", "boolValue", "doubleValue", "stringValue")
|
|
49
|
+
TYPE_FIELD_NUMBER: _ClassVar[int]
|
|
19
50
|
BOOLVALUE_FIELD_NUMBER: _ClassVar[int]
|
|
20
51
|
DOUBLEVALUE_FIELD_NUMBER: _ClassVar[int]
|
|
21
52
|
STRINGVALUE_FIELD_NUMBER: _ClassVar[int]
|
|
22
|
-
|
|
53
|
+
type: CustomDataType
|
|
23
54
|
boolValue: bool
|
|
24
55
|
doubleValue: float
|
|
25
56
|
stringValue: str
|
|
26
|
-
type: CustomDataType
|
|
27
57
|
def __init__(self, type: _Optional[_Union[CustomDataType, str]] = ..., boolValue: bool = ..., doubleValue: _Optional[float] = ..., stringValue: _Optional[str] = ...) -> None: ...
|
|
28
58
|
|
|
29
|
-
class DVCUser_PB(_message.Message):
|
|
30
|
-
__slots__ = ["appBuild", "appVersion", "country", "customData", "deviceModel", "email", "language", "name", "privateCustomData", "user_id"]
|
|
31
|
-
APPBUILD_FIELD_NUMBER: _ClassVar[int]
|
|
32
|
-
APPVERSION_FIELD_NUMBER: _ClassVar[int]
|
|
33
|
-
COUNTRY_FIELD_NUMBER: _ClassVar[int]
|
|
34
|
-
CUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
|
|
35
|
-
DEVICEMODEL_FIELD_NUMBER: _ClassVar[int]
|
|
36
|
-
EMAIL_FIELD_NUMBER: _ClassVar[int]
|
|
37
|
-
LANGUAGE_FIELD_NUMBER: _ClassVar[int]
|
|
38
|
-
NAME_FIELD_NUMBER: _ClassVar[int]
|
|
39
|
-
PRIVATECUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
|
|
40
|
-
USER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
41
|
-
appBuild: NullableDouble
|
|
42
|
-
appVersion: NullableString
|
|
43
|
-
country: NullableString
|
|
44
|
-
customData: NullableCustomData
|
|
45
|
-
deviceModel: NullableString
|
|
46
|
-
email: NullableString
|
|
47
|
-
language: NullableString
|
|
48
|
-
name: NullableString
|
|
49
|
-
privateCustomData: NullableCustomData
|
|
50
|
-
user_id: str
|
|
51
|
-
def __init__(self, user_id: _Optional[str] = ..., email: _Optional[_Union[NullableString, _Mapping]] = ..., name: _Optional[_Union[NullableString, _Mapping]] = ..., language: _Optional[_Union[NullableString, _Mapping]] = ..., country: _Optional[_Union[NullableString, _Mapping]] = ..., appBuild: _Optional[_Union[NullableDouble, _Mapping]] = ..., appVersion: _Optional[_Union[NullableString, _Mapping]] = ..., deviceModel: _Optional[_Union[NullableString, _Mapping]] = ..., customData: _Optional[_Union[NullableCustomData, _Mapping]] = ..., privateCustomData: _Optional[_Union[NullableCustomData, _Mapping]] = ...) -> None: ...
|
|
52
|
-
|
|
53
59
|
class NullableCustomData(_message.Message):
|
|
54
|
-
__slots__ =
|
|
60
|
+
__slots__ = ("value", "isNull")
|
|
55
61
|
class ValueEntry(_message.Message):
|
|
56
|
-
__slots__ =
|
|
62
|
+
__slots__ = ("key", "value")
|
|
57
63
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
|
58
64
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
|
59
65
|
key: str
|
|
60
66
|
value: CustomDataValue
|
|
61
67
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[CustomDataValue, _Mapping]] = ...) -> None: ...
|
|
62
|
-
ISNULL_FIELD_NUMBER: _ClassVar[int]
|
|
63
68
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
|
64
|
-
|
|
69
|
+
ISNULL_FIELD_NUMBER: _ClassVar[int]
|
|
65
70
|
value: _containers.MessageMap[str, CustomDataValue]
|
|
71
|
+
isNull: bool
|
|
66
72
|
def __init__(self, value: _Optional[_Mapping[str, CustomDataValue]] = ..., isNull: bool = ...) -> None: ...
|
|
67
73
|
|
|
68
|
-
class
|
|
69
|
-
__slots__ =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
class VariableForUserParams_PB(_message.Message):
|
|
75
|
+
__slots__ = ("sdkKey", "variableKey", "variableType", "user", "shouldTrackEvent")
|
|
76
|
+
SDKKEY_FIELD_NUMBER: _ClassVar[int]
|
|
77
|
+
VARIABLEKEY_FIELD_NUMBER: _ClassVar[int]
|
|
78
|
+
VARIABLETYPE_FIELD_NUMBER: _ClassVar[int]
|
|
79
|
+
USER_FIELD_NUMBER: _ClassVar[int]
|
|
80
|
+
SHOULDTRACKEVENT_FIELD_NUMBER: _ClassVar[int]
|
|
81
|
+
sdkKey: str
|
|
82
|
+
variableKey: str
|
|
83
|
+
variableType: VariableType_PB
|
|
84
|
+
user: DVCUser_PB
|
|
85
|
+
shouldTrackEvent: bool
|
|
86
|
+
def __init__(self, sdkKey: _Optional[str] = ..., variableKey: _Optional[str] = ..., variableType: _Optional[_Union[VariableType_PB, str]] = ..., user: _Optional[_Union[DVCUser_PB, _Mapping]] = ..., shouldTrackEvent: bool = ...) -> None: ...
|
|
75
87
|
|
|
76
|
-
class
|
|
77
|
-
__slots__ =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
class DVCUser_PB(_message.Message):
|
|
89
|
+
__slots__ = ("user_id", "email", "name", "language", "country", "appBuild", "appVersion", "deviceModel", "customData", "privateCustomData")
|
|
90
|
+
USER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
91
|
+
EMAIL_FIELD_NUMBER: _ClassVar[int]
|
|
92
|
+
NAME_FIELD_NUMBER: _ClassVar[int]
|
|
93
|
+
LANGUAGE_FIELD_NUMBER: _ClassVar[int]
|
|
94
|
+
COUNTRY_FIELD_NUMBER: _ClassVar[int]
|
|
95
|
+
APPBUILD_FIELD_NUMBER: _ClassVar[int]
|
|
96
|
+
APPVERSION_FIELD_NUMBER: _ClassVar[int]
|
|
97
|
+
DEVICEMODEL_FIELD_NUMBER: _ClassVar[int]
|
|
98
|
+
CUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
|
|
99
|
+
PRIVATECUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
|
|
100
|
+
user_id: str
|
|
101
|
+
email: NullableString
|
|
102
|
+
name: NullableString
|
|
103
|
+
language: NullableString
|
|
104
|
+
country: NullableString
|
|
105
|
+
appBuild: NullableDouble
|
|
106
|
+
appVersion: NullableString
|
|
107
|
+
deviceModel: NullableString
|
|
108
|
+
customData: NullableCustomData
|
|
109
|
+
privateCustomData: NullableCustomData
|
|
110
|
+
def __init__(self, user_id: _Optional[str] = ..., email: _Optional[_Union[NullableString, _Mapping]] = ..., name: _Optional[_Union[NullableString, _Mapping]] = ..., language: _Optional[_Union[NullableString, _Mapping]] = ..., country: _Optional[_Union[NullableString, _Mapping]] = ..., appBuild: _Optional[_Union[NullableDouble, _Mapping]] = ..., appVersion: _Optional[_Union[NullableString, _Mapping]] = ..., deviceModel: _Optional[_Union[NullableString, _Mapping]] = ..., customData: _Optional[_Union[NullableCustomData, _Mapping]] = ..., privateCustomData: _Optional[_Union[NullableCustomData, _Mapping]] = ...) -> None: ...
|
|
83
111
|
|
|
84
112
|
class SDKVariable_PB(_message.Message):
|
|
85
|
-
__slots__ =
|
|
113
|
+
__slots__ = ("_id", "type", "key", "boolValue", "doubleValue", "stringValue", "evalReason", "_feature", "eval")
|
|
114
|
+
_ID_FIELD_NUMBER: _ClassVar[int]
|
|
115
|
+
TYPE_FIELD_NUMBER: _ClassVar[int]
|
|
116
|
+
KEY_FIELD_NUMBER: _ClassVar[int]
|
|
86
117
|
BOOLVALUE_FIELD_NUMBER: _ClassVar[int]
|
|
87
118
|
DOUBLEVALUE_FIELD_NUMBER: _ClassVar[int]
|
|
88
|
-
EVALREASON_FIELD_NUMBER: _ClassVar[int]
|
|
89
|
-
KEY_FIELD_NUMBER: _ClassVar[int]
|
|
90
119
|
STRINGVALUE_FIELD_NUMBER: _ClassVar[int]
|
|
91
|
-
|
|
92
|
-
|
|
120
|
+
EVALREASON_FIELD_NUMBER: _ClassVar[int]
|
|
121
|
+
_FEATURE_FIELD_NUMBER: _ClassVar[int]
|
|
122
|
+
EVAL_FIELD_NUMBER: _ClassVar[int]
|
|
93
123
|
_id: str
|
|
124
|
+
type: VariableType_PB
|
|
125
|
+
key: str
|
|
94
126
|
boolValue: bool
|
|
95
127
|
doubleValue: float
|
|
96
|
-
evalReason: NullableString
|
|
97
|
-
key: str
|
|
98
128
|
stringValue: str
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
__slots__ = ["sdkKey", "shouldTrackEvent", "user", "variableKey", "variableType"]
|
|
104
|
-
SDKKEY_FIELD_NUMBER: _ClassVar[int]
|
|
105
|
-
SHOULDTRACKEVENT_FIELD_NUMBER: _ClassVar[int]
|
|
106
|
-
USER_FIELD_NUMBER: _ClassVar[int]
|
|
107
|
-
VARIABLEKEY_FIELD_NUMBER: _ClassVar[int]
|
|
108
|
-
VARIABLETYPE_FIELD_NUMBER: _ClassVar[int]
|
|
109
|
-
sdkKey: str
|
|
110
|
-
shouldTrackEvent: bool
|
|
111
|
-
user: DVCUser_PB
|
|
112
|
-
variableKey: str
|
|
113
|
-
variableType: VariableType_PB
|
|
114
|
-
def __init__(self, sdkKey: _Optional[str] = ..., variableKey: _Optional[str] = ..., variableType: _Optional[_Union[VariableType_PB, str]] = ..., user: _Optional[_Union[DVCUser_PB, _Mapping]] = ..., shouldTrackEvent: bool = ...) -> None: ...
|
|
115
|
-
|
|
116
|
-
class VariableType_PB(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
117
|
-
__slots__ = []
|
|
129
|
+
evalReason: NullableString
|
|
130
|
+
_feature: NullableString
|
|
131
|
+
eval: EvalReason_PB
|
|
132
|
+
def __init__(self, _id: _Optional[str] = ..., type: _Optional[_Union[VariableType_PB, str]] = ..., key: _Optional[str] = ..., boolValue: bool = ..., doubleValue: _Optional[float] = ..., stringValue: _Optional[str] = ..., evalReason: _Optional[_Union[NullableString, _Mapping]] = ..., _feature: _Optional[_Union[NullableString, _Mapping]] = ..., eval: _Optional[_Union[EvalReason_PB, _Mapping]] = ...) -> None: ...
|
|
118
133
|
|
|
119
|
-
class
|
|
120
|
-
__slots__ =
|
|
134
|
+
class EvalReason_PB(_message.Message):
|
|
135
|
+
__slots__ = ("reason", "details", "target_id")
|
|
136
|
+
REASON_FIELD_NUMBER: _ClassVar[int]
|
|
137
|
+
DETAILS_FIELD_NUMBER: _ClassVar[int]
|
|
138
|
+
TARGET_ID_FIELD_NUMBER: _ClassVar[int]
|
|
139
|
+
reason: str
|
|
140
|
+
details: str
|
|
141
|
+
target_id: str
|
|
142
|
+
def __init__(self, reason: _Optional[str] = ..., details: _Optional[str] = ..., target_id: _Optional[str] = ...) -> None: ...
|
{devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/RECORD
RENAMED
|
@@ -1,55 +1,56 @@
|
|
|
1
|
-
devcycle_python_sdk/VERSION.txt,sha256=
|
|
1
|
+
devcycle_python_sdk/VERSION.txt,sha256=TuPL9BzJKZqNU-jwsDtquUrxVYO4LjhqEL1HqlHKDZU,7
|
|
2
2
|
devcycle_python_sdk/__init__.py,sha256=30TcYb9PbvVi97gwk16omCGG1UoUI9lmmU7xpvxNMvc,336
|
|
3
|
-
devcycle_python_sdk/bucketing-lib.release.wasm,sha256=
|
|
4
|
-
devcycle_python_sdk/cloud_client.py,sha256=
|
|
3
|
+
devcycle_python_sdk/bucketing-lib.release.wasm,sha256=wpbXlOj_TVIO2OcOD781sW5ICmGsYe9pvxG6DwYw6FY,223167
|
|
4
|
+
devcycle_python_sdk/cloud_client.py,sha256=LMKB6gEuQNn5YePUPO0t1QIAUXot4OyshSUlUbuu07U,9413
|
|
5
5
|
devcycle_python_sdk/devcycle_client.py,sha256=OqNza7OXOSRmMQq2kmyivljmH1GyN4dTiz0EhvDvt8w,1069
|
|
6
6
|
devcycle_python_sdk/exceptions.py,sha256=Et7fS8pEFbHkFY86T2pJoEUstwjh4_rzhJxVH8fzAns,1053
|
|
7
|
-
devcycle_python_sdk/local_client.py,sha256=
|
|
7
|
+
devcycle_python_sdk/local_client.py,sha256=obZlHBJ9MS79sntdpPb4JTwC29e58Q5PzzEtei3m9F0,11684
|
|
8
8
|
devcycle_python_sdk/options.py,sha256=W3vNZfV6QNnQyaOuRf6Z5cK6ut6fWtlGqlUfhzWX0_c,5253
|
|
9
9
|
devcycle_python_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
devcycle_python_sdk/api/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
11
11
|
devcycle_python_sdk/api/backoff.py,sha256=F6WYCloaZD9CjNu6mKBMq7K20S8-i2WVU6X05ZRoawE,303
|
|
12
|
-
devcycle_python_sdk/api/bucketing_client.py,sha256
|
|
12
|
+
devcycle_python_sdk/api/bucketing_client.py,sha256=BRQWtTnIyLdWoFT7sPHPGg8kstDtW-jx1N4og3_SEgI,5565
|
|
13
13
|
devcycle_python_sdk/api/config_client.py,sha256=73aQAq130rTHpQrVDX6n_DwFKz0WuKxRoLDiE3vSoAM,5077
|
|
14
14
|
devcycle_python_sdk/api/event_client.py,sha256=WxkuV6geEC9MgG4FN36HeEQQa22ilq2mkOVMZkpiEk4,3356
|
|
15
|
-
devcycle_python_sdk/api/local_bucketing.py,sha256=
|
|
15
|
+
devcycle_python_sdk/api/local_bucketing.py,sha256=IXOP-680OmaO1XoX31YwY8FychAD5aqeyFFGe9SE_u4,15949
|
|
16
16
|
devcycle_python_sdk/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
devcycle_python_sdk/managers/config_manager.py,sha256=AIGY9jXEhUzhEeaKIA1P5F2q6GaclrezAoGHgYU4dLQ,5634
|
|
18
18
|
devcycle_python_sdk/managers/eval_hooks_manager.py,sha256=skOGK_hyZNpgkdeNXTTa-d1GVRITjoi8T6dO5I_Cqfk,2679
|
|
19
19
|
devcycle_python_sdk/managers/event_queue_manager.py,sha256=uPy1vO6OAisZjUNAhWELE_g17alTiOcPv42z-WwkGck,8110
|
|
20
|
-
devcycle_python_sdk/managers/sse_manager.py,sha256=
|
|
20
|
+
devcycle_python_sdk/managers/sse_manager.py,sha256=hSGpnZ6gNbQXGotqNxjogoB82znAyMMFhqdlqeCc5bI,2586
|
|
21
21
|
devcycle_python_sdk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
devcycle_python_sdk/models/bucketed_config.py,sha256=qTfhIViX7m2WS2oDBBCmKKzPsJG6DEav3zCEXwP27nA,3980
|
|
23
23
|
devcycle_python_sdk/models/error_response.py,sha256=r_dRUDAbral9UGaMYcnBsZOr9Hmkyi1SQbhGmZilgpA,442
|
|
24
24
|
devcycle_python_sdk/models/eval_hook.py,sha256=_mDoEa8gkzMq-XpFRZrcXu_ldtpUzv5tlh-PghLpMMI,590
|
|
25
25
|
devcycle_python_sdk/models/eval_hook_context.py,sha256=uBa8t0XxbcPBYAewNPFC2902qCjGhVoqUzZOZbGZ3n0,267
|
|
26
|
+
devcycle_python_sdk/models/eval_reason.py,sha256=nMXcbR1DM8eriCQ1NNnIRQKkhJSYAbY8_9GgBfj7P8k,943
|
|
26
27
|
devcycle_python_sdk/models/event.py,sha256=Sj3eEopuPp8QwQl_Oz8E6Y9Ph6nLG0HxU7eR7xSbA7g,3722
|
|
27
28
|
devcycle_python_sdk/models/feature.py,sha256=m3wxxIjC5jxhKpPLI4RcsBaNLuy-zsMqoAgc8ySPe0I,852
|
|
28
29
|
devcycle_python_sdk/models/platform_data.py,sha256=nyQsgI6pcTd1FL3Y2ACmpvTYb08R_UtWul25OWZstps,849
|
|
29
30
|
devcycle_python_sdk/models/user.py,sha256=mhEk-mnKJRg_tydpBlAVhGDj9rDCacJ-jNwiGeSpZiE,7027
|
|
30
|
-
devcycle_python_sdk/models/variable.py,sha256=
|
|
31
|
+
devcycle_python_sdk/models/variable.py,sha256=NXubhYWXYRAUEh4kDe2Tu9T5QnZmQ3roYVPbP-u2H3s,3030
|
|
31
32
|
devcycle_python_sdk/open_feature_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
-
devcycle_python_sdk/open_feature_provider/provider.py,sha256=
|
|
33
|
+
devcycle_python_sdk/open_feature_provider/provider.py,sha256=l-psKU5m6jSBE53VwX3fBBas-TztekFDlfYTyTJQm6c,5558
|
|
33
34
|
devcycle_python_sdk/protobuf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
devcycle_python_sdk/protobuf/utils.py,sha256=
|
|
35
|
-
devcycle_python_sdk/protobuf/variableForUserParams_pb2.py,sha256=
|
|
36
|
-
devcycle_python_sdk/protobuf/variableForUserParams_pb2.pyi,sha256=
|
|
35
|
+
devcycle_python_sdk/protobuf/utils.py,sha256=6cBL8GZ5nCZw6JoO7lAdgCj9wM1u0lp6QFQMnGE1fiM,5414
|
|
36
|
+
devcycle_python_sdk/protobuf/variableForUserParams_pb2.py,sha256=yawFsQ7m-6OB7DxrjXo2FLLkHhiTv6lDnjubhjAgw_8,5198
|
|
37
|
+
devcycle_python_sdk/protobuf/variableForUserParams_pb2.pyi,sha256=tfN0zBapUPA9WGqUG5lU5XXgH_BsHTt5mgXKGsyS4-I,6623
|
|
37
38
|
devcycle_python_sdk/util/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
38
39
|
devcycle_python_sdk/util/strings.py,sha256=gH0Xm3gIXNqn6hczKPk10Jn5_9knuD7OB5MrB7bqztw,252
|
|
39
40
|
devcycle_python_sdk/util/version.py,sha256=Cz7Us_EuXP-T8GI47g2WHYdBJj_zKo-5y545IDHJAjI,253
|
|
40
|
-
devcycle_python_server_sdk-3.
|
|
41
|
+
devcycle_python_server_sdk-3.12.0.dist-info/licenses/LICENSE,sha256=tHQMqu-Jic2UpMGqb-vNFbGygysl8IG4l1clnnh0OFo,1071
|
|
41
42
|
example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
43
|
example/cloud_client_example.py,sha256=YD8ZsTv3dPYfbFUKraRpRT0VsJGrPwjm8ZCuA5H54yI,2463
|
|
43
44
|
example/local_bucketing_client_example.py,sha256=TaIfy3VnW9fXii9NPYs5UOuP4fLKruXMjnZApveqzl4,2796
|
|
44
45
|
example/openfeature_example.py,sha256=kO6F-Xk20l6CPiMfIxSRreshEGmHMFHRg3zbeLXa3AI,2410
|
|
45
46
|
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
-
test/test_cloud_client.py,sha256=
|
|
47
|
-
test/test_local_client.py,sha256=
|
|
47
|
+
test/test_cloud_client.py,sha256=oqOWh4aa07ObJOJeEjXMon0O2jPW_lYZMVInbFR-uzI,14586
|
|
48
|
+
test/test_local_client.py,sha256=d0D0h2vqSiZItiqGXrlFXRRmuZG-TNg8MT84dY6w0Ks,16250
|
|
48
49
|
test/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
50
|
test/api/test_bucketing_client.py,sha256=M2mjACowLvbsIjXdP-w8-fwY02xqv1X1r_h9v-pXa7A,7739
|
|
50
51
|
test/api/test_config_client.py,sha256=3BwMm4y-4NQcdW2DDGppoJLIF-ffgjlUJti2Qjw1D1M,5129
|
|
51
52
|
test/api/test_event_client.py,sha256=v4Xoum-PdywfczrfMV1llM5vrIJoqHybzxPLVokh09g,3310
|
|
52
|
-
test/api/test_local_bucketing.py,sha256=
|
|
53
|
+
test/api/test_local_bucketing.py,sha256=0cbD8kiRYCVadvU-ldMES-sr7rV9Ld4H1q62HCx4AHU,15970
|
|
53
54
|
test/fixture/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
55
|
test/fixture/data.py,sha256=6ReLxWPMhCDbJ5Ex24_iR0PJNjKzPFV1I9j_v0FVbBU,1462
|
|
55
56
|
test/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -59,13 +60,13 @@ test/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
59
60
|
test/models/test_bucketed_config.py,sha256=8wCEyxaHxY21nl7ZbiFzp-IzZQBZIPIVPPzeREjumQk,858
|
|
60
61
|
test/models/test_user.py,sha256=ZUbFUndYrnefyGl8rxZgG3HdHPZs4BosQWimkT7etJc,10540
|
|
61
62
|
test/openfeature_test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
-
test/openfeature_test/test_provider.py,sha256=
|
|
63
|
-
test/openfeature_test/test_provider_local_sdk.py,sha256=
|
|
63
|
+
test/openfeature_test/test_provider.py,sha256=hIzdqqBTulGRODeTcRJUP93v76WEolUC2QRe3DccF3U,10932
|
|
64
|
+
test/openfeature_test/test_provider_local_sdk.py,sha256=ExkA1LYLuvW-dD4Bw2BKggzDQ8GpnIp3WyNK4rUZgJQ,5696
|
|
64
65
|
test/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
66
|
test/util/test_strings.py,sha256=T5H5U0af7_ld1yJC8IdDmay_Mx-iJQ5Fi5fbvKcAc7A,862
|
|
66
67
|
test/util/test_utils.py,sha256=tVBJm_qnqg-4PssLpXIxqlk0JehuiAIPs4zpi2oNVnY,6671
|
|
67
68
|
test/util/test_version.py,sha256=3Xg0_84m0bdHXz6pxd2Z85Oz9DoUC1T0Ol_WqEH_jac,335
|
|
68
|
-
devcycle_python_server_sdk-3.
|
|
69
|
-
devcycle_python_server_sdk-3.
|
|
70
|
-
devcycle_python_server_sdk-3.
|
|
71
|
-
devcycle_python_server_sdk-3.
|
|
69
|
+
devcycle_python_server_sdk-3.12.0.dist-info/METADATA,sha256=-bA66ayqIdC5UFrsLx0jZ9m4eNSHzcdTuFvA-sY-IQE,723
|
|
70
|
+
devcycle_python_server_sdk-3.12.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
71
|
+
devcycle_python_server_sdk-3.12.0.dist-info/top_level.txt,sha256=34f8zDzihAHoH7siAFlmscyO_QLNWWq9HApFlwtYL4k,33
|
|
72
|
+
devcycle_python_server_sdk-3.12.0.dist-info/RECORD,,
|
test/api/test_local_bucketing.py
CHANGED
|
@@ -10,6 +10,7 @@ from devcycle_python_sdk.models.bucketed_config import (
|
|
|
10
10
|
Project,
|
|
11
11
|
ProjectSettings,
|
|
12
12
|
)
|
|
13
|
+
from devcycle_python_sdk.models.eval_reason import EvalReason
|
|
13
14
|
from devcycle_python_sdk.models.feature import Feature
|
|
14
15
|
from devcycle_python_sdk.models.variable import Variable
|
|
15
16
|
from devcycle_python_sdk.models.platform_data import default_platform_data
|
|
@@ -155,6 +156,11 @@ class LocalBucketingTest(unittest.TestCase):
|
|
|
155
156
|
evalReason=None,
|
|
156
157
|
)
|
|
157
158
|
}
|
|
159
|
+
expected_eval = EvalReason(
|
|
160
|
+
reason="TARGETING_MATCH",
|
|
161
|
+
details="All Users",
|
|
162
|
+
target_id="63125321d31c601f992288bc",
|
|
163
|
+
)
|
|
158
164
|
expected_variables = {
|
|
159
165
|
"a-cool-new-feature": Variable(
|
|
160
166
|
_id="62fbf6566f1ba302829f9e34",
|
|
@@ -164,6 +170,7 @@ class LocalBucketingTest(unittest.TestCase):
|
|
|
164
170
|
isDefaulted=None,
|
|
165
171
|
defaultValue=None,
|
|
166
172
|
evalReason=None,
|
|
173
|
+
eval=expected_eval,
|
|
167
174
|
),
|
|
168
175
|
"string-var": Variable(
|
|
169
176
|
_id="63125320a4719939fd57cb2b",
|
|
@@ -173,6 +180,7 @@ class LocalBucketingTest(unittest.TestCase):
|
|
|
173
180
|
isDefaulted=None,
|
|
174
181
|
defaultValue=None,
|
|
175
182
|
evalReason=None,
|
|
183
|
+
eval=expected_eval,
|
|
176
184
|
),
|
|
177
185
|
"json-var": Variable(
|
|
178
186
|
_id="64372363125123fca69d3f7b",
|
|
@@ -186,6 +194,7 @@ class LocalBucketingTest(unittest.TestCase):
|
|
|
186
194
|
isDefaulted=None,
|
|
187
195
|
defaultValue=None,
|
|
188
196
|
evalReason=None,
|
|
197
|
+
eval=expected_eval,
|
|
189
198
|
),
|
|
190
199
|
"num-var": Variable(
|
|
191
200
|
_id="65272363125123fca69d3a7d",
|
|
@@ -195,6 +204,7 @@ class LocalBucketingTest(unittest.TestCase):
|
|
|
195
204
|
isDefaulted=None,
|
|
196
205
|
defaultValue=None,
|
|
197
206
|
evalReason=None,
|
|
207
|
+
eval=expected_eval,
|
|
198
208
|
),
|
|
199
209
|
"float-var": Variable(
|
|
200
210
|
_id="61200363125123fca69d3a7a",
|
|
@@ -204,6 +214,7 @@ class LocalBucketingTest(unittest.TestCase):
|
|
|
204
214
|
isDefaulted=None,
|
|
205
215
|
defaultValue=None,
|
|
206
216
|
evalReason=None,
|
|
217
|
+
eval=expected_eval,
|
|
207
218
|
),
|
|
208
219
|
}
|
|
209
220
|
|
|
@@ -12,6 +12,10 @@ from openfeature.exception import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
from devcycle_python_sdk.models.variable import Variable, TypeEnum
|
|
15
|
+
from devcycle_python_sdk.models.eval_reason import (
|
|
16
|
+
EvalReason,
|
|
17
|
+
DefaultReasonDetails,
|
|
18
|
+
)
|
|
15
19
|
|
|
16
20
|
from devcycle_python_sdk.open_feature_provider.provider import (
|
|
17
21
|
DevCycleProvider,
|
|
@@ -69,7 +73,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
69
73
|
|
|
70
74
|
def test_resolve_details_client_returns_default_variable(self):
|
|
71
75
|
self.client.variable.return_value = Variable.create_default_variable(
|
|
72
|
-
key="test-flag",
|
|
76
|
+
key="test-flag",
|
|
77
|
+
default_value=False,
|
|
78
|
+
default_reason_detail=DefaultReasonDetails.USER_NOT_TARGETED,
|
|
73
79
|
)
|
|
74
80
|
context = EvaluationContext(targeting_key="user-1234")
|
|
75
81
|
details = self.provider._resolve("test-flag", False, context)
|
|
@@ -77,6 +83,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
77
83
|
self.assertIsNotNone(details)
|
|
78
84
|
self.assertEqual(details.value, False)
|
|
79
85
|
self.assertEqual(details.reason, Reason.DEFAULT)
|
|
86
|
+
self.assertEqual(
|
|
87
|
+
details.flag_metadata["evalReasonDetails"], "User Not Targeted"
|
|
88
|
+
)
|
|
80
89
|
|
|
81
90
|
def test_resolve_boolean_details(self):
|
|
82
91
|
key = "test-flag"
|
|
@@ -90,6 +99,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
90
99
|
type=TypeEnum.BOOLEAN,
|
|
91
100
|
isDefaulted=False,
|
|
92
101
|
defaultValue=False,
|
|
102
|
+
eval=EvalReason(
|
|
103
|
+
reason="TARGETING_MATCH", details="All Users", target_id="targetId"
|
|
104
|
+
),
|
|
93
105
|
)
|
|
94
106
|
|
|
95
107
|
context = EvaluationContext(targeting_key="user-1234")
|
|
@@ -98,6 +110,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
98
110
|
self.assertIsNotNone(details)
|
|
99
111
|
self.assertEqual(details.value, variable_value)
|
|
100
112
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
113
|
+
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
114
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
115
|
+
self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId")
|
|
101
116
|
|
|
102
117
|
def test_resolve_string_details(self):
|
|
103
118
|
key = "test-flag"
|
|
@@ -111,6 +126,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
111
126
|
type=TypeEnum.STRING,
|
|
112
127
|
isDefaulted=False,
|
|
113
128
|
defaultValue=False,
|
|
129
|
+
eval=EvalReason(
|
|
130
|
+
reason="TARGETING_MATCH", details="All Users", target_id="targetId"
|
|
131
|
+
),
|
|
114
132
|
)
|
|
115
133
|
|
|
116
134
|
context = EvaluationContext(targeting_key="user-1234")
|
|
@@ -120,6 +138,8 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
120
138
|
self.assertEqual(details.value, variable_value)
|
|
121
139
|
self.assertIsInstance(details.value, str)
|
|
122
140
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
141
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
142
|
+
self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId")
|
|
123
143
|
|
|
124
144
|
def test_resolve_integer_details(self):
|
|
125
145
|
key = "test-flag"
|
|
@@ -133,6 +153,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
133
153
|
type=TypeEnum.STRING,
|
|
134
154
|
isDefaulted=False,
|
|
135
155
|
defaultValue=False,
|
|
156
|
+
eval=EvalReason(
|
|
157
|
+
reason="TARGETING_MATCH", details="All Users", target_id="targetId"
|
|
158
|
+
),
|
|
136
159
|
)
|
|
137
160
|
|
|
138
161
|
context = EvaluationContext(targeting_key="user-1234")
|
|
@@ -142,6 +165,8 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
142
165
|
self.assertIsInstance(details.value, int)
|
|
143
166
|
self.assertEqual(details.value, variable_value)
|
|
144
167
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
168
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
169
|
+
self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId")
|
|
145
170
|
|
|
146
171
|
def test_resolve_float_details(self):
|
|
147
172
|
key = "test-flag"
|
|
@@ -155,6 +180,7 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
155
180
|
type=TypeEnum.STRING,
|
|
156
181
|
isDefaulted=False,
|
|
157
182
|
defaultValue=False,
|
|
183
|
+
eval=EvalReason(reason="SPLIT", details="Rollout", target_id="targetId"),
|
|
158
184
|
)
|
|
159
185
|
|
|
160
186
|
context = EvaluationContext(targeting_key="user-1234")
|
|
@@ -163,7 +189,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
163
189
|
self.assertIsNotNone(details)
|
|
164
190
|
self.assertIsInstance(details.value, float)
|
|
165
191
|
self.assertEqual(details.value, variable_value)
|
|
166
|
-
self.assertEqual(details.reason, Reason.
|
|
192
|
+
self.assertEqual(details.reason, Reason.SPLIT)
|
|
193
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "Rollout")
|
|
194
|
+
self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId")
|
|
167
195
|
|
|
168
196
|
def test_resolve_object_details_verify_default_value(self):
|
|
169
197
|
key = "test-flag"
|
|
@@ -204,6 +232,9 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
204
232
|
type=TypeEnum.STRING,
|
|
205
233
|
isDefaulted=False,
|
|
206
234
|
defaultValue=False,
|
|
235
|
+
eval=EvalReason(
|
|
236
|
+
reason="TARGETING_MATCH", details="Rollout", target_id="targetId"
|
|
237
|
+
),
|
|
207
238
|
)
|
|
208
239
|
|
|
209
240
|
context = EvaluationContext(targeting_key="user-1234")
|
|
@@ -213,6 +244,52 @@ class DevCycleProviderTest(unittest.TestCase):
|
|
|
213
244
|
self.assertIsInstance(details.value, dict)
|
|
214
245
|
self.assertDictEqual(details.value, variable_value)
|
|
215
246
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
247
|
+
self.assertIsNotNone(details.flag_metadata)
|
|
248
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "Rollout")
|
|
249
|
+
self.assertEqual(details.flag_metadata["evalReasonTargetId"], "targetId")
|
|
250
|
+
|
|
251
|
+
def test_resolve_string_details_null_eval(self):
|
|
252
|
+
key = "test-flag"
|
|
253
|
+
variable_value = "some string"
|
|
254
|
+
default_value = "default string"
|
|
255
|
+
|
|
256
|
+
self.client.variable.return_value = Variable(
|
|
257
|
+
_id=None,
|
|
258
|
+
value=variable_value,
|
|
259
|
+
key=key,
|
|
260
|
+
type=TypeEnum.STRING,
|
|
261
|
+
isDefaulted=False,
|
|
262
|
+
defaultValue=False,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
context = EvaluationContext(targeting_key="user-1234")
|
|
266
|
+
details = self.provider.resolve_string_details(key, default_value, context)
|
|
267
|
+
|
|
268
|
+
self.assertIsNotNone(details)
|
|
269
|
+
self.assertEqual(details.value, variable_value)
|
|
270
|
+
self.assertIsInstance(details.value, str)
|
|
271
|
+
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
272
|
+
|
|
273
|
+
def test_default_string_details_null_eval(self):
|
|
274
|
+
key = "test-flag"
|
|
275
|
+
default_value = "default string"
|
|
276
|
+
|
|
277
|
+
self.client.variable.return_value = Variable(
|
|
278
|
+
_id=None,
|
|
279
|
+
value=default_value,
|
|
280
|
+
key=key,
|
|
281
|
+
type=TypeEnum.STRING,
|
|
282
|
+
isDefaulted=True,
|
|
283
|
+
defaultValue=False,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
context = EvaluationContext(targeting_key="user-1234")
|
|
287
|
+
details = self.provider.resolve_string_details(key, default_value, context)
|
|
288
|
+
|
|
289
|
+
self.assertIsNotNone(details)
|
|
290
|
+
self.assertEqual(details.value, default_value)
|
|
291
|
+
self.assertIsInstance(details.value, str)
|
|
292
|
+
self.assertEqual(details.reason, Reason.DEFAULT)
|
|
216
293
|
|
|
217
294
|
|
|
218
295
|
if __name__ == "__main__":
|
|
@@ -66,6 +66,11 @@ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
|
|
|
66
66
|
self.assertIsNotNone(details)
|
|
67
67
|
self.assertEqual(details.value, expected_value)
|
|
68
68
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
69
|
+
self.assertIsNotNone(details.flag_metadata)
|
|
70
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
71
|
+
self.assertEqual(
|
|
72
|
+
details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc"
|
|
73
|
+
)
|
|
69
74
|
|
|
70
75
|
@responses.activate
|
|
71
76
|
def test_resolve_integer_details(self):
|
|
@@ -82,6 +87,11 @@ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
|
|
|
82
87
|
self.assertIsNotNone(details)
|
|
83
88
|
self.assertEqual(details.value, expected_value)
|
|
84
89
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
90
|
+
self.assertIsNotNone(details.flag_metadata)
|
|
91
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
92
|
+
self.assertEqual(
|
|
93
|
+
details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc"
|
|
94
|
+
)
|
|
85
95
|
|
|
86
96
|
@responses.activate
|
|
87
97
|
def test_resolve_float_details(self):
|
|
@@ -98,6 +108,11 @@ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
|
|
|
98
108
|
self.assertIsNotNone(details)
|
|
99
109
|
self.assertEqual(details.value, expected_value)
|
|
100
110
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
111
|
+
self.assertIsNotNone(details.flag_metadata)
|
|
112
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
113
|
+
self.assertEqual(
|
|
114
|
+
details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc"
|
|
115
|
+
)
|
|
101
116
|
|
|
102
117
|
@responses.activate
|
|
103
118
|
def test_resolve_string_details(self):
|
|
@@ -114,6 +129,11 @@ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
|
|
|
114
129
|
self.assertIsNotNone(details)
|
|
115
130
|
self.assertEqual(details.value, expected_value)
|
|
116
131
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
132
|
+
self.assertIsNotNone(details.flag_metadata)
|
|
133
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
134
|
+
self.assertEqual(
|
|
135
|
+
details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc"
|
|
136
|
+
)
|
|
117
137
|
|
|
118
138
|
@responses.activate
|
|
119
139
|
def test_resolve_object_details(self):
|
|
@@ -134,3 +154,8 @@ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
|
|
|
134
154
|
self.assertIsNotNone(details)
|
|
135
155
|
self.assertEqual(details.value, expected_value)
|
|
136
156
|
self.assertEqual(details.reason, Reason.TARGETING_MATCH)
|
|
157
|
+
self.assertIsNotNone(details.flag_metadata)
|
|
158
|
+
self.assertEqual(details.flag_metadata["evalReasonDetails"], "All Users")
|
|
159
|
+
self.assertEqual(
|
|
160
|
+
details.flag_metadata["evalReasonTargetId"], "63125321d31c601f992288bc"
|
|
161
|
+
)
|
test/test_cloud_client.py
CHANGED
|
@@ -5,6 +5,7 @@ import uuid
|
|
|
5
5
|
from time import time
|
|
6
6
|
from unittest.mock import patch
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
from devcycle_python_sdk import DevCycleCloudClient, DevCycleCloudOptions
|
|
9
10
|
from devcycle_python_sdk.models.eval_hook import EvalHook
|
|
10
11
|
from devcycle_python_sdk.models.user import DevCycleUser
|
|
@@ -97,6 +98,8 @@ class DevCycleCloudClientTest(unittest.TestCase):
|
|
|
97
98
|
self.assertIsNotNone(result)
|
|
98
99
|
self.assertEqual(result.value, "default_value")
|
|
99
100
|
self.assertTrue(result.isDefaulted)
|
|
101
|
+
self.assertEqual(result.eval.reason, "DEFAULT")
|
|
102
|
+
self.assertEqual(result.eval.details, "Missing Variable")
|
|
100
103
|
|
|
101
104
|
# other exception - return default
|
|
102
105
|
mock_variable_call.reset_mock()
|
|
@@ -105,6 +108,8 @@ class DevCycleCloudClientTest(unittest.TestCase):
|
|
|
105
108
|
self.assertIsNotNone(result)
|
|
106
109
|
self.assertEqual(result.value, "default_value")
|
|
107
110
|
self.assertTrue(result.isDefaulted)
|
|
111
|
+
self.assertEqual(result.eval.reason, "DEFAULT")
|
|
112
|
+
self.assertEqual(result.eval.details, "Error")
|
|
108
113
|
|
|
109
114
|
@patch("devcycle_python_sdk.api.bucketing_client.BucketingAPIClient.variable")
|
|
110
115
|
def test_variable_type_mismatch(self, mock_variable_call):
|
|
@@ -116,6 +121,8 @@ class DevCycleCloudClientTest(unittest.TestCase):
|
|
|
116
121
|
self.assertIsNotNone(result)
|
|
117
122
|
self.assertEqual(result.value, "default_value")
|
|
118
123
|
self.assertTrue(result.isDefaulted)
|
|
124
|
+
self.assertEqual(result.eval.reason, "DEFAULT")
|
|
125
|
+
self.assertEqual(result.eval.details, "Variable Type Mismatch")
|
|
119
126
|
|
|
120
127
|
@patch("devcycle_python_sdk.api.bucketing_client.BucketingAPIClient.variable")
|
|
121
128
|
def test_variable_value_defaults(self, mock_variable_call):
|
test/test_local_client.py
CHANGED
|
@@ -10,6 +10,7 @@ from devcycle_python_sdk import DevCycleLocalClient, DevCycleLocalOptions
|
|
|
10
10
|
from devcycle_python_sdk.local_client import _validate_user, _validate_sdk_key
|
|
11
11
|
from devcycle_python_sdk.exceptions import MalformedConfigError
|
|
12
12
|
from devcycle_python_sdk.models.eval_hook import EvalHook
|
|
13
|
+
from devcycle_python_sdk.models.eval_reason import EvalReason
|
|
13
14
|
from devcycle_python_sdk.models.event import DevCycleEvent
|
|
14
15
|
from devcycle_python_sdk.models.feature import Feature
|
|
15
16
|
from devcycle_python_sdk.api.local_bucketing import LocalBucketing
|
|
@@ -208,6 +209,8 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
208
209
|
self.assertEqual(result.defaultValue, test_value)
|
|
209
210
|
self.assertEqual(result.value, test_value)
|
|
210
211
|
self.assertEqual(result.type, value_type)
|
|
212
|
+
self.assertEqual(result.eval.reason, "DEFAULT")
|
|
213
|
+
self.assertEqual(result.eval.details, "User Not Targeted")
|
|
211
214
|
|
|
212
215
|
@responses.activate
|
|
213
216
|
def test_variable_with_bucketing(self):
|
|
@@ -241,6 +244,9 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
241
244
|
self.assertDictEqual(result.value, expected, msg="Test key: " + key)
|
|
242
245
|
else:
|
|
243
246
|
self.assertEqual(result.value, expected, msg="Test key: " + key)
|
|
247
|
+
self.assertEqual(result.eval.reason, "TARGETING_MATCH")
|
|
248
|
+
self.assertEqual(result.eval.details, "All Users")
|
|
249
|
+
self.assertEqual(result.eval.target_id, "63125321d31c601f992288bc")
|
|
244
250
|
|
|
245
251
|
@responses.activate
|
|
246
252
|
def test_variable_with_events(self):
|
|
@@ -294,7 +300,11 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
294
300
|
user = DevCycleUser(user_id="1234")
|
|
295
301
|
|
|
296
302
|
result = self.client.all_variables(user)
|
|
297
|
-
|
|
303
|
+
expected_eval = EvalReason(
|
|
304
|
+
reason="TARGETING_MATCH",
|
|
305
|
+
details="All Users",
|
|
306
|
+
target_id="63125321d31c601f992288bc",
|
|
307
|
+
)
|
|
298
308
|
expected_variables = {
|
|
299
309
|
"a-cool-new-feature": Variable(
|
|
300
310
|
_id="62fbf6566f1ba302829f9e34",
|
|
@@ -304,6 +314,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
304
314
|
isDefaulted=None,
|
|
305
315
|
defaultValue=None,
|
|
306
316
|
evalReason=None,
|
|
317
|
+
eval=expected_eval,
|
|
307
318
|
),
|
|
308
319
|
"string-var": Variable(
|
|
309
320
|
_id="63125320a4719939fd57cb2b",
|
|
@@ -313,6 +324,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
313
324
|
isDefaulted=None,
|
|
314
325
|
defaultValue=None,
|
|
315
326
|
evalReason=None,
|
|
327
|
+
eval=expected_eval,
|
|
316
328
|
),
|
|
317
329
|
"json-var": Variable(
|
|
318
330
|
_id="64372363125123fca69d3f7b",
|
|
@@ -326,6 +338,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
326
338
|
isDefaulted=None,
|
|
327
339
|
defaultValue=None,
|
|
328
340
|
evalReason=None,
|
|
341
|
+
eval=expected_eval,
|
|
329
342
|
),
|
|
330
343
|
"num-var": Variable(
|
|
331
344
|
_id="65272363125123fca69d3a7d",
|
|
@@ -335,6 +348,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
335
348
|
isDefaulted=None,
|
|
336
349
|
defaultValue=None,
|
|
337
350
|
evalReason=None,
|
|
351
|
+
eval=expected_eval,
|
|
338
352
|
),
|
|
339
353
|
"float-var": Variable(
|
|
340
354
|
_id="61200363125123fca69d3a7a",
|
|
@@ -344,6 +358,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
|
|
|
344
358
|
isDefaulted=None,
|
|
345
359
|
defaultValue=None,
|
|
346
360
|
evalReason=None,
|
|
361
|
+
eval=expected_eval,
|
|
347
362
|
),
|
|
348
363
|
}
|
|
349
364
|
self.assertEqual(result, expected_variables)
|
{devcycle_python_server_sdk-3.11.4.dist-info → devcycle_python_server_sdk-3.12.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|