flagsmith-common 3.0.0__tar.gz → 3.1.0__tar.gz
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.
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/PKG-INFO +3 -1
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/pyproject.toml +6 -4
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/test_tools/plugin.py +12 -10
- flagsmith_common-3.1.0/src/flagsmith_schemas/constants.py +4 -0
- flagsmith_common-3.1.0/src/flagsmith_schemas/dynamodb.py +346 -0
- flagsmith_common-3.1.0/src/flagsmith_schemas/pydantic_types.py +22 -0
- flagsmith_common-3.1.0/src/flagsmith_schemas/types.py +91 -0
- flagsmith_common-3.1.0/src/flagsmith_schemas/validators.py +55 -0
- flagsmith_common-3.1.0/src/task_processor/migrations/sql/__init__.py +0 -0
- flagsmith_common-3.1.0/src/task_processor/py.typed +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/LICENSE +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/README.md +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/app.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/cli/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/cli/healthcheck.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/constants.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/logging.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/main.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/docgen.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/start.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/waitfordb.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/metrics.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/middleware.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/templates/docgen-metrics.md +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/urls.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/utils.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/views.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/environments/permissions.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/multivariate/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/multivariate/serializers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/serializers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/versioning/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/versioning/serializers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/conf.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/constants.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/logging.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/metrics.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/middleware.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/gunicorn/utils.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/migrations/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/migrations/helpers/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/migrations/helpers/postgres_helpers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/organisations/permissions.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/projects/permissions.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/prometheus/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/prometheus/utils.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/py.typed +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/test_tools/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/test_tools/types.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/test_tools/utils.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/types.py +0 -0
- {flagsmith_common-3.0.0/src/task_processor → flagsmith_common-3.1.0/src/flagsmith_schemas}/__init__.py +0 -0
- {flagsmith_common-3.0.0/src/task_processor → flagsmith_common-3.1.0/src/flagsmith_schemas}/py.typed +0 -0
- {flagsmith_common-3.0.0/src/task_processor/migrations → flagsmith_common-3.1.0/src/task_processor}/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/admin.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/apps.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/decorators.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/exceptions.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/health.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/managers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/metrics.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0001_initial.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0002_healthcheckmodel.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0003_add_completed_to_task.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0004_recreate_task_indexes.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0005_update_conditional_index_conditions.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0006_auto_20230221_0802.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0007_add_is_locked.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0008_add_get_task_to_process_function.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0009_add_recurring_task_run_first_run_at.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0010_task_priority.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0011_add_priority_to_get_tasks_to_process.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0012_add_locked_at_and_timeout.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0013_add_last_picked_at.py +0 -0
- {flagsmith_common-3.0.0/src/task_processor/migrations/sql → flagsmith_common-3.1.0/src/task_processor/migrations}/__init__.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/sql/0008_get_recurring_tasks_to_process.sql +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/sql/0008_get_tasks_to_process.sql +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/sql/0011_get_tasks_to_process.sql +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/sql/0012_get_recurringtasks_to_process.sql +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/sql/0013_get_recurringtasks_to_process.sql +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/models.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/monitoring.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/processor.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/routers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/serializers.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/task_registry.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/task_run_method.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/tasks.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/threads.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/types.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/urls.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/utils.py +0 -0
- {flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flagsmith-common
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Flagsmith's common library
|
|
5
5
|
Author: Matthew Elwell, Gagan Trivedi, Kim Gustyr, Zach Aysan, Francesco Lo Franco, Rodrigo López Dato, Evandro Myller, Wadii Zaim
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -21,6 +21,7 @@ Requires-Dist: prometheus-client>=0.0.16 ; extra == 'common-core'
|
|
|
21
21
|
Requires-Dist: psycopg2-binary>=2.9,<3 ; extra == 'common-core'
|
|
22
22
|
Requires-Dist: requests ; extra == 'common-core'
|
|
23
23
|
Requires-Dist: simplejson>=3,<4 ; extra == 'common-core'
|
|
24
|
+
Requires-Dist: typing-extensions ; extra == 'flagsmith-schemas'
|
|
24
25
|
Requires-Dist: backoff>=2.2.1,<3.0.0 ; extra == 'task-processor'
|
|
25
26
|
Requires-Dist: django>4,<6 ; extra == 'task-processor'
|
|
26
27
|
Requires-Dist: django-health-check ; extra == 'task-processor'
|
|
@@ -36,6 +37,7 @@ Project-URL: Homepage, https://flagsmith.com
|
|
|
36
37
|
Project-URL: Issues, https://github.com/flagsmith/flagsmith-common/issues
|
|
37
38
|
Project-URL: Repository, https://github.com/flagsmith/flagsmith-common
|
|
38
39
|
Provides-Extra: common-core
|
|
40
|
+
Provides-Extra: flagsmith-schemas
|
|
39
41
|
Provides-Extra: task-processor
|
|
40
42
|
Provides-Extra: test-tools
|
|
41
43
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flagsmith-common"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.1.0"
|
|
4
4
|
description = "Flagsmith's common library"
|
|
5
5
|
requires-python = ">=3.11,<4.0"
|
|
6
6
|
dependencies = []
|
|
@@ -20,12 +20,13 @@ optional-dependencies = { test-tools = [
|
|
|
20
20
|
"psycopg2-binary (>=2.9,<3)",
|
|
21
21
|
"requests",
|
|
22
22
|
"simplejson (>=3,<4)",
|
|
23
|
-
|
|
24
23
|
], task-processor = [
|
|
25
24
|
"backoff (>=2.2.1,<3.0.0)",
|
|
26
25
|
"django (>4,<6)",
|
|
27
26
|
"django-health-check",
|
|
28
27
|
"prometheus-client (>=0.0.16)",
|
|
28
|
+
], flagsmith-schemas = [
|
|
29
|
+
"typing_extensions",
|
|
29
30
|
] }
|
|
30
31
|
authors = [
|
|
31
32
|
{ name = "Matthew Elwell" },
|
|
@@ -68,6 +69,7 @@ dev = [
|
|
|
68
69
|
"djangorestframework-stubs (>=3.15.3, <4.0.0)",
|
|
69
70
|
"mypy (>=1.15.0, <2.0.0)",
|
|
70
71
|
"pre-commit",
|
|
72
|
+
"pydantic>=2.12.5",
|
|
71
73
|
"pyfakefs (>=5.7.4, <6.0.0)",
|
|
72
74
|
"pytest (>=8.3.4, <9.0.0)",
|
|
73
75
|
"pytest-asyncio (>=0.25.3, <1.0.0)",
|
|
@@ -77,8 +79,8 @@ dev = [
|
|
|
77
79
|
"pytest-httpserver (>=1.1.3, <2.0.0)",
|
|
78
80
|
"pytest-mock (>=3.14.0, <4.0.0)",
|
|
79
81
|
"setuptools (>=78.1.1, <79.0.0)",
|
|
80
|
-
"types-simplejson (>=3.20.0.20250326, <4.0.0)",
|
|
81
82
|
"types-python-dateutil (>=2.9.0.20250516, <3.0.0)",
|
|
83
|
+
"types-simplejson (>=3.20.0.20250326, <4.0.0)",
|
|
82
84
|
]
|
|
83
85
|
|
|
84
86
|
[build-system]
|
|
@@ -86,7 +88,7 @@ requires = ["uv_build>=0.9.17,<0.10.0"]
|
|
|
86
88
|
build-backend = "uv_build"
|
|
87
89
|
|
|
88
90
|
[tool.uv.build-backend]
|
|
89
|
-
module-name = ["common", "task_processor"]
|
|
91
|
+
module-name = ["common", "task_processor", "flagsmith_schemas"]
|
|
90
92
|
|
|
91
93
|
[tool.coverage.report]
|
|
92
94
|
# Regexes for lines to exclude from consideration
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
from functools import partial
|
|
2
|
-
from typing import Generator
|
|
2
|
+
from typing import TYPE_CHECKING, Generator
|
|
3
3
|
|
|
4
|
-
import prometheus_client
|
|
5
4
|
import pytest
|
|
6
|
-
from prometheus_client.metrics import MetricWrapperBase
|
|
7
|
-
from pyfakefs.fake_filesystem import FakeFilesystem
|
|
8
|
-
from pytest_django.fixtures import SettingsWrapper
|
|
9
5
|
|
|
10
6
|
from common.test_tools.types import (
|
|
11
7
|
AssertMetricFixture,
|
|
@@ -15,6 +11,10 @@ from common.test_tools.types import (
|
|
|
15
11
|
)
|
|
16
12
|
from task_processor.task_run_method import TaskRunMethod
|
|
17
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from pyfakefs.fake_filesystem import FakeFilesystem
|
|
16
|
+
from pytest_django.fixtures import SettingsWrapper
|
|
17
|
+
|
|
18
18
|
|
|
19
19
|
def pytest_addoption(parser: pytest.Parser) -> None:
|
|
20
20
|
group = parser.getgroup("snapshot")
|
|
@@ -26,12 +26,14 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def assert_metric_impl() -> Generator[AssertMetricFixture, None, None]:
|
|
29
|
+
import prometheus_client
|
|
30
|
+
|
|
29
31
|
registry = prometheus_client.REGISTRY
|
|
30
32
|
collectors = [*registry._collector_to_names]
|
|
31
33
|
|
|
32
34
|
# Reset registry state
|
|
33
35
|
for collector in collectors:
|
|
34
|
-
if isinstance(collector, MetricWrapperBase):
|
|
36
|
+
if isinstance(collector, prometheus_client.metrics.MetricWrapperBase):
|
|
35
37
|
collector.clear()
|
|
36
38
|
|
|
37
39
|
def _assert_metric(
|
|
@@ -53,7 +55,7 @@ assert_metric = pytest.fixture(assert_metric_impl)
|
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
@pytest.fixture()
|
|
56
|
-
def saas_mode(fs: FakeFilesystem) -> Generator[None, None, None]:
|
|
58
|
+
def saas_mode(fs: "FakeFilesystem") -> Generator[None, None, None]:
|
|
57
59
|
from common.core.utils import is_saas
|
|
58
60
|
|
|
59
61
|
is_saas.cache_clear()
|
|
@@ -65,7 +67,7 @@ def saas_mode(fs: FakeFilesystem) -> Generator[None, None, None]:
|
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
@pytest.fixture()
|
|
68
|
-
def enterprise_mode(fs: FakeFilesystem) -> Generator[None, None, None]:
|
|
70
|
+
def enterprise_mode(fs: "FakeFilesystem") -> Generator[None, None, None]:
|
|
69
71
|
from common.core.utils import is_enterprise
|
|
70
72
|
|
|
71
73
|
is_enterprise.cache_clear()
|
|
@@ -77,7 +79,7 @@ def enterprise_mode(fs: FakeFilesystem) -> Generator[None, None, None]:
|
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
@pytest.fixture()
|
|
80
|
-
def task_processor_mode(settings: SettingsWrapper) -> None:
|
|
82
|
+
def task_processor_mode(settings: "SettingsWrapper") -> None:
|
|
81
83
|
settings.TASK_PROCESSOR_MODE = True
|
|
82
84
|
# The setting is supposed to be set before the metrics module is imported,
|
|
83
85
|
# so reload it
|
|
@@ -101,7 +103,7 @@ def flagsmith_markers_marked(
|
|
|
101
103
|
|
|
102
104
|
@pytest.fixture(name="run_tasks")
|
|
103
105
|
def run_tasks_impl(
|
|
104
|
-
settings: SettingsWrapper,
|
|
106
|
+
settings: "SettingsWrapper",
|
|
105
107
|
transactional_db: None,
|
|
106
108
|
task_processor_mode: None,
|
|
107
109
|
) -> RunTasksFixture:
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The types in this module describe the Edge API's data model.
|
|
3
|
+
They are used to type DynamoDB documents representing Flagsmith entities.
|
|
4
|
+
|
|
5
|
+
These types can be used with Pydantic for validation and serialization
|
|
6
|
+
when `pydantic` is installed.
|
|
7
|
+
Otherwise, they serve as documentation for the structure of the data stored in DynamoDB.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Annotated, Literal
|
|
11
|
+
|
|
12
|
+
from typing_extensions import NotRequired, TypedDict
|
|
13
|
+
|
|
14
|
+
from flagsmith_schemas.constants import PYDANTIC_INSTALLED
|
|
15
|
+
from flagsmith_schemas.types import (
|
|
16
|
+
ConditionOperator,
|
|
17
|
+
DateTimeStr,
|
|
18
|
+
DynamoContextValue,
|
|
19
|
+
DynamoFeatureValue,
|
|
20
|
+
DynamoFloat,
|
|
21
|
+
DynamoInt,
|
|
22
|
+
FeatureType,
|
|
23
|
+
RuleType,
|
|
24
|
+
UUIDStr,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if PYDANTIC_INSTALLED:
|
|
28
|
+
from flagsmith_schemas.pydantic_types import (
|
|
29
|
+
ValidateIdentityFeatureStatesList,
|
|
30
|
+
ValidateMultivariateFeatureValuesList,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Feature(TypedDict):
|
|
35
|
+
"""Represents a Flagsmith feature, defined at project level."""
|
|
36
|
+
|
|
37
|
+
id: DynamoInt
|
|
38
|
+
"""Unique identifier for the feature in Core."""
|
|
39
|
+
name: str
|
|
40
|
+
"""Name of the feature. Must be unique within a project."""
|
|
41
|
+
type: FeatureType
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MultivariateFeatureOption(TypedDict):
|
|
45
|
+
"""Represents a single multivariate feature option in the Flagsmith UI."""
|
|
46
|
+
|
|
47
|
+
id: NotRequired[DynamoInt | None]
|
|
48
|
+
"""Unique identifier for the multivariate feature option in Core. This is used by Core UI to display the selected option for an identity override for a multivariate feature."""
|
|
49
|
+
value: DynamoFeatureValue
|
|
50
|
+
"""The feature state value that should be served when this option's parent multivariate feature state is selected by the engine."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class MultivariateFeatureStateValue(TypedDict):
|
|
54
|
+
"""Represents a multivariate feature state value.
|
|
55
|
+
|
|
56
|
+
Identity overrides are meant to hold only one of these, solely to inform the UI which option is selected for the given identity.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
id: NotRequired[DynamoInt | None]
|
|
60
|
+
"""Unique identifier for the multivariate feature state value in Core. Used for multivariate bucketing. If feature state created via `edge-identities` APIs in Core, this can be missing or `None`."""
|
|
61
|
+
mv_fs_value_uuid: NotRequired[UUIDStr]
|
|
62
|
+
"""The UUID for this multivariate feature state value. Should be used for multivariate bucketing if `id` is `None`."""
|
|
63
|
+
percentage_allocation: DynamoFloat
|
|
64
|
+
"""The percentage allocation for this multivariate feature state value. Should be between or equal to 0 and 100."""
|
|
65
|
+
multivariate_feature_option: MultivariateFeatureOption
|
|
66
|
+
"""The multivariate feature option that this value corresponds to."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class FeatureSegment(TypedDict):
|
|
70
|
+
"""Represents data specific to a segment feature override."""
|
|
71
|
+
|
|
72
|
+
priority: NotRequired[DynamoInt | None]
|
|
73
|
+
"""The priority of this segment feature override. Lower numbers indicate stronger priority. If `None` or not set, the weakest priority is assumed."""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class FeatureState(TypedDict):
|
|
77
|
+
"""Used to define the state of a feature for an environment, segment overrides, and identity overrides."""
|
|
78
|
+
|
|
79
|
+
feature: Feature
|
|
80
|
+
"""The feature that this feature state is for."""
|
|
81
|
+
enabled: bool
|
|
82
|
+
"""Whether the feature is enabled or disabled."""
|
|
83
|
+
feature_state_value: DynamoFeatureValue
|
|
84
|
+
"""The value for this feature state."""
|
|
85
|
+
django_id: NotRequired[DynamoInt | None]
|
|
86
|
+
"""Unique identifier for the feature state in Core. If feature state created via Core's `edge-identities` APIs in Core, this can be missing or `None`."""
|
|
87
|
+
featurestate_uuid: NotRequired[UUIDStr]
|
|
88
|
+
"""The UUID for this feature state. Should be used if `django_id` is `None`. If not set, should be generated."""
|
|
89
|
+
feature_segment: NotRequired[FeatureSegment | None]
|
|
90
|
+
"""Segment override data, if this feature state is for a segment override."""
|
|
91
|
+
multivariate_feature_state_values: "NotRequired[Annotated[list[MultivariateFeatureStateValue], ValidateMultivariateFeatureValuesList]]"
|
|
92
|
+
"""List of multivariate feature state values, if this feature state is for a multivariate feature.
|
|
93
|
+
|
|
94
|
+
Total `percentage_allocation` sum of the child multivariate feature state values must be less or equal to 100.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Trait(TypedDict):
|
|
99
|
+
"""Represents a key-value pair associated with an identity."""
|
|
100
|
+
|
|
101
|
+
trait_key: str
|
|
102
|
+
"""Key of the trait."""
|
|
103
|
+
trait_value: DynamoContextValue
|
|
104
|
+
"""Value of the trait."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class SegmentCondition(TypedDict):
|
|
108
|
+
"""Represents a condition within a segment rule used by Flagsmith engine."""
|
|
109
|
+
|
|
110
|
+
operator: ConditionOperator
|
|
111
|
+
"""Operator to be applied for this condition."""
|
|
112
|
+
value: NotRequired[str | None]
|
|
113
|
+
"""Value to be compared against in this condition. May be `None` for `IS_SET` and `IS_NOT_SET` operators."""
|
|
114
|
+
property_: NotRequired[str | None]
|
|
115
|
+
"""The property (context key) this condition applies to. May be `None` for the `PERCENTAGE_SPLIT` operator.
|
|
116
|
+
|
|
117
|
+
Named `property_` to avoid conflict with Python's `property` built-in.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class SegmentRule(TypedDict):
|
|
122
|
+
"""Represents a rule within a segment used by Flagsmith engine."""
|
|
123
|
+
|
|
124
|
+
type: RuleType
|
|
125
|
+
"""Type of the rule, defining how conditions are evaluated."""
|
|
126
|
+
rules: "list[SegmentRule]"
|
|
127
|
+
"""Nested rules within this rule."""
|
|
128
|
+
conditions: list[SegmentCondition]
|
|
129
|
+
"""Conditions that must be met for this rule, evaluated based on the rule type."""
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Segment(TypedDict):
|
|
133
|
+
"""Represents a Flagsmith segment. Carries rules, feature overrides, and segment rules."""
|
|
134
|
+
|
|
135
|
+
id: DynamoInt
|
|
136
|
+
"""Unique identifier for the segment in Core."""
|
|
137
|
+
name: str
|
|
138
|
+
"""Name of the segment."""
|
|
139
|
+
rules: list[SegmentRule]
|
|
140
|
+
"""List of rules within the segment."""
|
|
141
|
+
feature_states: list[FeatureState]
|
|
142
|
+
"""List of segment overrides."""
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class Organisation(TypedDict):
|
|
146
|
+
"""Represents data about a Flagsmith organisation. Carries settings necessary for an SDK API operation."""
|
|
147
|
+
|
|
148
|
+
id: DynamoInt
|
|
149
|
+
"""Unique identifier for the organisation in Core."""
|
|
150
|
+
name: str
|
|
151
|
+
"""Organisation name."""
|
|
152
|
+
feature_analytics: NotRequired[bool]
|
|
153
|
+
"""Whether the SDK API should log feature analytics events for this organisation. Defaults to `False`."""
|
|
154
|
+
stop_serving_flags: NotRequired[bool]
|
|
155
|
+
"""Whether flag serving is disabled for this organisation. Defaults to `False`."""
|
|
156
|
+
persist_trait_data: NotRequired[bool]
|
|
157
|
+
"""If set to `False`, trait data will never be persisted for this organisation. Defaults to `True`."""
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Project(TypedDict):
|
|
161
|
+
"""Represents data about a Flagsmith project. Carries settings necessary for an SDK API operation."""
|
|
162
|
+
|
|
163
|
+
id: DynamoInt
|
|
164
|
+
"""Unique identifier for the project in Core."""
|
|
165
|
+
name: str
|
|
166
|
+
"""Project name."""
|
|
167
|
+
organisation: Organisation
|
|
168
|
+
"""The organisation that this project belongs to."""
|
|
169
|
+
segments: list[Segment]
|
|
170
|
+
"""List of segments."""
|
|
171
|
+
server_key_only_feature_ids: NotRequired[list[DynamoInt]]
|
|
172
|
+
"""List of feature IDs that are skipped when the SDK API serves flags for a public client-side key."""
|
|
173
|
+
enable_realtime_updates: NotRequired[bool]
|
|
174
|
+
"""Whether the SDK API should use real-time updates. Defaults to `False`. Not currently used neither by SDK APIs nor by SDKs themselves."""
|
|
175
|
+
hide_disabled_flags: NotRequired[bool | None]
|
|
176
|
+
"""Whether the SDK API should hide disabled flags for this project. Defaults to `False`."""
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class Integration(TypedDict):
|
|
180
|
+
"""Represents evaluation integration data."""
|
|
181
|
+
|
|
182
|
+
api_key: NotRequired[str | None]
|
|
183
|
+
"""API key for the integration."""
|
|
184
|
+
base_url: NotRequired[str | None]
|
|
185
|
+
"""Base URL for the integration."""
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class DynatraceIntegration(Integration):
|
|
189
|
+
"""Represents Dynatrace evaluation integration data."""
|
|
190
|
+
|
|
191
|
+
entity_selector: str
|
|
192
|
+
"""A Dynatrace entity selector string."""
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class Webhook(TypedDict):
|
|
196
|
+
"""Represents a webhook configuration."""
|
|
197
|
+
|
|
198
|
+
url: str
|
|
199
|
+
"""Webhook target URL."""
|
|
200
|
+
secret: str
|
|
201
|
+
"""Secret used to sign webhook payloads."""
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class _EnvironmentFields(TypedDict):
|
|
205
|
+
"""Common fields for Environment documents."""
|
|
206
|
+
|
|
207
|
+
name: NotRequired[str]
|
|
208
|
+
"""Environment name. Defaults to an empty string if not set."""
|
|
209
|
+
updated_at: NotRequired[DateTimeStr | None]
|
|
210
|
+
"""Last updated timestamp. If not set, current timestamp should be assumed."""
|
|
211
|
+
|
|
212
|
+
project: Project
|
|
213
|
+
"""Project-specific data for this environment."""
|
|
214
|
+
feature_states: list[FeatureState]
|
|
215
|
+
"""List of feature states representing the environment defaults."""
|
|
216
|
+
|
|
217
|
+
allow_client_traits: NotRequired[bool]
|
|
218
|
+
"""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`."""
|
|
219
|
+
hide_sensitive_data: NotRequired[bool]
|
|
220
|
+
"""Whether the SDK API should hide sensitive data for this environment. Defaults to `False`."""
|
|
221
|
+
hide_disabled_flags: NotRequired[bool | None]
|
|
222
|
+
"""Whether the SDK API should hide disabled flags for this environment. If `None`, the SDK API should fall back to project-level setting."""
|
|
223
|
+
use_identity_composite_key_for_hashing: NotRequired[bool]
|
|
224
|
+
"""Whether the SDK API should set `$.identity.key` in engine evaluation context to identity's composite key. Defaults to `False`."""
|
|
225
|
+
use_identity_overrides_in_local_eval: NotRequired[bool]
|
|
226
|
+
"""Whether the SDK API should return identity overrides as part of the environment document. Defaults to `False`."""
|
|
227
|
+
|
|
228
|
+
amplitude_config: NotRequired[Integration | None]
|
|
229
|
+
"""Amplitude integration configuration."""
|
|
230
|
+
dynatrace_config: NotRequired[DynatraceIntegration | None]
|
|
231
|
+
"""Dynatrace integration configuration."""
|
|
232
|
+
heap_config: NotRequired[Integration | None]
|
|
233
|
+
"""Heap integration configuration."""
|
|
234
|
+
mixpanel_config: NotRequired[Integration | None]
|
|
235
|
+
"""Mixpanel integration configuration."""
|
|
236
|
+
rudderstack_config: NotRequired[Integration | None]
|
|
237
|
+
"""RudderStack integration configuration."""
|
|
238
|
+
segment_config: NotRequired[Integration | None]
|
|
239
|
+
"""Segment integration configuration."""
|
|
240
|
+
webhook_config: NotRequired[Webhook | None]
|
|
241
|
+
"""Webhook configuration."""
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
### Root document schemas below. Indexed fields are marked as **INDEXED** in the docstrings. ###
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class EnvironmentAPIKey(TypedDict):
|
|
248
|
+
"""Represents a server-side API key for a Flagsmith environment.
|
|
249
|
+
|
|
250
|
+
**DynamoDB table**: `flagsmith_environment_api_key`
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
id: DynamoInt
|
|
254
|
+
"""Unique identifier for the environment API key in Core. **INDEXED**."""
|
|
255
|
+
key: str
|
|
256
|
+
"""The server-side API key string, e.g. `"ser.xxxxxxxxxxxxx"`. **INDEXED**."""
|
|
257
|
+
created_at: DateTimeStr
|
|
258
|
+
"""Creation timestamp."""
|
|
259
|
+
name: str
|
|
260
|
+
"""Name of the API key."""
|
|
261
|
+
client_api_key: str
|
|
262
|
+
"""The corresponding public client-side API key."""
|
|
263
|
+
expires_at: NotRequired[DateTimeStr | None]
|
|
264
|
+
"""Expiration timestamp. If `None`, the key does not expire."""
|
|
265
|
+
active: bool
|
|
266
|
+
"""Whether the key is active. Defaults to `True`."""
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class Identity(TypedDict):
|
|
270
|
+
"""Represents a Flagsmith identity within an environment. Carries traits and feature overrides.
|
|
271
|
+
Used for per-identity flag evaluations in remote evaluation mode.
|
|
272
|
+
|
|
273
|
+
**DynamoDB table**: `flagsmith_identities`
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
identifier: str
|
|
277
|
+
"""Unique identifier for the identity. **INDEXED**."""
|
|
278
|
+
environment_api_key: str
|
|
279
|
+
"""API key of the environment this identity belongs to. Used to scope the identity within a specific environment. **INDEXED**."""
|
|
280
|
+
identity_uuid: UUIDStr
|
|
281
|
+
"""The UUID for this identity. **INDEXED**."""
|
|
282
|
+
composite_key: str
|
|
283
|
+
"""A composite key combining the environment and identifier. **INDEXED**.
|
|
284
|
+
|
|
285
|
+
Generated as: `{environment_api_key}_{identifier}`.
|
|
286
|
+
"""
|
|
287
|
+
created_date: DateTimeStr
|
|
288
|
+
"""Creation timestamp."""
|
|
289
|
+
identity_features: (
|
|
290
|
+
"NotRequired[Annotated[list[FeatureState], ValidateIdentityFeatureStatesList]]"
|
|
291
|
+
)
|
|
292
|
+
"""List of identity overrides for this identity."""
|
|
293
|
+
identity_traits: list[Trait]
|
|
294
|
+
"""List of traits associated with this identity."""
|
|
295
|
+
django_id: NotRequired[DynamoInt | None]
|
|
296
|
+
"""Unique identifier for the identity in Core. If identity created via Core's `edge-identities` API, this can be missing or `None`."""
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class Environment(_EnvironmentFields):
|
|
300
|
+
"""Represents a Flagsmith environment. Carries all necessary data for flag evaluation within the environment.
|
|
301
|
+
|
|
302
|
+
**DynamoDB table**: `flagsmith_environments`
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
api_key: str
|
|
306
|
+
"""Public client-side API key for the environment. **INDEXED**."""
|
|
307
|
+
id: DynamoInt
|
|
308
|
+
"""Unique identifier for the environment in Core."""
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class EnvironmentV2Meta(_EnvironmentFields):
|
|
312
|
+
"""Represents a Flagsmith environment. Carries all necessary data for flag evaluation within the environment.
|
|
313
|
+
|
|
314
|
+
**DynamoDB table**: `flagsmith_environments_v2`
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
environment_id: str
|
|
318
|
+
"""Unique identifier for the environment in Core. Same as `Environment.id`, but string-typed to reduce coupling with Core's type definitions **INDEXED**."""
|
|
319
|
+
environment_api_key: str
|
|
320
|
+
"""Public client-side API key for the environment. **INDEXED**."""
|
|
321
|
+
document_key: Literal["_META"]
|
|
322
|
+
"""The fixed document key for the environment v2 document. Always `"_META"`. **INDEXED**."""
|
|
323
|
+
|
|
324
|
+
id: DynamoInt
|
|
325
|
+
"""Unique identifier for the environment in Core. Exists for compatibility with the API environment document schema."""
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class EnvironmentV2IdentityOverride(TypedDict):
|
|
329
|
+
"""Represents an identity override.
|
|
330
|
+
Used for per-identity flag evaluations in local evaluation mode. Presented as part of the API environment document.
|
|
331
|
+
|
|
332
|
+
**DynamoDB table**: `flagsmith_environments_v2`
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
environment_id: str
|
|
336
|
+
"""Unique identifier for the environment in Core. **INDEXED**."""
|
|
337
|
+
document_key: str
|
|
338
|
+
"""The document key for this identity override, formatted as `identity_override:{feature Core ID}:{identity UUID}`. **INDEXED**."""
|
|
339
|
+
environment_api_key: str
|
|
340
|
+
"""Public client-side API key for the environment. **INDEXED**."""
|
|
341
|
+
identifier: str
|
|
342
|
+
"""Unique identifier for the identity. **INDEXED**."""
|
|
343
|
+
identity_uuid: str
|
|
344
|
+
"""The UUID for this identity, used by `edge-identities` APIs in Core. **INDEXED**."""
|
|
345
|
+
feature_state: FeatureState
|
|
346
|
+
"""The feature state override for this identity."""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from pydantic import AfterValidator, BeforeValidator, ValidateAs
|
|
6
|
+
|
|
7
|
+
from flagsmith_schemas.validators import (
|
|
8
|
+
validate_dynamo_feature_state_value,
|
|
9
|
+
validate_identity_feature_states,
|
|
10
|
+
validate_multivariate_feature_state_values,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
ValidateDecimalAsFloat = ValidateAs(float, lambda v: Decimal(str(v)))
|
|
14
|
+
ValidateDecimalAsInt = ValidateAs(int, lambda v: Decimal(v))
|
|
15
|
+
ValidateStrAsISODateTime = ValidateAs(datetime, lambda dt: dt.isoformat())
|
|
16
|
+
ValidateStrAsUUID = ValidateAs(UUID, str)
|
|
17
|
+
|
|
18
|
+
ValidateDynamoFeatureStateValue = BeforeValidator(validate_dynamo_feature_state_value)
|
|
19
|
+
ValidateIdentityFeatureStatesList = AfterValidator(validate_identity_feature_states)
|
|
20
|
+
ValidateMultivariateFeatureValuesList = AfterValidator(
|
|
21
|
+
validate_multivariate_feature_state_values
|
|
22
|
+
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from typing import TYPE_CHECKING, Annotated, Literal, TypeAlias
|
|
3
|
+
|
|
4
|
+
from flagsmith_schemas.constants import PYDANTIC_INSTALLED
|
|
5
|
+
|
|
6
|
+
if PYDANTIC_INSTALLED:
|
|
7
|
+
from flagsmith_schemas.pydantic_types import (
|
|
8
|
+
ValidateDecimalAsFloat,
|
|
9
|
+
ValidateDecimalAsInt,
|
|
10
|
+
ValidateDynamoFeatureStateValue,
|
|
11
|
+
ValidateStrAsISODateTime,
|
|
12
|
+
ValidateStrAsUUID,
|
|
13
|
+
)
|
|
14
|
+
elif not TYPE_CHECKING:
|
|
15
|
+
# This code runs at runtime when Pydantic is not installed.
|
|
16
|
+
# We could use PEP 649 strings with `Annotated`, but Pydantic is inconsistent in how it parses them.
|
|
17
|
+
# Define dummy types instead.
|
|
18
|
+
ValidateDecimalAsFloat = ...
|
|
19
|
+
ValidateDecimalAsInt = ...
|
|
20
|
+
ValidateDynamoFeatureStateValue = ...
|
|
21
|
+
ValidateStrAsISODateTime = ...
|
|
22
|
+
ValidateStrAsUUID = ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
DynamoInt: TypeAlias = Annotated[Decimal, ValidateDecimalAsInt]
|
|
26
|
+
"""An integer value stored in DynamoDB.
|
|
27
|
+
|
|
28
|
+
DynamoDB represents all numbers as `Decimal`.
|
|
29
|
+
`DynamoInt` indicates that the value should be treated as an integer.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
DynamoFloat: TypeAlias = Annotated[Decimal, ValidateDecimalAsFloat]
|
|
33
|
+
"""A float value stored in DynamoDB.
|
|
34
|
+
|
|
35
|
+
DynamoDB represents all numbers as `Decimal`.
|
|
36
|
+
`DynamoFloat` indicates that the value should be treated as a float.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
UUIDStr: TypeAlias = Annotated[str, ValidateStrAsUUID]
|
|
40
|
+
"""A string representing a UUID."""
|
|
41
|
+
|
|
42
|
+
DateTimeStr: TypeAlias = Annotated[str, ValidateStrAsISODateTime]
|
|
43
|
+
"""A string representing a date and time in ISO 8601 format."""
|
|
44
|
+
|
|
45
|
+
FeatureType = Literal["STANDARD", "MULTIVARIATE"]
|
|
46
|
+
"""Represents the type of a Flagsmith feature. Multivariate features include multiple weighted values."""
|
|
47
|
+
|
|
48
|
+
DynamoFeatureValue: TypeAlias = Annotated[
|
|
49
|
+
DynamoInt | bool | str | None,
|
|
50
|
+
ValidateDynamoFeatureStateValue,
|
|
51
|
+
]
|
|
52
|
+
"""Represents the value of a Flagsmith feature. Can be stored a boolean, an integer, or a string.
|
|
53
|
+
|
|
54
|
+
The default (SaaS) maximum length for strings is 20000 characters.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
DynamoContextValue: TypeAlias = DynamoInt | DynamoFloat | bool | str
|
|
58
|
+
"""Represents a scalar value in the Flagsmith context, e.g., of an identity trait.
|
|
59
|
+
Here's how we store different types:
|
|
60
|
+
- Numeric string values (int, float) are stored as numbers.
|
|
61
|
+
- Boolean values are stored as booleans.
|
|
62
|
+
- All other values are stored as strings.
|
|
63
|
+
- Maximum length for strings is 2000 characters.
|
|
64
|
+
|
|
65
|
+
This type does not include complex structures like lists or dictionaries.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
ConditionOperator = Literal[
|
|
69
|
+
"EQUAL",
|
|
70
|
+
"GREATER_THAN",
|
|
71
|
+
"LESS_THAN",
|
|
72
|
+
"LESS_THAN_INCLUSIVE",
|
|
73
|
+
"CONTAINS",
|
|
74
|
+
"GREATER_THAN_INCLUSIVE",
|
|
75
|
+
"NOT_CONTAINS",
|
|
76
|
+
"NOT_EQUAL",
|
|
77
|
+
"REGEX",
|
|
78
|
+
"PERCENTAGE_SPLIT",
|
|
79
|
+
"MODULO",
|
|
80
|
+
"IS_SET",
|
|
81
|
+
"IS_NOT_SET",
|
|
82
|
+
"IN",
|
|
83
|
+
]
|
|
84
|
+
"""Represents segment condition operators used by Flagsmith engine."""
|
|
85
|
+
|
|
86
|
+
RuleType = Literal[
|
|
87
|
+
"ALL",
|
|
88
|
+
"ANY",
|
|
89
|
+
"NONE",
|
|
90
|
+
]
|
|
91
|
+
"""Represents segment rule types used by Flagsmith engine."""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from flagsmith_schemas.constants import MAX_STRING_FEATURE_STATE_VALUE_LENGTH
|
|
5
|
+
|
|
6
|
+
if typing.TYPE_CHECKING:
|
|
7
|
+
from flagsmith_schemas.dynamodb import FeatureState, MultivariateFeatureStateValue
|
|
8
|
+
from flagsmith_schemas.types import DynamoFeatureValue
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def validate_dynamo_feature_state_value(
|
|
12
|
+
value: typing.Any,
|
|
13
|
+
) -> "DynamoFeatureValue":
|
|
14
|
+
if isinstance(value, bool | None):
|
|
15
|
+
return value
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
if len(value) > MAX_STRING_FEATURE_STATE_VALUE_LENGTH:
|
|
18
|
+
raise ValueError(
|
|
19
|
+
"Dynamo feature state value string length cannot exceed "
|
|
20
|
+
f"{MAX_STRING_FEATURE_STATE_VALUE_LENGTH} characters "
|
|
21
|
+
f"(got {len(value)} characters)."
|
|
22
|
+
)
|
|
23
|
+
return value
|
|
24
|
+
if isinstance(value, int):
|
|
25
|
+
return Decimal(value)
|
|
26
|
+
return str(value)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def validate_multivariate_feature_state_values(
|
|
30
|
+
values: "list[MultivariateFeatureStateValue]",
|
|
31
|
+
) -> "list[MultivariateFeatureStateValue]":
|
|
32
|
+
total_percentage = sum(value["percentage_allocation"] for value in values)
|
|
33
|
+
if total_percentage > 100:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
"Total `percentage_allocation` of multivariate feature state values "
|
|
36
|
+
"cannot exceed 100."
|
|
37
|
+
)
|
|
38
|
+
return values
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def validate_identity_feature_states(
|
|
42
|
+
values: "list[FeatureState]",
|
|
43
|
+
) -> "list[FeatureState]":
|
|
44
|
+
seen: set[Decimal] = set()
|
|
45
|
+
|
|
46
|
+
for feature_state in values:
|
|
47
|
+
feature_id = feature_state["feature"]["id"]
|
|
48
|
+
if feature_id in seen:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"Feature id={feature_id} cannot have multiple "
|
|
51
|
+
"feature states for a single identity."
|
|
52
|
+
)
|
|
53
|
+
seen.add(feature_id)
|
|
54
|
+
|
|
55
|
+
return values
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/__init__.py
RENAMED
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/docgen.py
RENAMED
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/start.py
RENAMED
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/management/commands/waitfordb.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/core/templates/docgen-metrics.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/multivariate/__init__.py
RENAMED
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/multivariate/serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/versioning/__init__.py
RENAMED
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/features/versioning/serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/common/migrations/helpers/postgres_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0/src/task_processor → flagsmith_common-3.1.0/src/flagsmith_schemas}/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flagsmith_common-3.0.0 → flagsmith_common-3.1.0}/src/task_processor/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|