Skip to content

Commit f4420a1

Browse files
committed
feat: add config metadata to eval hooks for local bucketing
1 parent 4486168 commit f4420a1

13 files changed

+171
-31
lines changed

devcycle_python_sdk/api/local_bucketing.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from devcycle_python_sdk.models.user import DevCycleUser
2929
from devcycle_python_sdk.models.variable import Variable, determine_variable_type
3030
from devcycle_python_sdk.models.event import FlushPayload
31+
from devcycle_python_sdk.models.config_metadata import ConfigMetadata
3132

3233
logger = logging.getLogger(__name__)
3334

@@ -140,6 +141,7 @@ def __console_log_func(message_ptr) -> None:
140141
"generateBucketedConfigForUserUTF8"
141142
)
142143
self.VariableForUserProtobuf = self._get_export("variableForUser_PB")
144+
self.getConfigMetadata = self._get_export("getConfigMetadata")
143145

144146
# Extract variable type enum values from WASM
145147
self.variable_type_map = {
@@ -357,6 +359,14 @@ def store_config(self, config_json: str) -> None:
357359
config_addr = self._new_assembly_script_byte_array(data)
358360
self.setConfigDataUTF8(self.wasm_store, self.sdk_key_addr, config_addr)
359361

362+
def get_config_metadata(self) -> dict:
363+
with self.wasm_lock:
364+
config_addr = self.getConfigMetadata(self.wasm_store, self.sdk_key_addr)
365+
config_bytes = self._read_assembly_script_string(config_addr)
366+
config_data = json.loads(config_bytes.encode("utf-8"))
367+
368+
return ConfigMetadata.from_json(config_data)
369+
360370
def set_platform_data(self, platform_json: str) -> None:
361371
with self.wasm_lock:
362372
data = platform_json.encode("utf-8")
1.47 KB
Binary file not shown.

devcycle_python_sdk/local_client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ def variable(self, user: DevCycleUser, key: str, default_value: Any) -> Variable
150150
key, default_value, DefaultReasonDetails.MISSING_CONFIG
151151
)
152152

153-
context = HookContext(key, user, default_value)
153+
config_metadata = self.local_bucketing.get_config_metadata()
154+
155+
context = HookContext(key, user, default_value, config_metadata)
154156
variable = Variable.create_default_variable(
155157
key=key, default_value=default_value
156158
)

devcycle_python_sdk/managers/config_manager.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def _get_config(self, last_modified: Optional[float] = None):
108108
)
109109
self._polling_enabled = False
110110

111+
def get_config_metadata(self) -> dict:
112+
return self._local_bucketing.get_config_metadata()
113+
111114
def run(self):
112115
while self._polling_enabled:
113116
try:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from devcycle_python_sdk.models.environment_metadata import EnvironmentMetadata
2+
from devcycle_python_sdk.models.project_metadata import ProjectMetadata
3+
import json
4+
5+
6+
class ConfigMetadata:
7+
def __init__(
8+
self,
9+
project: ProjectMetadata,
10+
environment: EnvironmentMetadata,
11+
):
12+
self.project = project
13+
self.environment = environment
14+
15+
def to_json(self) -> str:
16+
return json.dumps(self, default=lambda o: o.__dict__)
17+
18+
@staticmethod
19+
def from_json(json_str: str) -> "ConfigMetadata":
20+
if json_str is None:
21+
return None
22+
return ConfigMetadata(
23+
project=ProjectMetadata.from_json(json_str["project"]),
24+
environment=EnvironmentMetadata.from_json(json_str["environment"]),
25+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class EnvironmentMetadata:
2+
def __init__(
3+
self,
4+
id: str,
5+
key: str,
6+
):
7+
self.id = id
8+
self.key = key
9+
10+
@staticmethod
11+
def from_json(json_str: str) -> "EnvironmentMetadata":
12+
if json_str is None:
13+
return None
14+
return EnvironmentMetadata(
15+
id=json_str["id"],
16+
key=json_str["key"],
17+
)
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from typing import Any
22

33
from devcycle_python_sdk.models.user import DevCycleUser
4-
4+
from devcycle_python_sdk.models.config_metadata import ConfigMetadata
55

66
class HookContext:
7-
def __init__(self, key: str, user: DevCycleUser, default_value: Any):
7+
def __init__(self, key: str, user: DevCycleUser, default_value: Any, config_metadata: ConfigMetadata = None):
88
self.key = key
99
self.default_value = default_value
1010
self.user = user
11+
self.config_metadata = config_metadata
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class ProjectMetadata:
2+
def __init__(
3+
self,
4+
id: str,
5+
key: str,
6+
):
7+
self.id = id
8+
self.key = key
9+
10+
@staticmethod
11+
def from_json(json_str: str) -> "ProjectMetadata":
12+
if json_str is None:
13+
return None
14+
return ProjectMetadata(
15+
id=json_str["id"],
16+
key=json_str["key"],
17+
)

devcycle_python_sdk/protobuf/variableForUserParams_pb2.py

Lines changed: 6 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

devcycle_python_sdk/protobuf/variableForUserParams_pb2.pyi

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Opti
77
DESCRIPTOR: _descriptor.FileDescriptor
88

99
class VariableType_PB(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
10-
__slots__ = ()
10+
__slots__ = []
1111
Boolean: _ClassVar[VariableType_PB]
1212
Number: _ClassVar[VariableType_PB]
1313
String: _ClassVar[VariableType_PB]
1414
JSON: _ClassVar[VariableType_PB]
1515

1616
class CustomDataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
17-
__slots__ = ()
17+
__slots__ = []
1818
Bool: _ClassVar[CustomDataType]
1919
Num: _ClassVar[CustomDataType]
2020
Str: _ClassVar[CustomDataType]
@@ -29,23 +29,23 @@ Str: CustomDataType
2929
Null: CustomDataType
3030

3131
class NullableString(_message.Message):
32-
__slots__ = ("value", "isNull")
32+
__slots__ = ["value", "isNull"]
3333
VALUE_FIELD_NUMBER: _ClassVar[int]
3434
ISNULL_FIELD_NUMBER: _ClassVar[int]
3535
value: str
3636
isNull: bool
3737
def __init__(self, value: _Optional[str] = ..., isNull: bool = ...) -> None: ...
3838

3939
class NullableDouble(_message.Message):
40-
__slots__ = ("value", "isNull")
40+
__slots__ = ["value", "isNull"]
4141
VALUE_FIELD_NUMBER: _ClassVar[int]
4242
ISNULL_FIELD_NUMBER: _ClassVar[int]
4343
value: float
4444
isNull: bool
4545
def __init__(self, value: _Optional[float] = ..., isNull: bool = ...) -> None: ...
4646

4747
class CustomDataValue(_message.Message):
48-
__slots__ = ("type", "boolValue", "doubleValue", "stringValue")
48+
__slots__ = ["type", "boolValue", "doubleValue", "stringValue"]
4949
TYPE_FIELD_NUMBER: _ClassVar[int]
5050
BOOLVALUE_FIELD_NUMBER: _ClassVar[int]
5151
DOUBLEVALUE_FIELD_NUMBER: _ClassVar[int]
@@ -57,9 +57,9 @@ class CustomDataValue(_message.Message):
5757
def __init__(self, type: _Optional[_Union[CustomDataType, str]] = ..., boolValue: bool = ..., doubleValue: _Optional[float] = ..., stringValue: _Optional[str] = ...) -> None: ...
5858

5959
class NullableCustomData(_message.Message):
60-
__slots__ = ("value", "isNull")
60+
__slots__ = ["value", "isNull"]
6161
class ValueEntry(_message.Message):
62-
__slots__ = ("key", "value")
62+
__slots__ = ["key", "value"]
6363
KEY_FIELD_NUMBER: _ClassVar[int]
6464
VALUE_FIELD_NUMBER: _ClassVar[int]
6565
key: str
@@ -72,7 +72,7 @@ class NullableCustomData(_message.Message):
7272
def __init__(self, value: _Optional[_Mapping[str, CustomDataValue]] = ..., isNull: bool = ...) -> None: ...
7373

7474
class VariableForUserParams_PB(_message.Message):
75-
__slots__ = ("sdkKey", "variableKey", "variableType", "user", "shouldTrackEvent")
75+
__slots__ = ["sdkKey", "variableKey", "variableType", "user", "shouldTrackEvent"]
7676
SDKKEY_FIELD_NUMBER: _ClassVar[int]
7777
VARIABLEKEY_FIELD_NUMBER: _ClassVar[int]
7878
VARIABLETYPE_FIELD_NUMBER: _ClassVar[int]
@@ -86,7 +86,7 @@ class VariableForUserParams_PB(_message.Message):
8686
def __init__(self, sdkKey: _Optional[str] = ..., variableKey: _Optional[str] = ..., variableType: _Optional[_Union[VariableType_PB, str]] = ..., user: _Optional[_Union[DVCUser_PB, _Mapping]] = ..., shouldTrackEvent: bool = ...) -> None: ...
8787

8888
class DVCUser_PB(_message.Message):
89-
__slots__ = ("user_id", "email", "name", "language", "country", "appBuild", "appVersion", "deviceModel", "customData", "privateCustomData")
89+
__slots__ = ["user_id", "email", "name", "language", "country", "appBuild", "appVersion", "deviceModel", "customData", "privateCustomData"]
9090
USER_ID_FIELD_NUMBER: _ClassVar[int]
9191
EMAIL_FIELD_NUMBER: _ClassVar[int]
9292
NAME_FIELD_NUMBER: _ClassVar[int]
@@ -110,7 +110,7 @@ class DVCUser_PB(_message.Message):
110110
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: ...
111111

112112
class SDKVariable_PB(_message.Message):
113-
__slots__ = ("_id", "type", "key", "boolValue", "doubleValue", "stringValue", "evalReason", "_feature", "eval")
113+
__slots__ = ["_id", "type", "key", "boolValue", "doubleValue", "stringValue", "evalReason", "_feature", "eval"]
114114
_ID_FIELD_NUMBER: _ClassVar[int]
115115
TYPE_FIELD_NUMBER: _ClassVar[int]
116116
KEY_FIELD_NUMBER: _ClassVar[int]
@@ -132,7 +132,7 @@ class SDKVariable_PB(_message.Message):
132132
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: ...
133133

134134
class EvalReason_PB(_message.Message):
135-
__slots__ = ("reason", "details", "target_id")
135+
__slots__ = ["reason", "details", "target_id"]
136136
REASON_FIELD_NUMBER: _ClassVar[int]
137137
DETAILS_FIELD_NUMBER: _ClassVar[int]
138138
TARGET_ID_FIELD_NUMBER: _ClassVar[int]

0 commit comments

Comments
 (0)