Skip to content

Commit 9d21d93

Browse files
committed
add all root documents + tests
1 parent 7c45ffb commit 9d21d93

File tree

7 files changed

+684
-280
lines changed

7 files changed

+684
-280
lines changed

src/flagsmith_models/__init__.py

Lines changed: 96 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
The schemas are written to DynamoDB documents by Core, and read by Edge.
44
"""
55

6-
from datetime import datetime
76
from typing import Literal, TypeAlias
8-
from uuid import UUID
97

108
from typing_extensions import NotRequired, TypedDict
119

10+
from flagsmith_models.types import DateTimeStr, UUIDStr
11+
1212
FeatureType = Literal["STANDARD", "MULTIVARIATE"]
1313
"""Represents the type of a Flagsmith feature. Multivariate features include multiple weighted values."""
1414

@@ -79,7 +79,7 @@ class MultivariateFeatureStateValue(TypedDict):
7979

8080
id: NotRequired[int | None]
8181
"""Unique identifier for the multivariate feature state value in Core. TODO: document why and when this can be `None`."""
82-
mv_fs_value_uuid: NotRequired[UUID]
82+
mv_fs_value_uuid: NotRequired[UUIDStr]
8383
"""The UUID for this multivariate feature state value. Should be used if `id` is `None`."""
8484
percentage_allocation: float
8585
"""The percentage allocation for this multivariate feature state value. Should be between or equal to 0 and 100."""
@@ -105,7 +105,7 @@ class FeatureState(TypedDict):
105105
"""The value for this feature state."""
106106
django_id: NotRequired[int | None]
107107
"""Unique identifier for the feature state in Core. TODO: document why and when this can be `None`."""
108-
featurestate_uuid: NotRequired[UUID]
108+
featurestate_uuid: NotRequired[UUIDStr]
109109
"""The UUID for this feature state. Should be used if `django_id` is `None`. If not set, should be generated."""
110110
feature_segment: NotRequired[FeatureSegment | None]
111111
"""Segment override data, if this feature state is for a segment override."""
@@ -193,7 +193,7 @@ class Project(TypedDict):
193193
"""List of feature IDs that are skipped when the SDK API serves flags for a public client-side key."""
194194
enable_realtime_updates: NotRequired[bool]
195195
"""Whether the SDK API should use real-time updates. Defaults to `False`. Not currently used neither by SDK APIs nor by SDKs themselves."""
196-
hide_disabled_flags: NotRequired[bool]
196+
hide_disabled_flags: NotRequired[bool | None]
197197
"""Whether the SDK API should hide disabled flags for this project. Defaults to `False`."""
198198

199199

@@ -222,43 +222,89 @@ class Webhook(TypedDict):
222222
"""Secret used to sign webhook payloads."""
223223

224224

225+
class _EnvironmentFields(TypedDict):
226+
"""Common fields for Environment documents."""
227+
228+
name: NotRequired[str]
229+
"""Environment name. TODO: Can we drop NotRequired and adjust test data?"""
230+
updated_at: NotRequired[DateTimeStr | None]
231+
"""Last updated timestamp. If not set, current timestamp should be assumed."""
232+
233+
project: Project
234+
"""Project-specific data for this environment."""
235+
feature_states: list[FeatureState]
236+
"""List of feature states representing the environment defaults."""
237+
238+
allow_client_traits: NotRequired[bool]
239+
"""Whether the SDK API should allow clients to set traits for this environment. Identical to project-level's `persist_trait_data` setting. Defaults to `True`."""
240+
hide_sensitive_data: NotRequired[bool]
241+
"""Whether the SDK API should hide sensitive data for this environment. Defaults to `False`."""
242+
hide_disabled_flags: NotRequired[bool | None]
243+
"""Whether the SDK API should hide disabled flags for this environment. If `None`, the SDK API should fall back to project-level setting."""
244+
use_identity_composite_key_for_hashing: NotRequired[bool]
245+
"""Whether the SDK API should set `$.identity.key` in engine evaluation context to identity's composite key. Defaults to `False`."""
246+
use_identity_overrides_in_local_eval: NotRequired[bool]
247+
"""Whether the SDK API should return identity overrides as part of the environment document. Defaults to `False`."""
248+
249+
amplitude_config: NotRequired[Integration | None]
250+
"""Amplitude integration configuration."""
251+
dynatrace_config: NotRequired[DynatraceIntegration | None]
252+
"""Dynatrace integration configuration."""
253+
heap_config: NotRequired[Integration | None]
254+
"""Heap integration configuration."""
255+
mixpanel_config: NotRequired[Integration | None]
256+
"""Mixpanel integration configuration."""
257+
rudderstack_config: NotRequired[Integration | None]
258+
"""RudderStack integration configuration."""
259+
segment_config: NotRequired[Integration | None]
260+
"""Segment integration configuration."""
261+
webhook_config: NotRequired[Webhook | None]
262+
"""Webhook configuration."""
263+
264+
225265
### Root document schemas below. Indexed fields are marked as **INDEXED** in the docstrings. ###
226266

227267

228268
class EnvironmentAPIKey(TypedDict):
229-
"""Represents a server-side API key for a Flagsmith environment."""
269+
"""Represents a server-side API key for a Flagsmith environment.
270+
271+
**DynamoDB table**: `flagsmith_environment_api_key`
272+
"""
230273

231274
id: int
232275
"""Unique identifier for the environment API key in Core. **INDEXED**."""
233276
key: str
234277
"""The server-side API key string, e.g. `"ser.xxxxxxxxxxxxx"`. **INDEXED**."""
235-
created_at: datetime
278+
created_at: DateTimeStr
236279
"""Creation timestamp."""
237280
name: str
238281
"""Name of the API key."""
239282
client_api_key: str
240283
"""The corresponding public client-side API key."""
241-
expires_at: NotRequired[datetime | None]
284+
expires_at: NotRequired[DateTimeStr | None]
242285
"""Expiration timestamp. If `None`, the key does not expire."""
243286
active: bool
244287
"""Whether the key is active. Defaults to `True`."""
245288

246289

247290
class Identity(TypedDict):
248-
"""Represents a Flagsmith identity within an environment. Carries traits and feature overrides."""
291+
"""Represents a Flagsmith identity within an environment. Carries traits and feature overrides.
292+
293+
**DynamoDB table**: `flagsmith_identities`
294+
"""
249295

250296
identifier: str
251297
"""Unique identifier for the identity. **INDEXED**."""
252298
environment_api_key: str
253299
"""API key of the environment this identity belongs to. Used to scope the identity within a specific environment. **INDEXED**."""
254-
identity_uuid: UUID
300+
identity_uuid: UUIDStr
255301
"""The UUID for this identity. **INDEXED**."""
256302
composite_key: str
257303
"""A composite key combining the environment and identifier. **INDEXED**.
258304
259305
Generated as: `{environment_api_key}_{identifier}`.
260306
"""
261-
created_date: datetime
307+
created_date: DateTimeStr
262308
"""Creation timestamp."""
263309
identity_features: NotRequired[list[FeatureState]]
264310
"""List of identity overrides for this identity."""
@@ -268,45 +314,50 @@ class Identity(TypedDict):
268314
"""Unique identifier for the identity in Core. TODO: document why and when this can be `None`."""
269315

270316

271-
class Environment(TypedDict):
272-
"""Represents a Flagsmith environment. Carries all necessary data for flag evaluation within the environment."""
317+
class Environment(_EnvironmentFields):
318+
"""Represents a Flagsmith environment. Carries all necessary data for flag evaluation within the environment.
319+
320+
**DynamoDB table**: `flagsmith_environments`
321+
"""
273322

274323
id: int
275324
"""Unique identifier for the environment in Core. **INDEXED**."""
276325
api_key: str
277326
"""Public client-side API key for the environment. **INDEXED**."""
278-
name: NotRequired[str]
279-
"""Environment name. TODO: Can we drop NotRequired and adjust test data?"""
280-
updated_at: NotRequired[datetime | None]
281-
"""Last updated timestamp. If not set, current timestamp should be assumed."""
282327

283-
project: Project
284-
"""Project-specific data for this environment."""
285-
feature_states: list[FeatureState]
286-
"""List of feature states representing the environment defaults."""
287328

288-
allow_client_traits: NotRequired[bool]
289-
"""Whether the SDK API should allow clients to set traits for this environment. Identical to project-level's `persist_trait_data` setting. Defaults to `True`."""
290-
hide_sensitive_data: NotRequired[bool]
291-
"""Whether the SDK API should hide sensitive data for this environment. Defaults to `False`."""
292-
hide_disabled_flags: NotRequired[bool]
293-
"""Whether the SDK API should hide disabled flags for this environment. If `None`, the SDK API should fall back to project-level setting."""
294-
use_identity_composite_key_for_hashing: NotRequired[bool]
295-
"""Whether the SDK API should set `$.identity.key` in engine evaluation context to identity's composite key. Defaults to `False`."""
296-
use_identity_overrides_in_local_eval: NotRequired[bool]
297-
"""Whether the SDK API should return identity overrides as part of the environment document. Defaults to `False`."""
329+
class EnvironmentV2Meta(_EnvironmentFields):
330+
"""Represents a Flagsmith environment. Carries all necessary data for flag evaluation within the environment.
298331
299-
amplitude_config: NotRequired[Integration]
300-
"""Amplitude integration configuration."""
301-
dynatrace_config: NotRequired[DynatraceIntegration]
302-
"""Dynatrace integration configuration."""
303-
heap_config: NotRequired[Integration]
304-
"""Heap integration configuration."""
305-
mixpanel_config: NotRequired[Integration]
306-
"""Mixpanel integration configuration."""
307-
rudderstack_config: NotRequired[Integration]
308-
"""RudderStack integration configuration."""
309-
segment_config: NotRequired[Integration]
310-
"""Segment integration configuration."""
311-
webhook_config: NotRequired[Webhook]
312-
"""Webhook configuration."""
332+
**DynamoDB table**: `flagsmith_environments_v2`
333+
"""
334+
335+
environment_id: str
336+
"""Unique identifier for the environment in Core. **INDEXED**."""
337+
environment_api_key: str
338+
"""Public client-side API key for the environment. **INDEXED**."""
339+
document_key: Literal["_META"]
340+
"""The fixed document key for the environment v2 document. Always `"_META"`. **INDEXED**."""
341+
342+
id: int
343+
"""Unique identifier for the environment in Core. Exists for compatibility with the API environment document schema."""
344+
345+
346+
class EnvironmentV2IdentityOverride(TypedDict):
347+
"""Represents an identity override.
348+
349+
**DynamoDB table**: `flagsmith_environments_v2`
350+
"""
351+
352+
environment_id: str
353+
"""Unique identifier for the environment in Core. **INDEXED**."""
354+
document_key: str
355+
"""The document key for this identity override, formatted as `identity_override:{feature Core ID}:{identity UUID}`. **INDEXED**."""
356+
environment_api_key: str
357+
"""Public client-side API key for the environment. **INDEXED**."""
358+
identifier: str
359+
"""Unique identifier for the identity. **INDEXED**."""
360+
identity_uuid: str
361+
"""The UUID for this identity. **INDEXED**."""
362+
feature_state: FeatureState
363+
"""The feature state override for this identity."""

src/flagsmith_models/types.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from typing import TypeAlias
2+
3+
UUIDStr: TypeAlias = str
4+
"""A string representing a UUID."""
5+
6+
DateTimeStr: TypeAlias = str
7+
"""A string representing a date and time in ISO 8601 format."""
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"key": "ser.ZSwVCQrCGpXXKdvVsVxoie",
3+
"active": true,
4+
"client_api_key": "pQuzvsMLQoOVAwITrTWDQJ",
5+
"created_at": "2023-04-21T13:11:13.913178+00:00",
6+
"expires_at": null,
7+
"id": 907,
8+
"name": "TestKey"
9+
}

tests/integration/flagsmith_models/data/environment.json renamed to tests/integration/flagsmith_models/data/flagsmith_environments.json

File renamed without changes.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{
2+
"environment_id": "49268",
3+
"document_key": "_META",
4+
"allow_client_traits": true,
5+
"amplitude_config": null,
6+
"dynatrace_config": null,
7+
"environment_api_key": "AQ9T6LixPqYMJkuqGJy3t2",
8+
"feature_states": [
9+
{
10+
"django_id": 577621,
11+
"enabled": true,
12+
"feature": {
13+
"id": 100298,
14+
"name": "test_feature",
15+
"type": "MULTIVARIATE"
16+
},
17+
"featurestate_uuid": "42d7805e-a9ac-401c-a7b7-d6583ac5a365",
18+
"feature_segment": null,
19+
"feature_state_value": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
20+
"multivariate_feature_state_values": [
21+
{
22+
"id": 185130,
23+
"multivariate_feature_option": {
24+
"id": 20919,
25+
"value": "second"
26+
},
27+
"mv_fs_value_uuid": "0b02ce41-9965-4c61-8b96-c8d76e3d4a27",
28+
"percentage_allocation": 10
29+
},
30+
{
31+
"id": 48717,
32+
"multivariate_feature_option": {
33+
"id": 14004,
34+
"value": true
35+
},
36+
"mv_fs_value_uuid": "cb05f49c-de1f-44f1-87eb-c3b55d473063",
37+
"percentage_allocation": 30
38+
}
39+
]
40+
},
41+
{
42+
"django_id": 1041292,
43+
"enabled": false,
44+
"feature": {
45+
"id": 172422,
46+
"name": "feature",
47+
"type": "STANDARD"
48+
},
49+
"featurestate_uuid": "58b7b954-1b75-493a-82df-5be0efeedd2a",
50+
"feature_segment": null,
51+
"feature_state_value": 3,
52+
"multivariate_feature_state_values": []
53+
}
54+
],
55+
"heap_config": null,
56+
"hide_disabled_flags": null,
57+
"hide_sensitive_data": false,
58+
"id": 49268,
59+
"identity_overrides": [],
60+
"mixpanel_config": null,
61+
"name": "Development",
62+
"project": {
63+
"enable_realtime_updates": false,
64+
"hide_disabled_flags": false,
65+
"id": 19368,
66+
"name": "Example Project",
67+
"organisation": {
68+
"feature_analytics": false,
69+
"id": 13,
70+
"name": "Flagsmith",
71+
"persist_trait_data": true,
72+
"stop_serving_flags": false
73+
},
74+
"segments": [
75+
{
76+
"feature_states": [],
77+
"id": 44126,
78+
"name": "test",
79+
"rules": [
80+
{
81+
"conditions": [],
82+
"rules": [
83+
{
84+
"conditions": [
85+
{
86+
"operator": "EQUAL",
87+
"property_": "test",
88+
"value": "test"
89+
}
90+
],
91+
"rules": [],
92+
"type": "ANY"
93+
}
94+
],
95+
"type": "ALL"
96+
}
97+
]
98+
}
99+
],
100+
"server_key_only_feature_ids": []
101+
},
102+
"rudderstack_config": null,
103+
"segment_config": null,
104+
"updated_at": "2025-11-16T13:28:31.244331+00:00",
105+
"use_identity_composite_key_for_hashing": true,
106+
"use_identity_overrides_in_local_eval": false,
107+
"webhook_config": null
108+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"composite_key": "pQuzvsMLQoOVAwITrTWDQJ_57c6edf1bbf145a1a23ea287ce44877f",
3+
"created_date": "2024-03-19T09:41:22.974595+00:00",
4+
"django_id": null,
5+
"environment_api_key": "pQuzvsMLQoOVAwITrTWDQJ",
6+
"identifier": "57c6edf1bbf145a1a23ea287ce44877f",
7+
"identity_features": [
8+
{
9+
"django_id": null,
10+
"enabled": true,
11+
"feature": {
12+
"id": 67,
13+
"name": "test_feature",
14+
"type": "STANDARD"
15+
},
16+
"featurestate_uuid": "20988957-d345-424e-9abc-1dc5a814da48",
17+
"feature_segment": null,
18+
"feature_state_value": null,
19+
"multivariate_feature_state_values": []
20+
}
21+
],
22+
"identity_traits": [
23+
{
24+
"trait_key": "test_trait",
25+
"trait_value": 42
26+
}
27+
],
28+
"identity_uuid": "118ecfc9-5234-4218-8af8-dd994dbfedc0"
29+
}

0 commit comments

Comments
 (0)